Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2023-10-26 00:24:28 +04:00
commit 3f977ba513
9 changed files with 114 additions and 86 deletions

View File

@ -1819,7 +1819,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
if let index = node.index { if let index = node.index {
nodes.append(.Node(index: index, frame: node.apparentFrame, referenceNode: QueueLocalObject(queue: Queue.mainQueue(), generate: { nodes.append(.Node(index: index, frame: node.apparentFrame, referenceNode: QueueLocalObject(queue: Queue.mainQueue(), generate: {
return node return node
}))) }), newNode: nil))
} else { } else {
nodes.append(.Placeholder(frame: node.apparentFrame)) nodes.append(.Placeholder(frame: node.apparentFrame))
} }
@ -2000,10 +2000,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
} }
switch state.nodes[i] { switch state.nodes[i] {
case let .Node(_, frame, referenceNode): case let .Node(_, frame, referenceNode, newNode):
state.nodes[i] = .Node(index: updatedIndex, frame: frame, referenceNode: referenceNode) state.nodes[i] = .Node(index: updatedIndex, frame: frame, referenceNode: referenceNode, newNode: newNode)
case .Placeholder: case .Placeholder:
break break
} }
i += 1 i += 1
} }
@ -2039,10 +2039,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
} }
switch state.nodes[i] { switch state.nodes[i] {
case let .Node(_, frame, referenceNode): case let .Node(_, frame, referenceNode, newNode):
state.nodes[i] = .Node(index: updatedIndex, frame: frame, referenceNode: referenceNode) state.nodes[i] = .Node(index: updatedIndex, frame: frame, referenceNode: referenceNode, newNode: newNode)
case .Placeholder: case .Placeholder:
break break
} }
} }
} }
@ -2097,7 +2097,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
var updateIndices = updateAdjacentItemsIndices var updateIndices = updateAdjacentItemsIndices
if widthUpdated { if widthUpdated {
for case let .Node(index, _, _) in updatedState.nodes { for case let .Node(index, _, _, _) in updatedState.nodes {
updateIndices.insert(index) updateIndices.insert(index)
} }
} }
@ -2205,7 +2205,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
var i = 0 var i = 0
for node in state.nodes { for node in state.nodes {
if case let .Node(index, _, referenceNode) = node , index == nodeIndex { if case let .Node(index, _, referenceNode, _) = node, index == nodeIndex {
if let referenceNode = referenceNode { if let referenceNode = referenceNode {
continueWithoutNode = false continueWithoutNode = false
var controlledTransition: ControlledTransition? var controlledTransition: ControlledTransition?
@ -2953,9 +2953,6 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
offset = (self.visibleSize.height - insets.bottom) - itemNode.apparentFrame.maxY + itemNode.insets.top offset = (self.visibleSize.height - insets.bottom) - itemNode.apparentFrame.maxY + itemNode.insets.top
offset += overflow offset += overflow
offset -= floor((self.visibleSize.height - insets.bottom - insets.top) * 0.5) offset -= floor((self.visibleSize.height - insets.bottom - insets.top) * 0.5)
//offset += 100.0
//offset = (self.visibleSize.height - insets.bottom) - itemNode.apparentFrame.maxY + getOverflow(itemNode)
} }
} }
case .visible: case .visible:

View File

@ -240,12 +240,12 @@ struct PendingNode {
} }
enum ListViewStateNode { enum ListViewStateNode {
case Node(index: Int, frame: CGRect, referenceNode: QueueLocalObject<ListViewItemNode>?) case Node(index: Int, frame: CGRect, referenceNode: QueueLocalObject<ListViewItemNode>?, newNode: QueueLocalObject<ListViewItemNode>?)
case Placeholder(frame: CGRect) case Placeholder(frame: CGRect)
var index: Int? { var index: Int? {
switch self { switch self {
case .Node(let index, _, _): case let .Node(index, _, _, _):
return index return index
case .Placeholder(_): case .Placeholder(_):
return nil return nil
@ -255,15 +255,15 @@ enum ListViewStateNode {
var frame: CGRect { var frame: CGRect {
get { get {
switch self { switch self {
case .Node(_, let frame, _): case let .Node(_, frame, _, _):
return frame return frame
case .Placeholder(let frame): case .Placeholder(let frame):
return frame return frame
} }
} set(value) { } set(value) {
switch self { switch self {
case let .Node(index, _, referenceNode): case let .Node(index, _, referenceNode, newNode):
self = .Node(index: index, frame: value, referenceNode: referenceNode) self = .Node(index: index, frame: value, referenceNode: referenceNode, newNode: newNode)
case .Placeholder(_): case .Placeholder(_):
self = .Placeholder(frame: value) self = .Placeholder(frame: value)
} }
@ -313,7 +313,7 @@ struct ListViewState {
if let (fixedIndex, fixedPosition) = self.scrollPosition { if let (fixedIndex, fixedPosition) = self.scrollPosition {
for node in self.nodes { for node in self.nodes {
if let index = node.index, index == fixedIndex { if let index = node.index, index == fixedIndex {
let offset: CGFloat var offset: CGFloat
switch fixedPosition { switch fixedPosition {
case let .bottom(additionalOffset): case let .bottom(additionalOffset):
offset = (self.visibleSize.height - self.insets.bottom) - node.frame.maxY + additionalOffset offset = (self.visibleSize.height - self.insets.bottom) - node.frame.maxY + additionalOffset
@ -327,8 +327,21 @@ struct ListViewState {
switch overflow { switch overflow {
case .top: case .top:
offset = self.insets.top - node.frame.minY offset = self.insets.top - node.frame.minY
case .bottom, .custom: case .bottom:
offset = (self.visibleSize.height - self.insets.bottom) - node.frame.maxY offset = (self.visibleSize.height - self.insets.bottom) - node.frame.maxY
case let .custom(getOverflow):
if Thread.isMainThread, case let .Node(_, _, referenceNode, newNode) = node, let listNode = referenceNode?.syncWith({ $0 }) ?? newNode?.syncWith({ $0 }) {
let overflow = getOverflow(listNode)
if overflow == 0.0 {
offset = self.insets.top - node.frame.minY
} else {
offset = (self.visibleSize.height - self.insets.bottom) - node.frame.maxY
offset += overflow
offset -= floor((self.visibleSize.height - self.insets.bottom - self.insets.top) * 0.5)
}
} else {
offset = self.insets.top - node.frame.minY
}
} }
} }
case .visible: case .visible:
@ -741,7 +754,7 @@ struct ListViewState {
let nodeFrame = CGRect(origin: nodeOrigin, size: CGSize(width: layout.size.width, height: animated ? 0.0 : layout.size.height)) let nodeFrame = CGRect(origin: nodeOrigin, size: CGSize(width: layout.size.width, height: animated ? 0.0 : layout.size.height))
operations.append(.InsertNode(index: insertionIndex, offsetDirection: offsetDirection, animated: animated, node: node, layout: layout, apply: apply)) operations.append(.InsertNode(index: insertionIndex, offsetDirection: offsetDirection, animated: animated, node: node, layout: layout, apply: apply))
self.nodes.insert(.Node(index: itemIndex, frame: nodeFrame, referenceNode: nil), at: insertionIndex) self.nodes.insert(.Node(index: itemIndex, frame: nodeFrame, referenceNode: nil, newNode: node), at: insertionIndex)
if !animated { if !animated {
switch offsetDirection { switch offsetDirection {
@ -786,7 +799,7 @@ struct ListViewState {
mutating func removeNodeAtIndex(_ index: Int, direction: ListViewItemOperationDirectionHint?, animated: Bool, operations: inout [ListViewStateOperation]) { mutating func removeNodeAtIndex(_ index: Int, direction: ListViewItemOperationDirectionHint?, animated: Bool, operations: inout [ListViewStateOperation]) {
let node = self.nodes[index] let node = self.nodes[index]
if case let .Node(_, _, referenceNode) = node { if case let .Node(_, _, referenceNode, _) = node {
let nodeFrame = node.frame let nodeFrame = node.frame
self.nodes.remove(at: index) self.nodes.remove(at: index)
let offsetDirection: ListViewInsertionOffsetDirection let offsetDirection: ListViewInsertionOffsetDirection

View File

@ -804,7 +804,7 @@ public func chatMessagePhotoInternal(photoData: Signal<Tuple4<Data?, Data?, Chat
} }
} }
private func chatMessagePhotoThumbnailDatas(account: Account, userLocation: MediaResourceUserLocation, photoReference: ImageMediaReference, onlyFullSize: Bool = false, forceThumbnail: Bool = false) -> Signal<Tuple3<Data?, Data?, Bool>, NoError> { private func chatMessagePhotoThumbnailDatas(account: Account, userLocation: MediaResourceUserLocation, photoReference: ImageMediaReference, onlyFullSize: Bool = false, forceThumbnail: Bool = false) -> Signal<Tuple3<(Data, Bool)?, Data?, Bool>, NoError> {
let fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0) let fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0)
if let smallestRepresentation = smallestImageRepresentation(photoReference.media.representations), let largestRepresentation = photoReference.media.representationForDisplayAtSize(PixelDimensions(width: Int32(fullRepresentationSize.width), height: Int32(fullRepresentationSize.height))) { if let smallestRepresentation = smallestImageRepresentation(photoReference.media.representations), let largestRepresentation = photoReference.media.representationForDisplayAtSize(PixelDimensions(width: Int32(fullRepresentationSize.width), height: Int32(fullRepresentationSize.height))) {
@ -813,17 +813,23 @@ private func chatMessagePhotoThumbnailDatas(account: Account, userLocation: Medi
let signal = maybeFullSize let signal = maybeFullSize
|> take(1) |> take(1)
|> mapToSignal { maybeData -> Signal<Tuple3<Data?, Data?, Bool>, NoError> in |> mapToSignal { maybeData -> Signal<Tuple3<(Data, Bool)?, Data?, Bool>, NoError> in
if maybeData.complete, !forceThumbnail { if maybeData.complete, !forceThumbnail {
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: []) let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
return .single(Tuple(nil, loadedData, true)) return .single(Tuple(nil, loadedData, true))
} else { } else {
let fetchedThumbnail = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: photoReference.resourceReference(smallestRepresentation.resource), statsCategory: .image) let fetchedThumbnail = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: userLocation, userContentType: .image, reference: photoReference.resourceReference(smallestRepresentation.resource), statsCategory: .image)
let thumbnail = Signal<Data?, NoError> { subscriber in let thumbnail = Signal<(Data, Bool)?, NoError> { subscriber in
let fetchedDisposable = fetchedThumbnail.start() let fetchedDisposable = fetchedThumbnail.start()
let thumbnailDisposable = account.postbox.mediaBox.resourceData(smallestRepresentation.resource).start(next: { next in let thumbnailDisposable = account.postbox.mediaBox.resourceData(smallestRepresentation.resource).start(next: { next in
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) if next.size == 0 {
subscriber.putNext(nil)
} else if let data = try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []) {
subscriber.putNext((data, false))
} else {
subscriber.putNext(nil)
}
}, error: subscriber.putError, completed: subscriber.putCompletion) }, error: subscriber.putError, completed: subscriber.putCompletion)
return ActionDisposable { return ActionDisposable {
@ -858,7 +864,8 @@ public func chatMessagePhotoThumbnail(account: Account, userLocation: MediaResou
let signal = chatMessagePhotoThumbnailDatas(account: account, userLocation: userLocation, photoReference: photoReference, onlyFullSize: onlyFullSize, forceThumbnail: blurred) let signal = chatMessagePhotoThumbnailDatas(account: account, userLocation: userLocation, photoReference: photoReference, onlyFullSize: onlyFullSize, forceThumbnail: blurred)
return signal return signal
|> map { value in |> map { value in
let thumbnailData = value._0 let thumbnailData: Data? = value._0?.0
let thumbnailIsBlurred: Bool = value._0?.1 ?? false
let fullSizeData = value._1 let fullSizeData = value._1
let fullSizeComplete = value._2 let fullSizeComplete = value._2
return { arguments in return { arguments in
@ -905,23 +912,27 @@ public func chatMessagePhotoThumbnail(account: Account, userLocation: MediaResou
thumbnailImage = image thumbnailImage = image
} }
var blurredThumbnailImage: UIImage? var blurredThumbnailImage: CGImage?
if let thumbnailImage = thumbnailImage { if let thumbnailImage = thumbnailImage {
let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height) if thumbnailIsBlurred {
let thumbnailContextSize = thumbnailSize.aspectFitted(blurred ? CGSize(width: 50.0, height: 50.0) : CGSize(width: 150.0, height: 150.0)) let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height)
if let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) { let thumbnailContextSize = thumbnailSize.aspectFitted(blurred ? CGSize(width: 50.0, height: 50.0) : CGSize(width: 150.0, height: 150.0))
thumbnailContext.withFlippedContext { c in if let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) {
c.interpolationQuality = .none thumbnailContext.withFlippedContext { c in
c.draw(thumbnailImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) c.interpolationQuality = .none
} c.draw(thumbnailImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize))
imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) }
if blurred {
imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
adjustSaturationInContext(context: thumbnailContext, saturation: 1.7)
if blurred {
imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes)
adjustSaturationInContext(context: thumbnailContext, saturation: 1.7)
}
blurredThumbnailImage = thumbnailContext.generateImage()?.cgImage
} }
} else {
blurredThumbnailImage = thumbnailContext.generateImage() blurredThumbnailImage = thumbnailImage
} }
} }
@ -933,9 +944,9 @@ public func chatMessagePhotoThumbnail(account: Account, userLocation: MediaResou
} }
c.setBlendMode(.copy) c.setBlendMode(.copy)
if let blurredThumbnailImage = blurredThumbnailImage, let cgImage = blurredThumbnailImage.cgImage { if let blurredThumbnailImage {
c.interpolationQuality = .low c.interpolationQuality = .low
drawImage(context: c, image: cgImage, orientation: imageOrientation, in: fittedRect) drawImage(context: c, image: blurredThumbnailImage, orientation: imageOrientation, in: fittedRect)
c.setBlendMode(.normal) c.setBlendMode(.normal)
} }

View File

@ -359,7 +359,7 @@ public func trimStringWithEntities(string: String, entities: [MessageTextEntity]
} }
while range.upperBound > range.lowerBound { while range.upperBound > range.lowerBound {
let c = nsString.character(at: range.lowerBound) let c = nsString.character(at: range.upperBound - 1)
if c == 0x0a || c == 0x20 { if c == 0x0a || c == 0x20 {
range = range.lowerBound ..< (range.upperBound - 1) range = range.lowerBound ..< (range.upperBound - 1)
} else { } else {

View File

@ -240,7 +240,7 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode {
var titleText: [CompositeTextNode.Component] = [] var titleText: [CompositeTextNode.Component] = []
if let peer = message?.peers[strongSelf.messageId.peerId] as? TelegramChannel, case .broadcast = peer.info { if let peer = message?.peers[strongSelf.messageId.peerId] as? TelegramChannel, case .broadcast = peer.info {
let icon: UIImage? let icon: UIImage?
icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon")?.withRenderingMode(.alwaysTemplate) icon = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon"), color: theme.chat.inputPanel.panelControlAccentColor)
if let icon { if let icon {
let rawString: PresentationStrings.FormattedString let rawString: PresentationStrings.FormattedString
@ -279,9 +279,9 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode {
if let peer = message?.peers[strongSelf.messageId.peerId], (peer is TelegramChannel || peer is TelegramGroup) { if let peer = message?.peers[strongSelf.messageId.peerId], (peer is TelegramChannel || peer is TelegramGroup) {
let icon: UIImage? let icon: UIImage?
if let channel = peer as? TelegramChannel, case .broadcast = channel.info { if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon")?.withRenderingMode(.alwaysTemplate) icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon")
} else { } else {
icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextGroupIcon")?.withRenderingMode(.alwaysTemplate) icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextGroupIcon")
} }
if let iconImage = generateTintedImage(image: icon, color: strongSelf.theme.chat.inputPanel.panelControlAccentColor) { if let iconImage = generateTintedImage(image: icon, color: strongSelf.theme.chat.inputPanel.panelControlAccentColor) {
titleText.append(.icon(iconImage)) titleText.append(.icon(iconImage))

View File

@ -273,6 +273,41 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
return .complete() return .complete()
} }
let applyCurrentQuoteSelection: () -> Void = { [weak selfController, weak chatController] in
guard let selfController, let chatController else {
return
}
var messageItemNode: ChatMessageItemView?
chatController.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in
if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item, item.message.id == replySubject.messageId {
messageItemNode = itemNode
}
return true
}
var targetContentNode: ChatMessageTextBubbleContentNode?
if let messageItemNode = messageItemNode as? ChatMessageBubbleItemNode {
for contentNode in messageItemNode.contentNodes {
if let contentNode = contentNode as? ChatMessageTextBubbleContentNode {
targetContentNode = contentNode
break
}
}
}
guard let contentNode = targetContentNode else {
return
}
guard let textSelection = contentNode.getCurrentTextSelection() else {
return
}
var quote: EngineMessageReplyQuote?
let trimmedText = trimStringWithEntities(string: textSelection.text, entities: textSelection.entities, maxLength: quoteMaxLength(appConfig: selfController.context.currentAppConfiguration.with({ $0 })))
if !trimmedText.string.isEmpty {
quote = EngineMessageReplyQuote(text: trimmedText.string, entities: trimmedText.entities, media: nil)
}
selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote)).withoutSelectionState() }) })
}
let items = combineLatest(queue: .mainQueue(), let items = combineLatest(queue: .mainQueue(),
selfController.context.account.postbox.messagesAtIds([replySubject.messageId]), selfController.context.account.postbox.messagesAtIds([replySubject.messageId]),
ApplicationSpecificNotice.getReplyQuoteTextSelectionTips(accountManager: selfController.context.sharedContext.accountManager) ApplicationSpecificNotice.getReplyQuoteTextSelectionTips(accountManager: selfController.context.sharedContext.accountManager)
@ -288,40 +323,8 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
if replySubject.quote != nil { if replySubject.quote != nil {
items.append(.action(ContextMenuActionItem(text: selfController.presentationData.strings.Conversation_MessageOptionsQuoteSelectedPart, icon: { theme in items.append(.action(ContextMenuActionItem(text: selfController.presentationData.strings.Conversation_MessageOptionsQuoteSelectedPart, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/QuoteSelected"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/QuoteSelected"), color: theme.contextMenu.primaryColor)
}, action: { [weak selfController, weak chatController] _, f in }, action: { _, f in
guard let selfController, let chatController else { applyCurrentQuoteSelection()
return
}
var messageItemNode: ChatMessageItemView?
chatController.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in
if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item, item.message.id == replySubject.messageId {
messageItemNode = itemNode
}
return true
}
var targetContentNode: ChatMessageTextBubbleContentNode?
if let messageItemNode = messageItemNode as? ChatMessageBubbleItemNode {
for contentNode in messageItemNode.contentNodes {
if let contentNode = contentNode as? ChatMessageTextBubbleContentNode {
targetContentNode = contentNode
break
}
}
}
guard let contentNode = targetContentNode else {
return
}
guard let textSelection = contentNode.getCurrentTextSelection() else {
return
}
var quote: EngineMessageReplyQuote?
let trimmedText = trimStringWithEntities(string: textSelection.text, entities: textSelection.entities, maxLength: quoteMaxLength(appConfig: selfController.context.currentAppConfiguration.with({ $0 })))
if !trimmedText.string.isEmpty {
quote = EngineMessageReplyQuote(text: trimmedText.string, entities: trimmedText.entities, media: nil)
}
selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote)).withoutSelectionState() }) })
f(.default) f(.default)
}))) })))
@ -418,6 +421,8 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
if canReplyInAnotherChat { if canReplyInAnotherChat {
items.append(.action(ContextMenuActionItem(text: selfController.presentationData.strings.Conversation_MessageOptionsReplyInAnotherChat, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replace"), color: theme.contextMenu.primaryColor) }, action: { [weak selfController] c, f in items.append(.action(ContextMenuActionItem(text: selfController.presentationData.strings.Conversation_MessageOptionsReplyInAnotherChat, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replace"), color: theme.contextMenu.primaryColor) }, action: { [weak selfController] c, f in
applyCurrentQuoteSelection()
f(.default) f(.default)
guard let selfController else { guard let selfController else {
@ -434,6 +439,8 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
items.append(.separator) items.append(.separator)
items.append(.action(ContextMenuActionItem(text: selfController.presentationData.strings.Conversation_MessageOptionsApplyChanges, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { _, f in items.append(.action(ContextMenuActionItem(text: selfController.presentationData.strings.Conversation_MessageOptionsApplyChanges, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) }, action: { _, f in
applyCurrentQuoteSelection()
f(.default) f(.default)
}))) })))
} }

View File

@ -17994,7 +17994,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
} }
} else if let embedUrl = content.embedUrl, !embedUrl.isEmpty { } else if content.file == nil, (content.image == nil || content.isMediaLargeByDefault == true || content.isMediaLargeByDefault == nil), let embedUrl = content.embedUrl, !embedUrl.isEmpty {
progress?.set(.single(false)) progress?.set(.single(false))
if let controllerInteraction = self.controllerInteraction { if let controllerInteraction = self.controllerInteraction {
if controllerInteraction.openMessage(message, .default) { if controllerInteraction.openMessage(message, .default) {

View File

@ -1375,7 +1375,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
initialSearchLocation = .index(.absoluteUpperBound()) initialSearchLocation = .index(.absoluteUpperBound())
} }
} }
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: initialSearchLocation, quote: nil), count: historyMessageCount, highlight: highlight != nil), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0) strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: initialSearchLocation, quote: highlight?.quote), count: historyMessageCount, highlight: highlight != nil), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)
} else if let subject = subject, case let .pinnedMessages(maybeMessageId) = subject, let messageId = maybeMessageId { } else if let subject = subject, case let .pinnedMessages(maybeMessageId) = subject, let messageId = maybeMessageId {
strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: .id(messageId), quote: nil), count: historyMessageCount, highlight: true), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0) strongSelf.chatHistoryLocationValue = ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: .id(messageId), quote: nil), count: historyMessageCount, highlight: true), id: (strongSelf.chatHistoryLocationValue?.id).flatMap({ $0 + 1 }) ?? 0)
} else if var chatHistoryLocation = strongSelf.chatHistoryLocationValue { } else if var chatHistoryLocation = strongSelf.chatHistoryLocationValue {

View File

@ -73,11 +73,11 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
controller.updateTextInputState(updateTextInputState) controller.updateTextInputState(updateTextInputState)
} }
var popAndComplete = true var popAndComplete = true
if let subject = params.subject, case let .message(messageSubject, _, timecode) = subject { if let subject = params.subject, case let .message(messageSubject, highlight, timecode) = subject {
if case let .id(messageId) = messageSubject { if case let .id(messageId) = messageSubject {
let navigationController = params.navigationController let navigationController = params.navigationController
let animated = params.animated let animated = params.animated
controller.navigateToMessage(messageLocation: .id(messageId, NavigateToMessageParams(timestamp: timecode, quote: nil)), animated: isFirst, completion: { [weak navigationController, weak controller] in controller.navigateToMessage(messageLocation: .id(messageId, NavigateToMessageParams(timestamp: timecode, quote: highlight?.quote)), animated: isFirst, completion: { [weak navigationController, weak controller] in
if let navigationController = navigationController, let controller = controller { if let navigationController = navigationController, let controller = controller {
let _ = navigationController.popToViewController(controller, animated: animated) let _ = navigationController.popToViewController(controller, animated: animated)
} }