From 01206562dbebfc3c340097e88e14f79cfa56cf6b Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 4 Sep 2020 17:38:55 +0100 Subject: [PATCH] Comments update --- Telegram/NotificationService/Serialization.m | 2 +- .../Sources/AccountContext.swift | 5 +- .../Sources/OpenChatMessage.swift | 6 + .../Sources/Node/ChatListItem.swift | 4 +- .../GalleryUI/Sources/GalleryController.swift | 16 +- .../Items/UniversalVideoGalleryItem.swift | 4 +- .../Postbox/Sources/MessageHistoryTable.swift | 73 ++-- .../Sources/MessageHistoryTagsTable.swift | 4 + .../Sources/MessageHistoryViewState.swift | 12 +- submodules/Postbox/Sources/Postbox.swift | 55 ++- submodules/TelegramApi/Sources/Api0.swift | 6 +- submodules/TelegramApi/Sources/Api1.swift | 28 +- .../Sources/TelegramBaseController.swift | 4 +- .../Sources/ExportMessageLink.swift | 33 +- submodules/TelegramCore/Sources/Holes.swift | 9 +- .../TelegramCore/Sources/PeerUtils.swift | 21 ++ .../TelegramCore/Sources/SearchMessages.swift | 2 +- .../TelegramCore/Sources/Serialization.swift | 2 +- .../Sources/StoreMessage_Telegram.swift | 127 +------ .../Sources/UpdateMessageService.swift | 17 +- .../Sources/UpdatesApiUtils.swift | 43 +-- .../PresentationThemeEssentialGraphics.swift | 2 +- .../FreeRepliesIcon.imageset/Contents.json | 12 + .../replies_sticker.pdf | Bin 0 -> 3999 bytes .../TelegramUI/Sources/AccountContext.swift | 4 +- .../TelegramUI/Sources/ChatController.swift | 326 ++++++++++++++---- .../Sources/ChatHistoryEntriesForView.swift | 36 ++ .../Sources/ChatHistoryListNode.swift | 5 +- .../Sources/ChatHistoryViewForLocation.swift | 3 +- .../ChatInterfaceStateContextMenus.swift | 20 +- .../ChatInterfaceStateInputPanels.swift | 2 +- .../ChatInterfaceStateNavigationButtons.swift | 18 +- .../ChatMessageAnimatedStickerItemNode.swift | 121 +++++-- .../Sources/ChatMessageBubbleItemNode.swift | 23 +- .../ChatMessageInstantVideoItemNode.swift | 4 +- .../TelegramUI/Sources/ChatMessageItem.swift | 8 +- .../Sources/ChatMessageStickerItemNode.swift | 4 +- .../ChatPanelInterfaceInteraction.swift | 4 +- .../ChatPinnedMessageTitlePanelNode.swift | 14 +- .../Sources/ChatRecentActionsController.swift | 2 +- .../ChatRecentActionsControllerNode.swift | 6 +- .../Sources/ChatSearchInputPanelNode.swift | 4 + .../TelegramUI/Sources/ChatTitleView.swift | 46 ++- .../Sources/EditAccessoryPanelNode.swift | 2 +- .../Sources/NavigateToChatController.swift | 2 +- .../TelegramUI/Sources/OpenChatMessage.swift | 18 +- .../TelegramUI/Sources/OpenResolvedUrl.swift | 4 + .../Sources/OverlayPlayerControllerNode.swift | 2 +- .../PeerInfo/Panes/PeerInfoListPaneNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 6 +- .../Sources/ReplyAccessoryPanelNode.swift | 2 +- .../Sources/SharedAccountContext.swift | 4 +- .../TelegramUI/Sources/TextLinkHandling.swift | 4 + .../UrlHandling/Sources/UrlHandling.swift | 43 ++- 54 files changed, 785 insertions(+), 441 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/FreeRepliesIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/FreeRepliesIcon.imageset/replies_sticker.pdf diff --git a/Telegram/NotificationService/Serialization.m b/Telegram/NotificationService/Serialization.m index f137bbe5df..cce25b19cc 100644 --- a/Telegram/NotificationService/Serialization.m +++ b/Telegram/NotificationService/Serialization.m @@ -3,7 +3,7 @@ @implementation Serialization - (NSUInteger)currentLayer { - return 119; + return 120; } - (id _Nullable)parseMessage:(NSData * _Nullable)data { diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index db37267557..e470deadee 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -170,6 +170,7 @@ public enum ResolvedUrl { case botStart(peerId: PeerId, payload: String) case groupBotStart(peerId: PeerId, payload: String) case channelMessage(peerId: PeerId, messageId: MessageId) + case replyThreadMessage(replyThreadMessageId: MessageId, isChannelPost: Bool, maxReadMessageId: MessageId?, messageId: MessageId) case stickerPack(name: String) case instantView(TelegramMediaWebpage, String?) case proxy(host: String, port: Int32, username: String?, password: String?, secret: Data?) @@ -252,7 +253,7 @@ public enum ChatSearchDomain: Equatable { public enum ChatLocation: Equatable { case peer(PeerId) - case replyThread(threadMessageId: MessageId, maxReadMessageId: MessageId?) + case replyThread(threadMessageId: MessageId, isChannelPost: Bool, maxReadMessageId: MessageId?) } public final class NavigateToChatControllerParams { @@ -528,7 +529,7 @@ public protocol SharedAccountContext: class { func handleTextLinkAction(context: AccountContext, peerId: PeerId?, navigateDisposable: MetaDisposable, controller: ViewController, action: TextLinkItemActionType, itemLink: TextLinkItem) func navigateToChat(accountId: AccountRecordId, peerId: PeerId, messageId: MessageId?) func openChatMessage(_ params: OpenChatMessageParams) -> Bool - func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError> + func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError> func makeOverlayAudioPlayerController(context: AccountContext, peerId: PeerId, type: MediaManagerPlayerType, initialMessageId: MessageId, initialOrder: MusicPlaybackSettingsOrder, parentNavigationController: NavigationController?) -> ViewController & OverlayAudioPlayerController func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool) -> ViewController? func makeChannelAdminController(context: AccountContext, peerId: PeerId, adminId: PeerId, initialParticipant: ChannelParticipant) -> ViewController? diff --git a/submodules/AccountContext/Sources/OpenChatMessage.swift b/submodules/AccountContext/Sources/OpenChatMessage.swift index 00f353051c..2dbdc86169 100644 --- a/submodules/AccountContext/Sources/OpenChatMessage.swift +++ b/submodules/AccountContext/Sources/OpenChatMessage.swift @@ -18,6 +18,8 @@ public enum ChatControllerInteractionOpenMessageMode { public final class OpenChatMessageParams { public let context: AccountContext + public let chatLocation: ChatLocation? + public let chatLocationContextHolder: Atomic? public let message: Message public let standalone: Bool public let reverseMessageGalleryOrder: Bool @@ -39,6 +41,8 @@ public final class OpenChatMessageParams { public init( context: AccountContext, + chatLocation: ChatLocation?, + chatLocationContextHolder: Atomic?, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, @@ -59,6 +63,8 @@ public final class OpenChatMessageParams { actionInteraction: GalleryControllerActionInteraction? = nil ) { self.context = context + self.chatLocation = chatLocation + self.chatLocationContextHolder = chatLocationContextHolder self.message = message self.standalone = standalone self.reverseMessageGalleryOrder = reverseMessageGalleryOrder diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index f8f9c90175..f1afc9861b 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -637,7 +637,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if let peer = peer { var overrideImage: AvatarNodeImageOverride? - if peer.id == item.context.account.peerId && !displayAsMessage { + if peer.id.isReplies { + overrideImage = .savedMessagesIcon + } else if peer.id == item.context.account.peerId && !displayAsMessage { overrideImage = .savedMessagesIcon } else if peer.isDeleted { overrideImage = .deletedIcon diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 16f78eee92..ec63e10e54 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -286,7 +286,7 @@ private enum GalleryMessageHistoryView { } public enum GalleryControllerItemSource { - case peerMessagesAtId(MessageId) + case peerMessagesAtId(messageId: MessageId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic) case standaloneMessage(Message) } @@ -417,7 +417,7 @@ public class GalleryController: ViewController, StandalonePresentableController let message: Signal switch source { - case let .peerMessagesAtId(messageId): + case let .peerMessagesAtId(messageId, _, _): message = context.account.postbox.messageAtId(messageId) case let .standaloneMessage(m): message = .single(m) @@ -427,7 +427,7 @@ public class GalleryController: ViewController, StandalonePresentableController |> filter({ $0 != nil }) |> mapToSignal { message -> Signal in switch source { - case .peerMessagesAtId: + case let .peerMessagesAtId(_, chatLocation, chatLocationContextHolder): if let tags = tagsForMessage(message!) { let namespaces: MessageIdNamespaces if Namespaces.Message.allScheduled.contains(message!.id.namespace) { @@ -435,7 +435,7 @@ public class GalleryController: ViewController, StandalonePresentableController } else { namespaces = .not(Namespaces.Message.allScheduled) } - return context.account.postbox.aroundMessageHistoryViewForLocation(.peer(message!.id.peerId), anchor: .index(message!.index), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tags, namespaces: namespaces, orderStatistics: [.combinedLocation]) + return context.account.postbox.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(message!.index), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tags, namespaces: namespaces, orderStatistics: [.combinedLocation]) |> mapToSignal { (view, _, _) -> Signal in let mapped = GalleryMessageHistoryView.view(view) return .single(mapped) @@ -470,7 +470,7 @@ public class GalleryController: ViewController, StandalonePresentableController loop: for i in 0 ..< entries.count { let message = entries[i].message switch source { - case let .peerMessagesAtId(messageId): + case let .peerMessagesAtId(messageId, _, _): if message.id == messageId { centralEntryStableId = message.stableId break loop @@ -813,7 +813,7 @@ public class GalleryController: ViewController, StandalonePresentableController self.isOpaqueWhenInOverlay = true switch source { - case let .peerMessagesAtId(id): + case let .peerMessagesAtId(id, _, _): if id.peerId.namespace == Namespaces.Peer.SecretChat { self.screenCaptureEventsDisposable = (screenCaptureEvents() |> deliverOnMainQueue).start(next: { [weak self] _ in @@ -984,7 +984,7 @@ public class GalleryController: ViewController, StandalonePresentableController } switch strongSelf.source { - case let .peerMessagesAtId(initialMessageId): + case let .peerMessagesAtId(initialMessageId, chatLocation, chatLocationContextHolder): var reloadAroundIndex: MessageIndex? if index <= 2 && strongSelf.hasLeftEntries { reloadAroundIndex = strongSelf.entries.first?.index @@ -998,7 +998,7 @@ public class GalleryController: ViewController, StandalonePresentableController } else { namespaces = .not(Namespaces.Message.allScheduled) } - let signal = strongSelf.context.account.postbox.aroundMessageHistoryViewForLocation(.peer(initialMessageId.peerId), anchor: .index(reloadAroundIndex), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, namespaces: namespaces, orderStatistics: [.combinedLocation]) + let signal = strongSelf.context.account.postbox.aroundMessageHistoryViewForLocation(strongSelf.context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(reloadAroundIndex), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, namespaces: namespaces, orderStatistics: [.combinedLocation]) |> mapToSignal { (view, _, _) -> Signal in let mapped = GalleryMessageHistoryView.view(view) return .single(mapped) diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index e406599fa7..79e7102a83 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -1352,7 +1352,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { switch contentInfo { case let .message(message): - let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), replaceRootController: { controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), replaceRootController: { controller, ready in if let baseNavigationController = baseNavigationController { baseNavigationController.replaceTopController(controller, animated: false, ready: ready) } @@ -1434,7 +1434,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { switch contentInfo { case let .message(message): - let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), replaceRootController: { controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), replaceRootController: { controller, ready in if let baseNavigationController = baseNavigationController { baseNavigationController.replaceTopController(controller, animated: false, ready: ready) } diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index c4f00ef448..bd0c1988b1 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -2724,11 +2724,34 @@ final class MessageHistoryTable: Table { precondition(fromIndex.id.namespace == toIndex.id.namespace) var result: [IntermediateMessage] = [] if let threadId = threadId { - let indices: [MessageIndex] - if fromIndex < toIndex { - indices = self.threadsTable.laterIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: fromIndex, includeFrom: includeFrom, count: limit) - } else { - indices = self.threadsTable.earlierIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: fromIndex, includeFrom: includeFrom, count: limit) + var indices: [MessageIndex] = [] + var startIndex = fromIndex + var localIncludeFrom = includeFrom + while true { + let sliceIndices: [MessageIndex] + if fromIndex < toIndex { + sliceIndices = self.threadsTable.laterIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: startIndex, includeFrom: localIncludeFrom, count: limit) + } else { + sliceIndices = self.threadsTable.earlierIndices(threadId: threadId, peerId: peerId, namespace: namespace, index: startIndex, includeFrom: localIncludeFrom, count: limit) + } + if sliceIndices.isEmpty { + break + } + startIndex = sliceIndices[sliceIndices.count - 1] + localIncludeFrom = false + + for index in sliceIndices { + if let tag = tag { + if self.tagsTable.entryExists(tag: tag, index: index) { + indices.append(index) + } + } else { + indices.append(index) + } + } + if indices.count >= limit { + break + } } for index in indices { if fromIndex < toIndex { @@ -2746,46 +2769,6 @@ final class MessageHistoryTable: Table { assertionFailure() } } - - // Unoptimized - /*var startKey: ValueBoxKey - if includeFrom && fromIndex != MessageIndex.upperBound(peerId: peerId, namespace: namespace) { - if fromIndex < toIndex { - startKey = self.key(fromIndex).predecessor - } else { - startKey = self.key(fromIndex).successor - } - } else { - startKey = self.key(fromIndex) - } - while true { - var found = false - var lastKey = startKey - self.valueBox.range(self.table, start: startKey, end: self.key(toIndex), values: { key, value in - lastKey = key - found = true - - let message = self.readIntermediateEntry(key, value: value).message - assert(message.id.peerId == peerId && message.id.namespace == namespace) - assert(message.index == extractKey(key)) - if message.threadId == threadId { - result.append(message) - - if result.count >= limit { - return false - } - } - return true - }, limit: max(64, limit - result.count)) - - if !found { - break - } - if result.count >= limit { - break - } - startKey = lastKey - }*/ } else if let tag = tag { let indices: [MessageIndex] if fromIndex < toIndex { diff --git a/submodules/Postbox/Sources/MessageHistoryTagsTable.swift b/submodules/Postbox/Sources/MessageHistoryTagsTable.swift index 2af930855d..a8102d32de 100644 --- a/submodules/Postbox/Sources/MessageHistoryTagsTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTagsTable.swift @@ -61,6 +61,10 @@ class MessageHistoryTagsTable: Table { } } + func entryExists(tag: MessageTags, index: MessageIndex) -> Bool { + return self.valueBox.exists(self.table, key: self.key(tag: tag, index: index, key: self.sharedKey)) + } + func entryLocation(at index: MessageIndex, tag: MessageTags) -> MessageHistoryEntryLocation? { if let _ = self.valueBox.get(self.table, key: self.key(tag: tag, index: index)) { var greaterCount = 0 diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index 1ffb84eae4..6134e3d7a8 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -2,7 +2,7 @@ import Foundation public enum MessageHistoryInput: Equatable, Hashable { case automatic(MessageTags?) - case external(MessageHistoryViewExternalInput) + case external(MessageHistoryViewExternalInput, MessageTags?) public func hash(into hasher: inout Hasher) { switch self { @@ -19,8 +19,8 @@ private extension MessageHistoryInput { switch self { case let .automatic(tag): return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, threadId: nil, from: fromIndex, includeFrom: includeFrom, to: toIndex, limit: limit) - case let .external(input): - return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: input.threadId, from: fromIndex, includeFrom: includeFrom, to: toIndex, limit: limit) + case let .external(input, tag): + return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, threadId: input.threadId, from: fromIndex, includeFrom: includeFrom, to: toIndex, limit: limit) } } @@ -344,7 +344,7 @@ private func sampleHoleRanges(input: MessageHistoryInput, orderedEntriesBySpace: switch input { case let .automatic(value): tag = value - case let .external(value): + case let .external(value, _): threadId = value.threadId } @@ -858,7 +858,7 @@ final class HistoryViewLoadedState { input = .automatic(tag) case let .external(external): peerIds.append(external.peerId) - input = .external(external) + input = .external(external, tag) } self.input = input @@ -1347,7 +1347,7 @@ private func fetchHoles(postbox: Postbox, locations: MessageHistoryViewInput, ta if namespaces.contains(namespace) { if !indices.isEmpty { let peerIdAndNamespace = PeerIdAndNamespace(peerId: peerId, namespace: namespace) - assert(canContainHoles(peerIdAndNamespace, input: .external(input), seedConfiguration: postbox.seedConfiguration)) + assert(canContainHoles(peerIdAndNamespace, input: .external(input, tag), seedConfiguration: postbox.seedConfiguration)) holesBySpace[peerIdAndNamespace] = indices } } diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index e25ca7c06b..4bbeaf1f7b 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -2344,14 +2344,17 @@ public final class Postbox { } } - func resolvedChatLocationInput(chatLocation: ChatLocationInput) -> Signal { + func resolvedChatLocationInput(chatLocation: ChatLocationInput) -> Signal<(ResolvedChatLocationInput, Bool), NoError> { switch chatLocation { case let .peer(peerId): - return .single(.peer(peerId)) + return .single((.peer(peerId), false)) case let .external(_, input): + var isHoleFill = false return input - |> map { value -> ResolvedChatLocationInput in - return .external(value) + |> map { value -> (ResolvedChatLocationInput, Bool) in + let wasHoleFill = isHoleFill + isHoleFill = true + return (.external(value), wasHoleFill) } } } @@ -2372,8 +2375,10 @@ public final class Postbox { public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocationInput, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) - |> mapToSignal { chatLocation in - return self.transactionSignal(userInteractive: true, { subscriber, transaction in + |> mapToSignal { chatLocationData in + let (chatLocation, isHoleFill) = chatLocationData + + let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal(userInteractive: true, { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation) var anchor: HistoryViewInputAnchor = .upperBound @@ -2426,27 +2431,57 @@ public final class Postbox { } return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) }) + + return signal + |> map { (view, updateType, data) -> (MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?) in + if isHoleFill { + return (view, .FillHole, data) + } else { + return (view, updateType, data) + } + } } } public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, count: Int, clipHoles: Bool = true, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) - |> mapToSignal { chatLocation in - return self.transactionSignal { subscriber, transaction in + |> mapToSignal { chatLocationData in + let (chatLocation, isHoleFill) = chatLocationData + let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation) return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) } + + return signal + |> map { (view, updateType, data) -> (MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?) in + if isHoleFill { + return (view, .FillHole, data) + } else { + return (view, updateType, data) + } + } } } public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) - |> mapToSignal { chatLocation in - return self.transactionSignal { subscriber, transaction in + |> mapToSignal { chatLocationData -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in + let (chatLocation, isHoleFill) = chatLocationData + let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation) return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tagMask: tagMask, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData) } + + return signal + |> map { viewData -> (MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?) in + let (view, updateType, data) = viewData + if isHoleFill { + return (view, .FillHole, data) + } else { + return (view, updateType, data) + } + } } } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 44d2857ee8..9fa11106e8 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -605,8 +605,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[537022650] = { return Api.User.parse_userEmpty($0) } dict[-1820043071] = { return Api.User.parse_user($0) } dict[-2082087340] = { return Api.Message.parse_messageEmpty($0) } - dict[-146464169] = { return Api.Message.parse_message($0) } - dict[-116603007] = { return Api.Message.parse_messageService($0) } + dict[951660196] = { return Api.Message.parse_message($0) } + dict[797820163] = { return Api.Message.parse_messageService($0) } dict[831924812] = { return Api.StatsGroupTopInviter.parse_statsGroupTopInviter($0) } dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) } dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) } @@ -863,7 +863,7 @@ public struct Api { return parser(reader) } else { - telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") + telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") return nil } } diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 763362e684..f19ae2ef95 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -17373,8 +17373,8 @@ public extension Api { } public enum Message: TypeConstructorDescription { case messageEmpty(id: Int32) - case message(flags: Int32, id: Int32, fromId: Api.Peer, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, replyToTopId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?) - case messageService(flags: Int32, id: Int32, fromId: Api.Peer, toId: Api.Peer, replyToMsgId: Int32?, date: Int32, action: Api.MessageAction) + case message(flags: Int32, id: Int32, fromId: Api.Peer, peerId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, replyToTopId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?) + case messageService(flags: Int32, id: Int32, fromId: Api.Peer, peerId: Api.Peer, replyToMsgId: Int32?, date: Int32, action: Api.MessageAction) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -17384,14 +17384,14 @@ public extension Api { } serializeInt32(id, buffer: buffer, boxed: false) break - case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let replyToTopId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let restrictionReason): + case .message(let flags, let id, let fromId, let peerId, let fwdFrom, let viaBotId, let replyToMsgId, let replyToTopId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let restrictionReason): if boxed { - buffer.appendInt32(-146464169) + buffer.appendInt32(951660196) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) fromId.serialize(buffer, true) - toId.serialize(buffer, true) + peerId.serialize(buffer, true) if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)} if Int(flags) & Int(1 << 11) != 0 {serializeInt32(viaBotId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)} @@ -17417,14 +17417,14 @@ public extension Api { item.serialize(buffer, true) }} break - case .messageService(let flags, let id, let fromId, let toId, let replyToMsgId, let date, let action): + case .messageService(let flags, let id, let fromId, let peerId, let replyToMsgId, let date, let action): if boxed { - buffer.appendInt32(-116603007) + buffer.appendInt32(797820163) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) fromId.serialize(buffer, true) - toId.serialize(buffer, true) + peerId.serialize(buffer, true) if Int(flags) & Int(1 << 3) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)} serializeInt32(date, buffer: buffer, boxed: false) action.serialize(buffer, true) @@ -17436,10 +17436,10 @@ public extension Api { switch self { case .messageEmpty(let id): return ("messageEmpty", [("id", id)]) - case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let replyToTopId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let restrictionReason): - return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("replyToTopId", replyToTopId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("forwards", forwards), ("replies", replies), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)]) - case .messageService(let flags, let id, let fromId, let toId, let replyToMsgId, let date, let action): - return ("messageService", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("replyToMsgId", replyToMsgId), ("date", date), ("action", action)]) + case .message(let flags, let id, let fromId, let peerId, let fwdFrom, let viaBotId, let replyToMsgId, let replyToTopId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let restrictionReason): + return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("peerId", peerId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("replyToTopId", replyToTopId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("forwards", forwards), ("replies", replies), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)]) + case .messageService(let flags, let id, let fromId, let peerId, let replyToMsgId, let date, let action): + return ("messageService", [("flags", flags), ("id", id), ("fromId", fromId), ("peerId", peerId), ("replyToMsgId", replyToMsgId), ("date", date), ("action", action)]) } } @@ -17532,7 +17532,7 @@ public extension Api { let _c19 = (Int(_1!) & Int(1 << 17) == 0) || _19 != nil let _c20 = (Int(_1!) & Int(1 << 22) == 0) || _20 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 { - return Api.Message.message(flags: _1!, id: _2!, fromId: _3!, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, replyToTopId: _8, date: _9!, message: _10!, media: _11, replyMarkup: _12, entities: _13, views: _14, forwards: _15, replies: _16, editDate: _17, postAuthor: _18, groupedId: _19, restrictionReason: _20) + return Api.Message.message(flags: _1!, id: _2!, fromId: _3!, peerId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, replyToTopId: _8, date: _9!, message: _10!, media: _11, replyMarkup: _12, entities: _13, views: _14, forwards: _15, replies: _16, editDate: _17, postAuthor: _18, groupedId: _19, restrictionReason: _20) } else { return nil @@ -17567,7 +17567,7 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Message.messageService(flags: _1!, id: _2!, fromId: _3!, toId: _4!, replyToMsgId: _5, date: _6!, action: _7!) + return Api.Message.messageService(flags: _1!, id: _2!, fromId: _3!, peerId: _4!, replyToMsgId: _5, date: _6!, action: _7!) } else { return nil diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 49fe371681..894999317b 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -28,7 +28,7 @@ public enum LocationBroadcastPanelSource { private func presentLiveLocationController(context: AccountContext, peerId: PeerId, controller: ViewController) { let presentImpl: (Message?) -> Void = { [weak controller] message in if let message = message, let strongController = controller { - let _ = context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongController.navigationController as? NavigationController, modal: true, dismissInput: { + let _ = context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongController.navigationController as? NavigationController, modal: true, dismissInput: { controller?.view.endEditing(true) }, present: { c, a in controller?.present(c, in: .window(.root), with: a, blockInteraction: true) @@ -557,7 +557,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { } if let id = state.id as? PeerMessagesMediaPlaylistItemId { if type == .music { - let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60), id: 0), context: strongSelf.context, chatLocation: .peer(id.messageId.peerId), tagMask: MessageTags.music) + let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60), id: 0), context: strongSelf.context, chatLocation: .peer(id.messageId.peerId), chatLocationContextHolder: Atomic(value: nil), tagMask: MessageTags.music) var cancelImpl: (() -> Void)? let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/TelegramCore/Sources/ExportMessageLink.swift b/submodules/TelegramCore/Sources/ExportMessageLink.swift index 75be0fce4c..a75088a9bb 100644 --- a/submodules/TelegramCore/Sources/ExportMessageLink.swift +++ b/submodules/TelegramCore/Sources/ExportMessageLink.swift @@ -3,16 +3,37 @@ import Postbox import TelegramApi import SwiftSignalKit -public func exportMessageLink(account: Account, peerId: PeerId, messageId: MessageId) -> Signal { - return account.postbox.transaction { transaction -> Peer? in - return transaction.getPeer(peerId) +public func exportMessageLink(account: Account, peerId: PeerId, messageId: MessageId, threadMessageId: MessageId? = nil) -> Signal { + return account.postbox.transaction { transaction -> (Peer, MessageId)? in + var peer: Peer? + var messageId = messageId + if let threadMessageId = threadMessageId { + messageId = threadMessageId + if let message = transaction.getMessage(threadMessageId), let sourceReference = message.sourceReference { + peer = transaction.getPeer(sourceReference.messageId.peerId) + messageId = sourceReference.messageId + } + } else { + peer = transaction.getPeer(messageId.peerId) + } + if let peer = peer { + return (peer, messageId) + } else { + return nil + } } - |> mapToSignal { peer -> Signal in - if let peer = peer, let input = apiInputChannel(peer) { - return account.network.request(Api.functions.channels.exportMessageLink(channel: input, id: messageId.id, grouped: .boolTrue)) |> mapError { _ in return } + |> mapToSignal { data -> Signal in + guard let (peer, sourceMessageId) = data else { + return .single(nil) + } + if let input = apiInputChannel(peer) { + return account.network.request(Api.functions.channels.exportMessageLink(channel: input, id: sourceMessageId.id, grouped: .boolTrue)) |> mapError { _ in return } |> map { res in switch res { case let .exportedMessageLink(link, _): + if let _ = threadMessageId { + return "\(link)?comment=\(messageId.id)" + } return link } } |> `catch` { _ -> Signal in diff --git a/submodules/TelegramCore/Sources/Holes.swift b/submodules/TelegramCore/Sources/Holes.swift index 7fa7502bfd..7f936e1fbd 100644 --- a/submodules/TelegramCore/Sources/Holes.swift +++ b/submodules/TelegramCore/Sources/Holes.swift @@ -357,7 +357,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH assertionFailure() minMaxRange = 1 ... 1 request = .never() - } + } } return request @@ -419,7 +419,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH return withResolvedAssociatedMessages(postbox: postbox, source: source, peers: Dictionary(peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages -> IndexSet in let _ = transaction.addMessages(storeMessages, location: .Random) let _ = transaction.addMessages(additionalMessages, location: .Random) - let filledRange: ClosedRange + var filledRange: ClosedRange let ids = storeMessages.compactMap { message -> MessageId.Id? in switch message.id { case let .Id(id): @@ -444,6 +444,11 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH switch direction { case let .aroundId(aroundId): filledRange = min(aroundId.id, messageRange.lowerBound) ... max(aroundId.id, messageRange.upperBound) + if threadId != nil { + if ids.count <= count / 2 - 1 { + filledRange = minMaxRange + } + } case let .range(start, end): if start.id <= end.id { let minBound = start.id diff --git a/submodules/TelegramCore/Sources/PeerUtils.swift b/submodules/TelegramCore/Sources/PeerUtils.swift index 28571aa2ea..23f84731d4 100644 --- a/submodules/TelegramCore/Sources/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/PeerUtils.swift @@ -231,3 +231,24 @@ public func isServicePeer(_ peer: Peer) -> Bool { } return false } + +public extension PeerId { + var isReplies: Bool { + if self.namespace == Namespaces.Peer.CloudUser { + if self.id == 708513 || self.id == 1271266957 { + return true + } + } + return false + } + + func isRepliesOrSavedMessages(accountPeerId: PeerId) -> Bool { + if accountPeerId == self { + return true + } else if self.isReplies { + return true + } else { + return false + } + } +} diff --git a/submodules/TelegramCore/Sources/SearchMessages.swift b/submodules/TelegramCore/Sources/SearchMessages.swift index bebdf9e81d..eab030886e 100644 --- a/submodules/TelegramCore/Sources/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/SearchMessages.swift @@ -222,7 +222,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q flags |= (1 << 0) } } - if let topMsgId = topMsgId { + if let _ = topMsgId { flags |= (1 << 1) } let peerMessages: Signal diff --git a/submodules/TelegramCore/Sources/Serialization.swift b/submodules/TelegramCore/Sources/Serialization.swift index 94cd1882c6..d03f659a53 100644 --- a/submodules/TelegramCore/Sources/Serialization.swift +++ b/submodules/TelegramCore/Sources/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 119 + return 120 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift index b17481689a..c02de703b2 100644 --- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift @@ -110,55 +110,19 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { switch messsage { case let .message(message): - let flags = message.flags - let fromId = message.fromId - let toId = message.toId - switch toId { - case .peerUser: - if (flags & Int32(2)) != 0 { - return toId.peerId - } else { - return fromId.peerId - } - case let .peerChat(chatId): - return PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - return PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } + let chatPeerId = message.peerId + return chatPeerId.peerId case .messageEmpty: return nil - case let .messageService(flags, _, fromId, toId, _, _, _): - switch toId { - case .peerUser: - if (flags & Int32(2)) != 0 { - return toId.peerId - } else { - return fromId.peerId - } - case let .peerChat(chatId): - return PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - return PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } + case let .messageService(flags, _, fromId, chatPeerId, _, _, _): + return chatPeerId.peerId } } func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { switch message { - case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, _, media, _, entities, _, _, _, _, _, _, _): - let peerId: PeerId - switch toId { - case .peerUser: - if (flags & Int32(2)) != 0 { - peerId = toId.peerId - } else { - peerId = fromId.peerId - } - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } + case let .message(flags, _, fromId, chatPeerId, fwdHeader, viaBotId, _, _, _, _, media, _, entities, _, _, _, _, _, _, _): + let peerId: PeerId = chatPeerId.peerId var result = [peerId] @@ -207,20 +171,8 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { return result case .messageEmpty: return [] - case let .messageService(flags, _, fromId, toId, _, _, action): - let peerId: PeerId - switch toId { - case .peerUser: - if (flags & Int32(2)) != 0 { - peerId = toId.peerId - } else { - peerId = fromId.peerId - } - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } + case let .messageService(flags, _, fromId, chatPeerId, _, _, action): + let peerId: PeerId = chatPeerId.peerId var result = [peerId] if fromId.peerId != peerId { @@ -254,41 +206,17 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? { switch message { - case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(flags, _, fromId, chatPeerId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _, _, _, _): if let replyToMsgId = replyToMsgId { - let peerId: PeerId - switch toId { - case .peerUser: - if (flags & Int32(2)) != 0 { - peerId = toId.peerId - } else { - peerId = fromId.peerId - } - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } + let peerId: PeerId = chatPeerId.peerId return [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId)] } case .messageEmpty: break - case let .messageService(flags, _, fromId, toId, replyToMsgId, _, _): + case let .messageService(flags, _, fromId, chatPeerId, replyToMsgId, _, _): if let replyToMsgId = replyToMsgId { - let peerId: PeerId - switch toId { - case .peerUser: - if (flags & Int32(2)) != 0 { - peerId = toId.peerId - } else { - peerId = fromId.peerId - } - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } + let peerId: PeerId = chatPeerId.peerId return [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId)] } @@ -420,16 +348,12 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes extension StoreMessage { convenience init?(apiMessage: Api.Message, namespace: MessageId.Namespace = Namespaces.Message.Cloud) { switch apiMessage { - case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, replyToTopId, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, restrictionReason): + case let .message(flags, id, fromId, chatPeerId, fwdFrom, viaBotId, replyToMsgId, replyToTopId, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, restrictionReason): let peerId: PeerId var authorId: PeerId? - switch toId { + switch chatPeerId { case .peerUser: - if (flags & Int32(2)) != 0 { - peerId = toId.peerId - } else { - peerId = fromId.peerId - } + peerId = chatPeerId.peerId authorId = fromId.peerId case let .peerChat(chatId): peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) @@ -650,24 +574,9 @@ extension StoreMessage { self.init(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, groupingKey: groupingId, threadId: threadId, timestamp: date, flags: storeFlags, tags: tags, globalTags: globalTags, localTags: [], forwardInfo: forwardInfo, authorId: authorId, text: messageText, attributes: attributes, media: medias) case .messageEmpty: return nil - case let .messageService(flags, id, fromId, toId, replyToMsgId, date, action): - let peerId: PeerId - var authorId: PeerId? - switch toId { - case .peerUser: - if (flags & Int32(2)) != 0 { - peerId = toId.peerId - } else { - peerId = fromId.peerId - } - authorId = fromId.peerId - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - authorId = fromId.peerId - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - authorId = fromId.peerId - } + case let .messageService(flags, id, fromId, chatPeerId, replyToMsgId, date, action): + let peerId: PeerId = chatPeerId.peerId + var authorId: PeerId? = fromId.peerId var attributes: [MessageAttribute] = [] if let replyToMsgId = replyToMsgId { diff --git a/submodules/TelegramCore/Sources/UpdateMessageService.swift b/submodules/TelegramCore/Sources/UpdateMessageService.swift index adce65cfa7..7aac219a9e 100644 --- a/submodules/TelegramCore/Sources/UpdateMessageService.swift +++ b/submodules/TelegramCore/Sources/UpdateMessageService.swift @@ -58,24 +58,23 @@ class UpdateMessageService: NSObject, MTMessageService { self.putNext(groups) } case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyToMsgId, entities): - let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: .peerChat(chatId: fromId), toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, replyToTopId: nil, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) + let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: .peerChat(chatId: fromId), peerId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, replyToTopId: nil, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { self.putNext(groups) } case let .updateShortMessage(flags, id, userId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyToMsgId, entities): - let generatedFromId: Int32 - let generatedToId: Api.Peer - if (Int(flags) & 2) != 0 { - generatedFromId = self.peerId.id - generatedToId = Api.Peer.peerUser(userId: userId) + let generatedFromId: Api.Peer + if (Int(flags) & 1 << 1) != 0 { + generatedFromId = Api.Peer.peerUser(userId: self.peerId.id) } else { - generatedFromId = userId - generatedToId = Api.Peer.peerUser(userId: self.peerId.id) + generatedFromId = Api.Peer.peerUser(userId: userId) } - let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: .peerUser(userId: userId), toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, replyToTopId: nil, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) + let generatedPeerId = Api.Peer.peerUser(userId: userId) + + let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, peerId: generatedPeerId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, replyToTopId: nil, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { diff --git a/submodules/TelegramCore/Sources/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/UpdatesApiUtils.swift index 3e14da27d7..0b80c1de20 100644 --- a/submodules/TelegramCore/Sources/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/UpdatesApiUtils.swift @@ -112,50 +112,13 @@ extension Api.Message { let flags = message.flags let id = message.id let fromId = message.fromId - let toId = message.toId - let peerId: PeerId - switch toId { - case let .peerUser(userId): - let id: PeerId - if namespace == Namespaces.Message.ScheduledCloud { - id = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - } else { - if (flags & Int32(2)) != 0 { - id = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - } else { - id = fromId.peerId - } - } - peerId = id - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } + let peerId: PeerId = message.peerId.peerId return MessageId(peerId: peerId, namespace: namespace, id: id) case .messageEmpty: return nil - case let .messageService(flags, id, fromId, toId, _, _, _): - let peerId: PeerId - switch toId { - case let .peerUser(userId): - let id: PeerId - if namespace == Namespaces.Message.ScheduledCloud { - id = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - } else { - if (flags & Int32(2)) != 0 { - id = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - } else { - id = fromId.peerId - } - } - peerId = id - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } + case let .messageService(flags, id, fromId, chatPeerId, _, _, _): + let peerId: PeerId = chatPeerId.peerId return MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id) } } diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index 12d70c6efc..f3f64c5426 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -47,7 +47,7 @@ private func generateClockMinImage(color: UIColor) -> UIImage? { }) } -private func chatBubbleActionButtonImage(fillColor: UIColor, strokeColor: UIColor, foregroundColor: UIColor, image: UIImage?, iconOffset: CGPoint = CGPoint()) -> UIImage? { +public func chatBubbleActionButtonImage(fillColor: UIColor, strokeColor: UIColor, foregroundColor: UIColor, image: UIImage?, iconOffset: CGPoint = CGPoint()) -> UIImage? { return generateImage(CGSize(width: 29.0, height: 29.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(fillColor.cgColor) diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/FreeRepliesIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/FreeRepliesIcon.imageset/Contents.json new file mode 100644 index 0000000000..c8af669573 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/FreeRepliesIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "replies_sticker.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/FreeRepliesIcon.imageset/replies_sticker.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/FreeRepliesIcon.imageset/replies_sticker.pdf new file mode 100644 index 0000000000000000000000000000000000000000..adbf0484c61c6410e34668aad4f204dc100e3429 GIT binary patch literal 3999 zcmai%cUV(dx5fiRfq;lg5kx&Q1Vl<2p{Vo{n$neyNeCT+5Q-p8DMOQ96_6qbNKsG` z3`mh84gqPR0!Df-BAwwT>fCX@x%c_bbDnedv-Z2!*=N1`kKcmot7%9eBvBA(%f#x$ zT+!G2-7T#UDF6--aZZqn7Xg?q!OfoJ0HA3hLjb1X=t3fR(B3Xs5mCh@6nQo5l&L=k3FlW&ZyK8?9gbfYDG;a*x$YP@^of!wMoey{VaI(3Cj1&_V2 zu075bTMj%{u{a-Y`A!Yz*AOd~KvMGZd&KC(+r#`O8R-i@WhEOSAE)_I+jt$ZVD*QH zxKiIsd}aKK%Wy69Hn=O*mvr%*Hru&VjdJbrLJnHC7@pMqZOI^}>!13iNk@`~sJgs< z{&}#?^0C|5HjC+fS>49Fi}vRmt8q;%n>Ie6+QsEf>**}(28Qw!doYCHX8W^Zv=P0~ z^y<>zNI5ip^84@?4~%cP69AY#*8cm`gWyI2P(S=IBzO|NJn#fhK>9~OmFPyI&3giL z4-M(w{LDw6r{|@6s_#L>8xcr=1#PIN0ayVrRiX>g!^j8mOsH497UL zE|sCbj%4n&nT-xjJs%vhUkVtk1?lhJ0ymLN8G@y9O}Q558ZD0=>ulLzJkH6$Vao7| zYG((=Y^pK#?ULm)G9GHSR;@2e2HVzs6mQTM_8iy`N)VJ$6*>rlsP{`>3>4;_U`#fu zdd-L22ftZM5k-b0kUY7pd5;9gS}I)DizdbDI?~gG$8-73y7*p7WyjR%k7Por`7z*J z%XH{x`}~{C1hTPKCTL>gY;W)8JzWr-E5qjPlyI&~BvPG=Z!vO}5yX<*BKOjRBYI2D zAsggdXg?kWj?}^x9vMt`oS1I6+g_P-R)ps%*~&55W}`0ik6>gT(7KTM-IG=IDJGz5 z*MmwE)JJ+t0BH39sCwky*Hhsx)`=}&*hSc)=jUBK8FHK@#inI;a&8gdG=$4-WOOd> zT)9xls~GZ|7B`BZK2caysH67wEjvdV#ddFlbARlWoZ}xI&EofRUh%GkO`8}@C$ok| zI@E<-3^9$Dw1tEQ^;3h6ON%h`1!g&OIe6KC0>b3kdE$KeqoFR1j7Q^{N%tXAA(C)L z@oR?!f>G{FBLFBgSd)_}l}S>Q(NPKfL|NfI2%?O5&)|LA$sN=e;@1tnd`sH>pe6X~ zt3&Q=?7%DhCwHE5Njy)%;S7}l&J*S=H&m7NWx1GMt479iCMs=p z3qL%5?siG8s=oZ%ZzTR&7OUIa_nEr^se^kA0ij3I8GEsZ-h-Y64^>_eV8JwXepd74 zzQ@e_>LXRVhDU+pc@r=7#4<=c%%&-cMX8;IsYk<<<;04bO1!BM!;v%234#TT#p;>y z90ey{#A+s7A7?rkk&8_~&eO^_7GV}c#a+P;;I!fW>GRp{f`Sx zMII@7vq~Si8!ORrDJ*!&t=@rGxd-lfuK{Pai?1wMB-V^!Opi1pGgITT!f4{`B|n|_Ck*| zNg0GZ)FTOobKYpN#ECw>^IrL-mVof(BrdIRAzniv(TwU4V=X5^D`9zI@|mQL^R^g| z<7P_MdgVenfDQC;qN0BCSz#_cZ2@(aq04gGFO+U5-B5jCc<7;9pJ_>YZI-znSuH?2 z0ES6IrF~5s>nKYqO>j<`OY?{7B@d?gq}fR3h}fJ7(*Cn>w}#Q6QK``=B){@Xt_HQb zNj^+VJu@!__d6~V7m2(51^wCp`Z6&j@%d4`Vx^hNchCoC_*Hrz+;~@0eX08V2f_#V zNsLUvPH)o8>$TI<J*;lU>OwM~>E--DQ>n7Wv_uTqmQC}Wh5 z(iM)@`!7XPw$8boyO|=GVwb{@5?7*YLhR@2&nj^((d&ad2|Hn(hMac#B}-!|MHHWG zN|ue({`hXhY-MevU(L4Dx8_;-*(Cl<{(62seiQyQsa7;!rdwu5=0xU{R9m%!IongP z`Gon@Q)_ri#^PIJJ7`t3Ty9zULXkv|LcwsUd>Q^>m8pIH<*Ke@R*8}pGRn^_pBwq8 z=;^>rVKx;8OSuXLIb}Ho4VVUtpf|6&fe+WBmG3q_%#F%zz-D9fsrcVY5=wi{<(ubU zXqCl3yB@WoFlHfK8JVS(s1@6B$>@FGi1D>rN;QSudD1~xsuW$VLZ(&aFi+j(I?6i7y5NrR4uV-e!Z(7}kOHs<_zJTE8y43D4iCG4T!b^#M!Z)YVj5>NMlp-?d|+ zV|jFVd^9>Ruhsi8m(=~$)6{j+OvTNr#x-KhV}L?=p3^6|{e`?eiZ|X3>qLyre}RlG zN}?=NtL8s=Je?@+q?E_oG@4yMck0CHlcSvbk;j^fCv?l#Y+m?GfCrQ6B6bp|TE(Vf zSFAP(y9wa5&?liiYH2N2k$A0gEpDwbMm^=HpS_;gtcHviC&wq}i=v8qivMs7t=_Ee zRe@roD#ff&@*-qdvs6A@wf2o;|DwsUJf)rl-Lsx%c6I4@{?z@Z86$KTdfSYx%IV37 zJ%*6;#^CLd$+hv!aktKs9bY?k0?~G8A20iQ??1zL8_|o-9A#kx<(3~`T=ubQtfhXd zpNn$^Ts4Q5J2g6E2`5I~qDJ}*?i*CrUvsEhnru~hp<+NKOLSRqxV(1Zc3BvL%pp$p^(>koE(K6|tHd>Wpq{c4(lGqWD*F8^#* zHt}{fZ}oZI)%p4>Sc~h|4m-x###JL9J|SyYIAQvWW(#IR_~SAs$+P~?7qULIeH?5U zbq(reh-G@stZ<4;;AK$zhQ4!|vg>Q?C+eeS%3R)TZG(M~O_17x=1Kwu6NY)v))eQ} zf1z(s=AlfobW?0%>*KNOZN$CL-&FmNmCop+@?UI%WzWd1d}-S}y_C&qqed|y1 zGXW!eJPzpcGTYG^#abF=ZG^%vQ{>-{ZB{%bhW4y<=ewqJKWyNa-of3G~?^+v7S~mYsX% zG8DB(9@fik53IXR*-nr5`Zz8X7DbJe)+?@u?B(v@*V+xfk$`T`bkngk(DyT3!S7md6T08Gcx){_R$$RF8$0b~SS{?Cl6SQ6HSX#WG> zJ%4ih-&l_LWdRM#ZN2cc_Uf!F7O!UnTp@UPIuhLg1Y8n)Ltx}_LIOMF_c_bVOhs(jG5NM=~l(abl2*(Ah@s?YwfT>jl-$4bA^#5@8clQhpKDP_nSbR&Nx^^FGsy$%=tA)L z-XR$|`VnaV3&0GCM4IJvTWQ^|mYW@s=Kqhe8tnvJltIH~?NC@87J)#bWe6xFUKS~Z nltJ4OkOVXej#PmBcL}Zk`rbm*y!>8hlr$0vfkM^wG$8*2Jg?a| literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift index 919fac2fdc..76c6453490 100644 --- a/submodules/TelegramUI/Sources/AccountContext.swift +++ b/submodules/TelegramUI/Sources/AccountContext.swift @@ -303,7 +303,7 @@ public final class AccountContextImpl: AccountContext { switch location { case let .peer(peerId): return .peer(peerId) - case let .replyThread(messageId, maxReadMessageId): + case let .replyThread(messageId, _, maxReadMessageId): let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxReadMessageId: maxReadMessageId) return .external(messageId.peerId, context.state) } @@ -313,7 +313,7 @@ public final class AccountContextImpl: AccountContext { switch location { case .peer: let _ = applyMaxReadIndexInteractively(postbox: self.account.postbox, stateManager: self.account.stateManager, index: messageIndex).start() - case let .replyThread(messageId, maxReadMessageId): + case let .replyThread(messageId, _, maxReadMessageId): let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxReadMessageId: maxReadMessageId) context.applyMaxReadIndex(messageIndex: messageIndex) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index a4ec4954a6..cff35478e5 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -66,7 +66,7 @@ extension ChatLocation { switch self { case let .peer(peerId): return peerId - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): return messageId.peerId } } @@ -86,6 +86,7 @@ public final class ChatControllerOverlayPresentationData { private enum ChatLocationInfoData { case peer(Promise) + case replyThread(Promise) //case group(Promise) } @@ -363,9 +364,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .peer(peerId): locationBroadcastPanelSource = .peer(peerId) self.chatLocationInfoData = .peer(Promise()) - case .replyThread: + case let .replyThread(messageId, _, _): locationBroadcastPanelSource = .none - self.chatLocationInfoData = .peer(Promise()) + let promise = Promise() + let key = PostboxViewKey.messages([messageId]) + promise.set(context.account.postbox.combinedView(keys: [key]) + |> map { views -> Message? in + guard let view = views.views[key] as? MessagesView else { + return nil + } + return view.messages[messageId] + }) + self.chatLocationInfoData = .replyThread(promise) } self.presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -463,7 +473,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, message: message, standalone: false, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.effectiveNavigationController, dismissInput: { + return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: strongSelf.chatLocation, chatLocationContextHolder: strongSelf.chatLocationContextHolder, message: message, standalone: false, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.effectiveNavigationController, dismissInput: { self?.chatDisplayNode.dismissInput() }, present: { c, a in self?.present(c, in: .window(.root), with: a, blockInteraction: true) @@ -693,7 +703,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, openMessageContextActions: { message, node, rect, gesture in gesture?.cancel() }, navigateToMessage: { [weak self] fromId, id in - self?.navigateToMessage(from: fromId, to: .id(id)) + self?.navigateToMessage(from: fromId, to: .id(id), forceInCurrentChat: fromId.peerId == id.peerId) }, tapMessage: nil, clickThroughMessage: { [weak self] in self?.chatDisplayNode.dismissInput() }, toggleMessagesSelection: { [weak self] ids, value in @@ -1612,7 +1622,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch strongSelf.chatLocation { case let .peer(peerId): strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil) - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): let peerId = messageId.peerId strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil) } @@ -2183,7 +2193,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let result = result { if let navigationController = strongSelf.navigationController as? NavigationController { - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: result.messageId, maxReadMessageId: result.maxReadMessageId), keepStack: .always)) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: result.messageId, isChannelPost: true, maxReadMessageId: result.maxReadMessageId), keepStack: .always)) } } }) @@ -2322,13 +2332,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - let chatLocationPeerId: PeerId - switch chatLocation { - case let .peer(peerId): - chatLocationPeerId = peerId - case let .replyThread(messageId, _): - chatLocationPeerId = messageId.peerId - } + let chatLocationPeerId: PeerId = chatLocation.peerId do { let peerId = chatLocationPeerId @@ -2400,9 +2404,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch chatLocation { case let .peer(peerId): //TODO:localize - isReplyThread = peerId.id == 708513 + isReplyThread = peerId.isReplies replyThreadType = nil - case let .replyThread(_, readMessageId): + case let .replyThread(_, _, readMessageId): isReplyThread = true if readMessageId != nil { replyThreadType = .comments @@ -2415,7 +2419,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G |> deliverOnMainQueue).start(next: { [weak self] peerView, onlineMemberCount, hasScheduledMessages, peerReportNotice in if let strongSelf = self { if let peer = peerViewMainPeer(peerView) { - strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, repleThread: replyThreadType) + strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages) let imageOverride: AvatarNodeImageOverride? if strongSelf.context.account.peerId == peer.id { imageOverride = .savedMessagesIcon @@ -2630,6 +2634,197 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } })) + } else if case let .replyThread(messagePromise) = self.chatLocationInfoData { + let onlineMemberCount: Signal = .single(nil) + let hasScheduledMessages: Signal = .single(false) + self.reportIrrelvantGeoNoticePromise.set(.single(nil)) + + let isReplyThread: Bool + let replyThreadType: ChatTitleContent.ReplyThreadType + switch chatLocation { + case .peer: + replyThreadType = .replies + case let .replyThread(_, _, readMessageId): + isReplyThread = true + if readMessageId != nil { + replyThreadType = .comments + } else { + replyThreadType = .replies + } + } + + let peerView = context.account.viewTracker.peerView(peerId) + + self.peerDisposable.set((combineLatest(queue: Queue.mainQueue(), + peerView, + messagePromise.get(), + hasScheduledMessages + ) + |> deliverOnMainQueue).start(next: { [weak self] peerView, message, onlineMemberCount in + if let strongSelf = self { + + var replyCount = 0 + if let message = message { + for attribute in message.attributes { + if let attribute = attribute as? ReplyThreadMessageAttribute { + replyCount = Int(attribute.count) + } + } + } + strongSelf.chatTitleView?.titleContent = .replyThread(type: replyThreadType, count: replyCount) + + let firstTime = strongSelf.peerView == nil + strongSelf.peerView = peerView + + if strongSelf.isNodeLoaded { + strongSelf.chatDisplayNode.peerView = peerView + } + var peerIsMuted = false + if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { + if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { + peerIsMuted = true + } + } + var peerDiscussionId: PeerId? + var peerGeoLocation: PeerGeoLocation? + if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData { + if case .broadcast = peer.info { + peerDiscussionId = cachedData.linkedDiscussionPeerId + } else { + peerGeoLocation = cachedData.peerGeoLocation + } + } + var renderedPeer: RenderedPeer? + var contactStatus: ChatContactStatus? + if let peer = peerView.peers[peerView.peerId] { + if let cachedData = peerView.cachedData as? CachedUserData { + contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil) + } else if let cachedData = peerView.cachedData as? CachedGroupData { + var invitedBy: Peer? + if let invitedByPeerId = cachedData.invitedBy { + if let peer = peerView.peers[invitedByPeerId] { + invitedBy = peer + } + } + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy) + } else if let cachedData = peerView.cachedData as? CachedChannelData { + var canReportIrrelevantLocation = true + if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.participationStatus == .member { + canReportIrrelevantLocation = false + } + canReportIrrelevantLocation = false + var invitedBy: Peer? + if let invitedByPeerId = cachedData.invitedBy { + if let peer = peerView.peers[invitedByPeerId] { + invitedBy = peer + } + } + contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy) + } + + var peers = SimpleDictionary() + peers[peer.id] = peer + if let associatedPeerId = peer.associatedPeerId, let associatedPeer = peerView.peers[associatedPeerId] { + peers[associatedPeer.id] = associatedPeer + } + renderedPeer = RenderedPeer(peerId: peer.id, peers: peers) + } + + var isNotAccessible: Bool = false + if let cachedChannelData = peerView.cachedData as? CachedChannelData { + isNotAccessible = cachedChannelData.isNotAccessible + } + + if firstTime && isNotAccessible { + strongSelf.context.account.viewTracker.forceUpdateCachedPeerData(peerId: peerView.peerId) + } + + var hasBots: Bool = false + if let peer = peerView.peers[peerView.peerId] { + if let cachedGroupData = peerView.cachedData as? CachedGroupData { + if !cachedGroupData.botInfos.isEmpty { + hasBots = true + } + } else if let cachedChannelData = peerView.cachedData as? CachedChannelData, let channel = peer as? TelegramChannel, case .group = channel.info { + if !cachedChannelData.botInfos.isEmpty { + hasBots = true + } + } + } + + let isArchived: Bool = peerView.groupId == Namespaces.PeerGroup.archive + + var explicitelyCanPinMessages: Bool = false + if let cachedUserData = peerView.cachedData as? CachedUserData { + explicitelyCanPinMessages = cachedUserData.canPinMessages + } else if peerView.peerId == context.account.peerId { + explicitelyCanPinMessages = true + } + + var animated = false + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat, let updated = renderedPeer?.peer as? TelegramSecretChat, peer.embeddedState != updated.embeddedState { + animated = true + } + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, let updated = renderedPeer?.peer as? TelegramChannel { + if peer.participationStatus != updated.participationStatus { + animated = true + } + } + + var didDisplayActionsPanel = false + if let contactStatus = strongSelf.presentationInterfaceState.contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { + if !peerStatusSettings.flags.isEmpty { + if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { + didDisplayActionsPanel = true + } else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) { + didDisplayActionsPanel = true + } else if peerStatusSettings.contains(.canShareContact) { + didDisplayActionsPanel = true + } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { + didDisplayActionsPanel = true + } + } + } + + var displayActionsPanel = false + if let contactStatus = contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { + if !peerStatusSettings.flags.isEmpty { + if contactStatus.canAddContact && peerStatusSettings.contains(.canAddContact) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.canReport) || peerStatusSettings.contains(.canBlock) { + displayActionsPanel = true + } else if peerStatusSettings.contains(.canShareContact) { + displayActionsPanel = true + } else if contactStatus.canReportIrrelevantLocation && peerStatusSettings.contains(.canReportIrrelevantGeoLocation) { + displayActionsPanel = true + } + } + } + + if displayActionsPanel != didDisplayActionsPanel { + animated = true + } + + if strongSelf.preloadHistoryPeerId != peerDiscussionId { + strongSelf.preloadHistoryPeerId = peerDiscussionId + if let peerDiscussionId = peerDiscussionId { + strongSelf.preloadHistoryPeerIdDisposable.set(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: peerDiscussionId)) + } else { + strongSelf.preloadHistoryPeerIdDisposable.set(nil) + } + } + + strongSelf.updateChatPresentationInterfaceState(animated: animated, interactive: false, { + return $0.updatedPeer { _ in + return renderedPeer + }.updatedIsNotAccessible(isNotAccessible).updatedContactStatus(contactStatus).updatedHasBots(hasBots).updatedIsArchived(isArchived).updatedPeerIsMuted(peerIsMuted).updatedPeerDiscussionId(peerDiscussionId).updatedPeerGeoLocation(peerGeoLocation).updatedExplicitelyCanPinMessages(explicitelyCanPinMessages).updatedHasScheduledMessages(false) + }) + if !strongSelf.didSetChatLocationInfoReady { + strongSelf.didSetChatLocationInfoReady = true + strongSelf._chatLocationInfoReady.set(.single(true)) + } + } + })) } } @@ -3043,8 +3238,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else if let _ = combinedInitialData.cachedData as? CachedSecretChatData { } - if case let .replyThread(messageId, _) = strongSelf.chatLocation { - pinnedMessageId = messageId + if case let .replyThread(messageId, isChannelPost, _) = strongSelf.chatLocation { + if isChannelPost { + pinnedMessageId = messageId + } else { + pinnedMessageId = nil + } } var pinnedMessage: Message? @@ -3186,8 +3385,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else if let _ = cachedData as? CachedSecretChatData { } - if case let .replyThread(messageId, _) = strongSelf.chatLocation { - pinnedMessageId = messageId + if case let .replyThread(messageId, isChannelPost, _) = strongSelf.chatLocation { + if isChannelPost { + pinnedMessageId = messageId + } else { + pinnedMessageId = nil + } } var pinnedMessage: Message? @@ -4017,8 +4220,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) strongSelf.updateItemNodesSearchTextHighlightStates() } - }, navigateToMessage: { [weak self] messageId in - self?.navigateToMessage(from: nil, to: .id(messageId)) + }, navigateToMessage: { [weak self] messageId, dropStack in + self?.navigateToMessage(from: nil, to: .id(messageId), dropStack: dropStack) }, navigateToChat: { [weak self] peerId in guard let strongSelf = self else { return @@ -4816,8 +5019,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if let navigationController = strongSelf.effectiveNavigationController { - let subject: ChatControllerSubject? = nil// sourceMessageId.flatMap(ChatControllerSubject.message) - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: replyThreadResult.messageId, maxReadMessageId: replyThreadResult.maxReadMessageId), subject: subject, keepStack: .always)) + let subject: ChatControllerSubject? = sourceMessageId.flatMap(ChatControllerSubject.message) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: replyThreadResult.messageId, isChannelPost: false, maxReadMessageId: replyThreadResult.maxReadMessageId), subject: subject, keepStack: .always)) } }, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get())) @@ -6024,6 +6227,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } })) + case .replyThread: + break } case .search: self.interfaceInteraction?.beginMessageSearch(.everything, "") @@ -6228,6 +6433,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) })) + case .replyThread: + break } case .toggleInfoPanel: self.updateChatPresentationInterfaceState(animated: true, interactive: true, { @@ -7250,7 +7457,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch self.chatLocation { case .peer: break - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): defaultReplyMessageId = messageId } @@ -7297,7 +7504,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch self.chatLocation { case let .peer(peerIdValue): peerId = peerIdValue - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): peerId = messageId.peerId } @@ -7715,13 +7922,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } return nil } + var searchTopMsgId: MessageId? + switch self.chatLocation { + case .peer: + break + case let .replyThread(messageId, _, _): + searchTopMsgId = messageId + } switch search.domain { case .everything: - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: self.chatLocation.peerId, fromId: nil, tags: nil, topMsgId: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: self.chatLocation.peerId, fromId: nil, tags: nil, topMsgId: searchTopMsgId), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) case .members: derivedSearchState = nil case let .member(peer): - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: self.chatLocation.peerId, fromId: peer.id, tags: nil, topMsgId: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: self.chatLocation.peerId, fromId: peer.id, tags: nil, topMsgId: searchTopMsgId), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) } } @@ -7868,17 +8082,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) { + public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, dropStack: Bool = false, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) { let scrollPosition: ListViewScrollPosition if case .upperBound = messageLocation { scrollPosition = .top(0.0) } else { scrollPosition = .center(.bottom) } - self.navigateToMessage(from: nil, to: messageLocation, scrollPosition: scrollPosition, rememberInStack: false, forceInCurrentChat: forceInCurrentChat, animated: animated, completion: completion, customPresentProgress: customPresentProgress) + self.navigateToMessage(from: nil, to: messageLocation, scrollPosition: scrollPosition, rememberInStack: false, forceInCurrentChat: forceInCurrentChat, dropStack: dropStack, animated: animated, completion: completion, customPresentProgress: customPresentProgress) } - private func navigateToMessage(from fromId: MessageId?, to messageLocation: NavigateToMessageLocation, scrollPosition: ListViewScrollPosition = .center(.bottom), rememberInStack: Bool = true, forceInCurrentChat: Bool = false, animated: Bool = true, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) { + private func navigateToMessage(from fromId: MessageId?, to messageLocation: NavigateToMessageLocation, scrollPosition: ListViewScrollPosition = .center(.bottom), rememberInStack: Bool = true, forceInCurrentChat: Bool = false, dropStack: Bool = false, animated: Bool = true, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) { if self.isNodeLoaded { var fromIndex: MessageIndex? @@ -7890,11 +8104,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } + var forceInCurrentChat = forceInCurrentChat + if case let .peer(peerId) = self.chatLocation, messageLocation.peerId == peerId { forceInCurrentChat = true + } + if case let .peer(peerId) = self.chatLocation, let messageId = messageLocation.messageId, (messageId.peerId != peerId && !forceInCurrentChat) || (self.presentationInterfaceState.isScheduledMessages && messageId.id != 0 && !Namespaces.Message.allScheduled.contains(messageId.namespace)) { if let navigationController = self.effectiveNavigationController { self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(messageId), keepStack: .always)) } - } else if case let .peer(peerId) = self.chatLocation, (messageLocation.peerId == peerId || forceInCurrentChat) { + } else if forceInCurrentChat { if let _ = fromId, let fromIndex = fromIndex, rememberInStack { self.historyNavigationStack.add(fromIndex) } @@ -7925,9 +8143,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .index(index): searchLocation = .index(index) case .upperBound: - searchLocation = .index(MessageIndex.upperBound(peerId: peerId)) + searchLocation = .index(MessageIndex.upperBound(peerId: self.chatLocation.peerId)) } - let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50), id: 0), context: self.context, chatLocation: self.chatLocation, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) + let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50), id: 0), context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) let signal = historyView |> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in switch historyView { @@ -8019,7 +8237,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.historyNavigationStack.add(fromIndex) } self.loadingMessage.set(true) - let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50), id: 0), context: self.context, chatLocation: self.chatLocation, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) + let historyView = preloadedChatHistoryViewForLocation(ChatHistoryLocationInput(content: .InitialSearch(location: searchLocation, count: 50), id: 0), context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: nil, additionalData: []) let signal = historyView |> mapToSignal { historyView -> Signal in switch historyView { @@ -8041,7 +8259,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.historyNode.scrollToMessage(from: fromIndex, to: index, animated: animated, scrollPosition: scrollPosition) completion?() } else { - strongSelf.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message($0) })) + if let navigationController = strongSelf.effectiveNavigationController { + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(messageLocation.peerId), subject: messageLocation.messageId.flatMap { .message($0) })) + } completion?() } } @@ -8791,7 +9011,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if let selectedTransitionNode = selectedTransitionNode { - if let previewData = chatMessagePreviewControllerData(context: self.context, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: self.effectiveNavigationController) { + if let previewData = chatMessagePreviewControllerData(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: self.effectiveNavigationController) { switch previewData { case let .gallery(gallery): gallery.setHintWillBePresentedInPreviewingContext(true) @@ -8881,36 +9101,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - private func debugStreamSingleVideo(_ id: MessageId) { - let gallery = GalleryController(context: self.context, source: .peerMessagesAtId(id), streamSingleVideo: true, replaceRootController: { [weak self] controller, ready in - if let strongSelf = self { - strongSelf.effectiveNavigationController?.replaceTopController(controller, animated: false, ready: ready) - } - }, baseNavigationController: self.effectiveNavigationController) - - self.chatDisplayNode.dismissInput() - self.present(gallery, in: .window(.root), with: GalleryControllerPresentationArguments(transitionArguments: { [weak self] messageId, media in - if let strongSelf = self { - var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? - strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? ChatMessageItemView { - if let result = itemNode.transitionNode(id: messageId, media: media) { - transitionNode = result - } - } - } - if let transitionNode = transitionNode { - return GalleryTransitionArguments(transitionNode: transitionNode, addToTransitionSurface: { view in - if let strongSelf = self { - strongSelf.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: strongSelf.chatDisplayNode.historyNode.view) - } - }) - } - } - return nil - })) - } - private func presentBanMessageOptions(accountPeerId: PeerId, author: Peer, messageIds: Set, options: ChatAvailableMessageActionOptions) { let peerId = self.chatLocation.peerId do { diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 502aec5017..a63346c975 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -111,6 +111,42 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView, } } + if case let .replyThread(messageId, isChannelPost, _) = location, !isChannelPost { + loop: for entry in view.additionalData { + switch entry { + case let .message(id, message) where id == messageId: + if let message = message { + let selection: ChatHistoryMessageSelection + if let selectedMessages = selectedMessages { + selection = .selectable(selected: selectedMessages.contains(message.id)) + } else { + selection = .none + } + + var adminRank: CachedChannelAdminRank? + if let author = message.author { + adminRank = adminRanks[author.id] + } + + var contentTypeHint: ChatMessageEntryContentType = .generic + if presentationData.largeEmoji, message.media.isEmpty { + if stickersEnabled && message.text.count == 1, let _ = associatedData.animatedEmojiStickers[message.text.basicEmoji.0] { + contentTypeHint = .animatedEmoji + } else if message.text.count < 10 && messageIsElligibleForLargeEmoji(message) { + contentTypeHint = .largeEmoji + } + } + + + entries.insert(.MessageEntry(message, presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[message.id])), at: 0) + } + break loop + default: + break + } + } + } + if includeChatInfoEntry { if view.earlierId == nil { var cachedPeerData: CachedPeerData? diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index bd3edfb56e..9095da2da9 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -583,7 +583,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if !isAuxiliaryChat { additionalData.append(.totalUnreadState) } - if case let .replyThread(messageId, _) = chatLocation { + if case let .replyThread(messageId, _, _) = chatLocation { additionalData.append(.message(messageId)) } @@ -1187,7 +1187,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if loaded.firstIndex < 5 && historyView.originalView.laterId != nil { self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .message(lastEntry.index), anchorIndex: .message(lastEntry.index), count: historyMessageCount), id: self.takeNextHistoryLocationId()) } else if loaded.firstIndex < 5, historyView.originalView.laterId == nil, !historyView.originalView.holeLater, let chatHistoryLocationValue = self.chatHistoryLocationValue, !chatHistoryLocationValue.isAtUpperBound, historyView.originalView.anchorIndex != .upperBound { + //TODO:localize + #if !DEBUG self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .upperBound, anchorIndex: .upperBound, count: historyMessageCount), id: self.takeNextHistoryLocationId()) + #endif } else if loaded.lastIndex >= historyView.filteredEntries.count - 5 && historyView.originalView.earlierId != nil { self.chatHistoryLocationValue = ChatHistoryLocationInput(content: .Navigation(index: .message(firstEntry.index), anchorIndex: .message(firstEntry.index), count: historyMessageCount), id: self.takeNextHistoryLocationId()) } diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index bad6e9fce0..c96e11f581 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -7,8 +7,7 @@ import SwiftSignalKit import Display import AccountContext -func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal { - let chatLocationContextHolder = Atomic(value: nil) +func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, fixedCombinedReadStates: MessageHistoryViewReadState?, tagMask: MessageTags?, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = []) -> Signal { return chatHistoryViewForLocation(location, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: false, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, additionalData: additionalData, orderStatistics: orderStatistics) |> castError(Bool.self) |> mapToSignal { update -> Signal in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index b08c266d6e..d87cae779c 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -575,6 +575,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } var threadId: Int64? + var threadMessageCount: Int = 0 if case .peer = chatPresentationInterfaceState.chatLocation { if let value = messages[0].threadId { threadId = value @@ -582,6 +583,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: for attribute in messages[0].attributes { if let attribute = attribute as? ReplyThreadMessageAttribute, attribute.count > 0 { threadId = makeMessageThreadId(messages[0].id) + threadMessageCount = Int(attribute.count) } } } @@ -590,7 +592,17 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: if let threadId = threadId { let replyThreadId = makeThreadIdMessageId(peerId: messages[0].id.peerId, threadId: threadId) //TODO:localize - actions.append(.action(ContextMenuActionItem(text: "View Replies", icon: { theme in + let text: String + if threadMessageCount != 0 { + if threadMessageCount == 1 { + text = "View 1 reply" + } else { + text = "View \(threadMessageCount) replies" + } + } else { + text = "View Thread" + } + actions.append(.action(ContextMenuActionItem(text: text, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replies"), color: theme.actionSheet.primaryTextColor) }, action: { c, _ in let foundIndex = Promise() @@ -728,7 +740,11 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in - let _ = (exportMessageLink(account: context.account, peerId: message.id.peerId, messageId: message.id) + var threadMessageId: MessageId? + if case let .replyThread(replyThread, _, _) = chatPresentationInterfaceState.chatLocation { + threadMessageId = replyThread + } + let _ = (exportMessageLink(account: context.account, peerId: message.id.peerId, messageId: message.id, threadMessageId: threadMessageId) |> map { result -> String? in return result } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index 10f21d6ed8..7eaca60e1b 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -71,7 +71,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState var displayInputTextPanel = false if let peer = chatPresentationInterfaceState.renderedPeer?.peer { - if peer.id.id == 708513 { + if peer.id.isReplies { if let currentPanel = (currentPanel as? ChatChannelSubscriberInputPanelNode) ?? (currentSecondaryPanel as? ChatChannelSubscriberInputPanelNode) { return (currentPanel, nil) } else { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift index 233339ca6b..54794ece85 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -65,11 +65,23 @@ func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Cha func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: ChatPresentationInterfaceState, strings: PresentationStrings, currentButton: ChatNavigationButton?, target: Any?, selector: Selector?, chatInfoNavigationButton: ChatNavigationButton?) -> ChatNavigationButton? { if case .replyThread = presentationInterfaceState.chatLocation { - return nil + if case .search = currentButton?.action { + return currentButton + } else { + let buttonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(presentationInterfaceState.theme), style: .plain, target: target, action: selector) + buttonItem.accessibilityLabel = strings.Conversation_Search + return ChatNavigationButton(action: .search, buttonItem: buttonItem) + } } if case let .peer(peerId) = presentationInterfaceState.chatLocation { - if peerId.id == 708513 { - return nil + if peerId.isReplies { + if case .search = currentButton?.action { + return currentButton + } else { + let buttonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(presentationInterfaceState.theme), style: .plain, target: target, action: selector) + buttonItem.accessibilityLabel = strings.Conversation_Search + return ChatNavigationButton(action: .search, buttonItem: buttonItem) + } } } if let _ = presentationInterfaceState.interfaceState.selectionState { diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 4404f27d79..4a6210c0b0 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -32,6 +32,77 @@ extension AnimatedStickerNode: GenericAnimatedStickerNode { } +class ChatMessageShareButton: HighlightTrackingButtonNode { + private let backgroundNode: ASImageNode + private let iconNode: ASImageNode + + private var theme: PresentationTheme? + private var isReplies: Bool = false + + private var textNode: ImmediateTextNode? + + init() { + self.backgroundNode = ASImageNode() + self.iconNode = ASImageNode() + + super.init(pointerStyle: nil) + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.iconNode) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(presentationData: ChatPresentationData, message: Message, account: Account) -> CGSize { + var isReplies = false + var replyCount = 0 + if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info { + for attribute in message.attributes { + if let attribute = attribute as? ReplyThreadMessageAttribute { + replyCount = Int(attribute.count) + isReplies = true + break + } + } + } + + if self.theme !== presentationData.theme.theme || self.isReplies != isReplies { + self.theme = presentationData.theme.theme + self.isReplies = isReplies + + let graphics = PresentationResourcesChat.additionalGraphics(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners) + var updatedShareButtonBackground: UIImage? + if isReplies { + updatedShareButtonBackground = chatBubbleActionButtonImage(fillColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.message.shareButtonFillColor, wallpaper: presentationData.theme.wallpaper), strokeColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.message.shareButtonStrokeColor, wallpaper: presentationData.theme.wallpaper), foregroundColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: presentationData.theme.wallpaper), image: UIImage(bundleImageName: "Chat/Message/FreeRepliesIcon"), iconOffset: CGPoint(x: 0.5, y: 1.0))?.stretchableImage(withLeftCapWidth: 29 / 2, topCapHeight: 29 / 2) + } else if message.id.peerId == account.peerId { + updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage + } else { + updatedShareButtonBackground = graphics.chatBubbleShareButtonImage + } + self.backgroundNode.image = updatedShareButtonBackground + } + var size = CGSize(width: 29.0, height: 29.0) + if isReplies, replyCount > 0 { + let textNode: ImmediateTextNode + if let current = self.textNode { + textNode = current + } else { + textNode = ImmediateTextNode() + self.textNode = textNode + self.addSubnode(textNode) + } + + } else if let textNode = self.textNode { + self.textNode = nil + textNode.removeFromSupernode() + } + self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size) + return size + } +} + class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { private let contextSourceNode: ContextExtractedContentContainingNode private let containerNode: ContextControllerSourceNode @@ -49,7 +120,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { private var selectionNode: ChatMessageSelectionNode? private var deliveryFailedNode: ChatMessageDeliveryFailedNode? - private var shareButtonNode: HighlightableButtonNode? + private var shareButtonNode: ChatMessageShareButton? var telegramFile: TelegramMediaFile? var emojiFile: TelegramMediaFile? @@ -466,7 +537,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } else if incoming { hasAvatar = true } - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): if messageId.peerId != item.context.account.peerId { if messageId.peerId.isGroupOrChannel && item.message.author != nil { var isBroadcastChannel = false @@ -494,7 +565,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var needShareButton = false if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { needShareButton = false - } else if item.message.id.peerId == item.context.account.peerId { + } else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { for attribute in item.content.firstMessage.attributes { if let _ = attribute as? SourceReferenceMessageAttribute { needShareButton = true @@ -647,7 +718,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { - if case let .replyThread(replyThreadMessageId, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId { } else { replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude)) } @@ -680,30 +751,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage } - var updatedShareButtonBackground: UIImage? - - var updatedShareButtonNode: HighlightableButtonNode? + var updatedShareButtonNode: ChatMessageShareButton? if needShareButton { - if currentShareButtonNode != nil { + if let currentShareButtonNode = currentShareButtonNode { updatedShareButtonNode = currentShareButtonNode - if item.presentationData.theme !== currentItem?.presentationData.theme { - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - if item.message.id.peerId == item.context.account.peerId { - updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage - } else { - updatedShareButtonBackground = graphics.chatBubbleShareButtonImage - } - } } else { - let buttonNode = HighlightableButtonNode() - let buttonIcon: UIImage? - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - if item.message.id.peerId == item.context.account.peerId { - buttonIcon = graphics.chatBubbleNavigateButtonImage - } else { - buttonIcon = graphics.chatBubbleShareButtonImage - } - buttonNode.setBackgroundImage(buttonIcon, for: [.normal]) + let buttonNode = ChatMessageShareButton() updatedShareButtonNode = buttonNode } } @@ -844,18 +897,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { strongSelf.addSubnode(updatedShareButtonNode) updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside) } - if let updatedShareButtonBackground = updatedShareButtonBackground { - strongSelf.shareButtonNode?.setBackgroundImage(updatedShareButtonBackground, for: [.normal]) - } + let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account) + updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - 30.0), size: buttonSize) } else if let shareButtonNode = strongSelf.shareButtonNode { shareButtonNode.removeFromSupernode() strongSelf.shareButtonNode = nil } - if let shareButtonNode = strongSelf.shareButtonNode { - shareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - 30.0), size: CGSize(width: 29.0, height: 29.0)) - } - dateAndStatusApply(false) strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: max(displayLeftInset, updatedImageFrame.maxX - dateAndStatusSize.width - 4.0), y: updatedImageFrame.maxY - dateAndStatusSize.height - 4.0), size: dateAndStatusSize) @@ -1174,6 +1222,15 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { @objc func shareButtonPressed() { if let item = self.item { + if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info { + for attribute in item.message.attributes { + if let _ = attribute as? ReplyThreadMessageAttribute { + item.controllerInteraction.openMessageReplies(item.message.id) + return + } + } + } + if item.content.firstMessage.id.peerId == item.context.account.peerId { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index fe0a4a7627..b657aa50a0 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -773,7 +773,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let nameFont = Font.medium(fontSize) let inlineBotPrefixFont = Font.regular(fontSize) - let inlineBotNameFont = nameFont let baseWidth = params.width - params.leftInset - params.rightInset @@ -792,7 +791,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode var isCrosspostFromChannel = false if let _ = sourceReference { - if firstMessage.id.peerId != item.context.account.peerId { + if !firstMessage.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { isCrosspostFromChannel = true } } @@ -809,13 +808,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode switch item.chatLocation { case let .peer(peerId): chatLocationPeerId = peerId - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): chatLocationPeerId = messageId.peerId } do { let peerId = chatLocationPeerId - if item.message.id.peerId == item.context.account.peerId { + if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { if let forwardInfo = item.content.firstMessage.forwardInfo { ignoreForward = true effectiveAuthor = forwardInfo.author @@ -838,7 +837,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } } - if peerId != item.context.account.peerId { + if !peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { if peerId.isGroupOrChannel && effectiveAuthor != nil { var isBroadcastChannel = false if let peer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .broadcast = peer.info { @@ -871,7 +870,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode var needShareButton = false if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) { needShareButton = false - } else if item.message.id.peerId == item.context.account.peerId { + } else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { if let _ = sourceReference { needShareButton = true } @@ -1003,7 +1002,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode inlineBotNameString = attribute.title } } else if let attribute = attribute as? ReplyMessageAttribute { - if case let .replyThread(replyThreadMessageId, _) = item.chatLocation, replyThreadMessageId == attribute.messageId { + if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == attribute.messageId { } else { replyMessage = firstMessage.associatedMessages[attribute.messageId] } @@ -1677,7 +1676,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode updatedShareButtonNode = currentShareButtonNode if item.presentationData.theme !== currentItem?.presentationData.theme { let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - if item.message.id.peerId == item.context.account.peerId { + if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage } else { updatedShareButtonBackground = graphics.chatBubbleShareButtonImage @@ -1687,7 +1686,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode let buttonNode = HighlightableButtonNode() let buttonIcon: UIImage? let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - if item.message.id.peerId == item.context.account.peerId { + if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { buttonIcon = graphics.chatBubbleNavigateButtonImage } else { buttonIcon = graphics.chatBubbleShareButtonImage @@ -2425,7 +2424,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode var openPeerId = item.effectiveAuthorId ?? author.id var navigate: ChatControllerInteractionNavigateToPeer - if item.content.firstMessage.id.peerId == item.context.account.peerId { + if item.content.firstMessage.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { navigate = .chat(textInputState: nil, subject: nil, peekData: nil) } else { navigate = .info @@ -2441,7 +2440,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty { item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame) } else { - if item.message.id.peerId == item.context.account.peerId, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil { + if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId), let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil { if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) { } else if case .member = channel.participationStatus { } else { @@ -3008,7 +3007,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode @objc func shareButtonPressed() { if let item = self.item { - if item.content.firstMessage.id.peerId == item.context.account.peerId { + if item.content.firstMessage.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { item.controllerInteraction.navigateToMessage(item.content.firstMessage.id, attribute.messageId) diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index d9bb1312d3..85f18630af 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -182,7 +182,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { switch item.chatLocation { case let .peer(peerId): messagePeerId = peerId - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): messagePeerId = messageId.peerId } @@ -337,7 +337,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { } if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { - if case let .replyThread(replyThreadMessageId, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId { } else { replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index a1ae1446d7..b652decc42 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -119,12 +119,12 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs } } - if lhs.id.peerId == accountPeerId { + if lhs.id.peerId.isRepliesOrSavedMessages(accountPeerId: accountPeerId) { if let forwardInfo = lhs.forwardInfo { lhsEffectiveAuthor = forwardInfo.author } } - if rhs.id.peerId == accountPeerId { + if rhs.id.peerId.isRepliesOrSavedMessages(accountPeerId: accountPeerId) { if let forwardInfo = rhs.forwardInfo { rhsEffectiveAuthor = forwardInfo.author } @@ -317,13 +317,13 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { switch chatLocation { case let .peer(peerId): messagePeerId = peerId - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): messagePeerId = messageId.peerId } do { let peerId = messagePeerId - if peerId == context.account.peerId { + if peerId.isRepliesOrSavedMessages(accountPeerId: context.account.peerId) { if let forwardInfo = content.firstMessage.forwardInfo { effectiveAuthor = forwardInfo.author if effectiveAuthor == nil, let authorSignature = forwardInfo.authorSignature { diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 29320c50b2..9a5ea55960 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -249,7 +249,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } else if incoming { hasAvatar = true } - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): if messageId.peerId != item.context.account.peerId { if messageId.peerId.isGroupOrChannel && item.message.author != nil { var isBroadcastChannel = false @@ -411,7 +411,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } } if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { - if case let .replyThread(replyThreadMessageId, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessageId, isChannelPost, _) = item.chatLocation, isChannelPost, replyThreadMessageId == replyAttribute.messageId { } else { replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } diff --git a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift index 9d74eed519..81eed2ea55 100644 --- a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift @@ -72,7 +72,7 @@ final class ChatPanelInterfaceInteraction { let openSearchResults: () -> Void let openCalendarSearch: () -> Void let toggleMembersSearch: (Bool) -> Void - let navigateToMessage: (MessageId) -> Void + let navigateToMessage: (MessageId, Bool) -> Void let navigateToChat: (PeerId) -> Void let navigateToProfile: (PeerId) -> Void let openPeerInfo: () -> Void @@ -146,7 +146,7 @@ final class ChatPanelInterfaceInteraction { navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, - navigateToMessage: @escaping (MessageId) -> Void, + navigateToMessage: @escaping (MessageId, Bool) -> Void, navigateToChat: @escaping (PeerId) -> Void, navigateToProfile: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 016edcb7d3..d400751579 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -28,6 +28,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { private var currentMessage: Message? private var previousMediaReference: AnyMediaReference? + private var isReplyThread: Bool = false + private let fetchDisposable = MetaDisposable() private let queue = Queue() @@ -121,9 +123,9 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { } else { isReplyThread = false } + self.isReplyThread = isReplyThread self.closeButton.isHidden = isReplyThread - self.tapButton.isUserInteractionEnabled = !isReplyThread var messageUpdated = false if let currentMessage = self.currentMessage, let pinnedMessage = interfaceState.pinnedMessage { @@ -296,7 +298,15 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { @objc func tapped() { if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage { - interfaceInteraction.navigateToMessage(message.id) + if self.isReplyThread { + if let sourceReference = message.sourceReference { + interfaceInteraction.navigateToMessage(sourceReference.messageId, true) + } else { + interfaceInteraction.navigateToMessage(message.id, false) + } + } else { + interfaceInteraction.navigateToMessage(message.id, false) + } } } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift index c662778197..763875bfce 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift @@ -75,7 +75,7 @@ final class ChatRecentActionsController: TelegramBaseController { }, navigateMessageSearch: { _ in }, openCalendarSearch: { }, toggleMembersSearch: { _ in - }, navigateToMessage: { _ in + }, navigateToMessage: { _, _ in }, navigateToChat: { _ in }, navigateToProfile: { _ in }, openPeerInfo: { diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 5fd3198a81..9f371b3aeb 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -157,7 +157,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { break } } - return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, message: message, standalone: true, reverseMessageGalleryOrder: false, navigationController: navigationController, dismissInput: { + return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: true, reverseMessageGalleryOrder: false, navigationController: navigationController, dismissInput: { //self?.chatDisplayNode.dismissInput() }, present: { c, a in self?.presentController(c, a) @@ -816,6 +816,10 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { if let navigationController = strongSelf.getNavigationController() { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(messageId))) } + case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxReadMessageId, messageId): + if let navigationController = strongSelf.getNavigationController() { + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: replyThreadMessageId, isChannelPost: isChannelPost, maxReadMessageId: maxReadMessageId), subject: .message(messageId))) + } case let .stickerPack(name): let packReference: StickerPackReference = .name(name) strongSelf.presentController(StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: strongSelf.getNavigationController()), nil) diff --git a/submodules/TelegramUI/Sources/ChatSearchInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatSearchInputPanelNode.swift index 8cbe14652a..2038a12862 100644 --- a/submodules/TelegramUI/Sources/ChatSearchInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchInputPanelNode.swift @@ -189,6 +189,10 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { canSearchMembers = false } } + if case .replyThread = interfaceState.chatLocation { + self.calendarButton.isHidden = true + canSearchMembers = false + } self.membersButton.isHidden = (!(interfaceState.search?.query.isEmpty ?? true)) || self.displayActivity || !canSearchMembers let resultsEnabled = (resultCount ?? 0) > 0 diff --git a/submodules/TelegramUI/Sources/ChatTitleView.swift b/submodules/TelegramUI/Sources/ChatTitleView.swift index 73c03b865f..e302cd7d0d 100644 --- a/submodules/TelegramUI/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Sources/ChatTitleView.swift @@ -23,7 +23,8 @@ enum ChatTitleContent { case comments } - case peer(peerView: PeerView, onlineMemberCount: Int32?, isScheduledMessages: Bool, repleThread: ReplyThreadType?) + case peer(peerView: PeerView, onlineMemberCount: Int32?, isScheduledMessages: Bool) + case replyThread(type: ReplyThreadType, count: Int) case group([Peer]) case custom(String) } @@ -105,16 +106,10 @@ final class ChatTitleView: UIView, NavigationBarTitleView { var titleScamIcon = false var isEnabled = true switch titleContent { - case let .peer(peerView, _, isScheduledMessages, replyThreadType): - if let replyThreadType = replyThreadType { + case let .peer(peerView, _, isScheduledMessages): + if peerView.peerId.isReplies { //TODO:localize - let typeText: String - switch replyThreadType { - case .replies: - typeText = "Replies" - case .comments: - typeText = "Comments" - } + let typeText: String = "Replies" string = NSAttributedString(string: typeText, font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor) isEnabled = false } else if isScheduledMessages { @@ -146,6 +141,29 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } } } + case let .replyThread(type, count): + //TODO:localize + let typeText: String + switch type { + case .comments: + if count == 0 { + typeText = "Comments" + } else if count == 1 { + typeText = "1 Comment" + } else { + typeText = "\(count) Comments" + } + case .replies: + if count == 0 { + typeText = "Replies" + } else if count == 1 { + typeText = "1 Reply" + } else { + typeText = "\(count) Replies" + } + } + string = NSAttributedString(string: typeText, font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor) + isEnabled = false case .group: string = NSAttributedString(string: "Feed", font: Font.medium(17.0), textColor: titleTheme.rootController.navigationBar.primaryTextColor) case let .custom(text): @@ -194,9 +212,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView { var inputActivitiesAllowed = true if let titleContent = self.titleContent { switch titleContent { - case let .peer(peerView, _, isScheduledMessages, replyThreadType): + case let .peer(peerView, _, isScheduledMessages): if let peer = peerViewMainPeer(peerView) { - if peer.id == self.account.peerId || isScheduledMessages || replyThreadType != nil || peer.id.id == 708513 { + if peer.id == self.account.peerId || isScheduledMessages || peer.id.isReplies { inputActivitiesAllowed = false } } @@ -282,10 +300,10 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } else { if let titleContent = self.titleContent { switch titleContent { - case let .peer(peerView, onlineMemberCount, isScheduledMessages, replyThreadType): + case let .peer(peerView, onlineMemberCount, isScheduledMessages): if let peer = peerViewMainPeer(peerView) { let servicePeer = isServicePeer(peer) - if peer.id == self.account.peerId || isScheduledMessages || replyThreadType != nil || peer.id.id == 708513 { + if peer.id == self.account.peerId || isScheduledMessages || peer.id.isReplies { let string = NSAttributedString(string: "", font: Font.regular(13.0), textColor: titleTheme.rootController.navigationBar.secondaryTextColor) state = .info(string, .generic) } else if let user = peer as? TelegramUser { diff --git a/submodules/TelegramUI/Sources/EditAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/EditAccessoryPanelNode.swift index a849ec4517..87c6738995 100644 --- a/submodules/TelegramUI/Sources/EditAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/EditAccessoryPanelNode.swift @@ -347,7 +347,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode { @objc func contentTap(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state, let message = self.currentMessage { - self.interfaceInteraction?.navigateToMessage(message.id) + self.interfaceInteraction?.navigateToMessage(message.id, false) } } } diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index b09a06020e..04c124c03a 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -125,7 +125,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam if message.id.peerId == peerId { return true } - case let .replyThread(messageId, _): + case let .replyThread(messageId, _, _): if message.id.peerId == messageId.peerId { return true } diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index fed47ab635..7fc987a44b 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -37,7 +37,7 @@ private enum ChatMessageGalleryControllerData { case other(Media) } -private func chatMessageGalleryControllerData(context: AccountContext, message: Message, navigationController: NavigationController?, standalone: Bool, reverseMessageGalleryOrder: Bool, mode: ChatControllerInteractionOpenMessageMode, synchronousLoad: Bool, actionInteraction: GalleryControllerActionInteraction?) -> ChatMessageGalleryControllerData? { +private func chatMessageGalleryControllerData(context: AccountContext, chatLocation: ChatLocation?, chatLocationContextHolder: Atomic?, message: Message, navigationController: NavigationController?, standalone: Bool, reverseMessageGalleryOrder: Bool, mode: ChatControllerInteractionOpenMessageMode, synchronousLoad: Bool, actionInteraction: GalleryControllerActionInteraction?) -> ChatMessageGalleryControllerData? { var galleryMedia: Media? var otherMedia: Media? var instantPageMedia: (TelegramMediaWebpage, [InstantPageGalleryEntry])? @@ -149,7 +149,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message: return .audio(file) } else if ext == "json", let fileSize = file.size, fileSize < 1024 * 1024 { if let path = context.account.postbox.mediaBox.completedResourcePath(file.resource), let composition = LOTComposition(filePath: path), composition.timeDuration > 0.0 { - let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: chatLocation ?? ChatLocation.peer(message.id.peerId), chatLocationContextHolder: chatLocationContextHolder ?? Atomic(value: nil)), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in navigationController?.replaceTopController(controller, animated: false, ready: ready) }, baseNavigationController: navigationController, actionInteraction: actionInteraction) return .gallery(.single(gallery)) @@ -162,7 +162,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message: } if internalDocumentItemSupportsMimeType(file.mimeType, fileName: file.fileName ?? "file") { - let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: chatLocation ?? .peer(message.id.peerId), chatLocationContextHolder: chatLocationContextHolder ?? Atomic(value: nil)), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in navigationController?.replaceTopController(controller, animated: false, ready: ready) }, baseNavigationController: navigationController, actionInteraction: actionInteraction) return .gallery(.single(gallery)) @@ -190,7 +190,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message: return .gallery(startTimecode |> deliverOnMainQueue |> map { timecode in - let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in + let gallery = GalleryController(context: context, source: standalone ? .standaloneMessage(message) : .peerMessagesAtId(messageId: message.id, chatLocation: chatLocation ?? .peer(message.id.peerId), chatLocationContextHolder: chatLocationContextHolder ?? Atomic(value: nil)), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in navigationController?.replaceTopController(controller, animated: false, ready: ready) }, baseNavigationController: navigationController, actionInteraction: actionInteraction) gallery.temporaryDoNotWaitForReady = autoplayingVideo @@ -211,8 +211,8 @@ enum ChatMessagePreviewControllerData { case gallery(GalleryController) } -func chatMessagePreviewControllerData(context: AccountContext, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?) -> ChatMessagePreviewControllerData? { - if let mediaData = chatMessageGalleryControllerData(context: context, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: .default, synchronousLoad: true, actionInteraction: nil) { +func chatMessagePreviewControllerData(context: AccountContext, chatLocation: ChatLocation?, chatLocationContextHolder: Atomic?, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?) -> ChatMessagePreviewControllerData? { + if let mediaData = chatMessageGalleryControllerData(context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: .default, synchronousLoad: true, actionInteraction: nil) { switch mediaData { case let .gallery(gallery): break @@ -225,8 +225,8 @@ func chatMessagePreviewControllerData(context: AccountContext, message: Message, return nil } -func chatMediaListPreviewControllerData(context: AccountContext, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?) -> Signal { - if let mediaData = chatMessageGalleryControllerData(context: context, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: .default, synchronousLoad: true, actionInteraction: nil) { +func chatMediaListPreviewControllerData(context: AccountContext, chatLocation: ChatLocation?, chatLocationContextHolder: Atomic?, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?) -> Signal { + if let mediaData = chatMessageGalleryControllerData(context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: .default, synchronousLoad: true, actionInteraction: nil) { switch mediaData { case let .gallery(gallery): return gallery @@ -243,7 +243,7 @@ func chatMediaListPreviewControllerData(context: AccountContext, message: Messag } func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { - if let mediaData = chatMessageGalleryControllerData(context: params.context, message: params.message, navigationController: params.navigationController, standalone: params.standalone, reverseMessageGalleryOrder: params.reverseMessageGalleryOrder, mode: params.mode, synchronousLoad: false, actionInteraction: params.actionInteraction) { + if let mediaData = chatMessageGalleryControllerData(context: params.context, chatLocation: params.chatLocation, chatLocationContextHolder: params.chatLocationContextHolder, message: params.message, navigationController: params.navigationController, standalone: params.standalone, reverseMessageGalleryOrder: params.reverseMessageGalleryOrder, mode: params.mode, synchronousLoad: false, actionInteraction: params.actionInteraction) { switch mediaData { case let .url(url): params.openUrl(url) diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 22df999139..f03ef83375 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -89,6 +89,10 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur navigationController?.pushViewController(controller) case let .channelMessage(peerId, messageId): openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId), peekData: nil)) + case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxReadMessageId, messageId): + if let navigationController = navigationController { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .replyThread(threadMessageId: replyThreadMessageId, isChannelPost: isChannelPost, maxReadMessageId: maxReadMessageId), subject: .message(messageId))) + } case let .stickerPack(name): dismissInput() if false { diff --git a/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift index b927c3022e..bdd32429f4 100644 --- a/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayPlayerControllerNode.swift @@ -240,7 +240,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu openMessageImpl = { [weak self] id in if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.historyNode.messageInCurrentHistoryView(id) { - return strongSelf.context.sharedContext.openChatMessage(OpenChatMessageParams(context: strongSelf.context, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _ in }, transitionNode: { _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, enqueueMessage: { _ in }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in })) + return strongSelf.context.sharedContext.openChatMessage(OpenChatMessageParams(context: strongSelf.context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _ in }, transitionNode: { _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, enqueueMessage: { _ in }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in })) } return false } diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift index 173d501e93..f99b2fe66b 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift @@ -268,7 +268,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { } if let id = state.id as? PeerMessagesMediaPlaylistItemId { if type == .music { - let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60), id: 0), context: strongSelf.context, chatLocation: .peer(id.messageId.peerId), tagMask: MessageTags.music) + let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(location: .id(id.messageId), count: 60), id: 0), context: strongSelf.context, chatLocation: .peer(id.messageId.peerId), chatLocationContextHolder: Atomic(value: nil), tagMask: MessageTags.music) var cancelImpl: (() -> Void)? let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 37a4156cd6..2c10cf10fb 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -377,7 +377,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { }, navigateMessageSearch: { _ in }, openCalendarSearch: { }, toggleMembersSearch: { _ in - }, navigateToMessage: { _ in + }, navigateToMessage: { _, _ in }, navigateToChat: { _ in }, navigateToProfile: { _ in }, openPeerInfo: { @@ -1681,7 +1681,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD return } - let _ = (chatMediaListPreviewControllerData(context: strongSelf.context, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongSelf.controller?.navigationController as? NavigationController) + let _ = (chatMediaListPreviewControllerData(context: strongSelf.context, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil), message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongSelf.controller?.navigationController as? NavigationController) |> deliverOnMainQueue).start(next: { previewData in guard let strongSelf = self else { gesture?.cancel() @@ -2714,7 +2714,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } self.view.endEditing(true) - return self.context.sharedContext.openChatMessage(OpenChatMessageParams(context: self.context, message: galleryMessage, standalone: false, reverseMessageGalleryOrder: true, navigationController: navigationController, dismissInput: { [weak self] in + return self.context.sharedContext.openChatMessage(OpenChatMessageParams(context: self.context, chatLocation: nil, chatLocationContextHolder: nil, message: galleryMessage, standalone: false, reverseMessageGalleryOrder: true, navigationController: navigationController, dismissInput: { [weak self] in self?.view.endEditing(true) }, present: { [weak self] c, a in self?.controller?.present(c, in: .window(.root), with: a, blockInteraction: true) diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift index 9fd3dc2846..14d0be64c1 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift @@ -256,7 +256,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { - self.interfaceInteraction?.navigateToMessage(self.messageId) + self.interfaceInteraction?.navigateToMessage(self.messageId, false) } } } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 4e73e39020..4a3e1deaaa 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -915,8 +915,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { self.navigateToChatImpl(accountId, peerId, messageId) } - public func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError> { - let historyView = preloadedChatHistoryViewForLocation(location, context: context, chatLocation: chatLocation, fixedCombinedReadStates: nil, tagMask: tagMask, additionalData: []) + public func messageFromPreloadedChatHistoryViewForLocation(id: MessageId, location: ChatHistoryLocationInput, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, tagMask: MessageTags?) -> Signal<(MessageIndex?, Bool), NoError> { + let historyView = preloadedChatHistoryViewForLocation(location, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, fixedCombinedReadStates: nil, tagMask: tagMask, additionalData: []) return historyView |> mapToSignal { historyView -> Signal<(MessageIndex?, Bool), NoError> in switch historyView { diff --git a/submodules/TelegramUI/Sources/TextLinkHandling.swift b/submodules/TelegramUI/Sources/TextLinkHandling.swift index f25dd539ed..8a0c852220 100644 --- a/submodules/TelegramUI/Sources/TextLinkHandling.swift +++ b/submodules/TelegramUI/Sources/TextLinkHandling.swift @@ -58,6 +58,10 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate if let navigationController = controller.navigationController as? NavigationController { context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(messageId))) } + case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxReadMessageId, messageId): + if let navigationController = controller.navigationController as? NavigationController { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .replyThread(threadMessageId: replyThreadMessageId, isChannelPost: isChannelPost, maxReadMessageId: maxReadMessageId), subject: .message(messageId))) + } case let .stickerPack(name): let packReference: StickerPackReference = .name(name) controller.present(StickerPackScreen(context: context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: controller.navigationController as? NavigationController), in: .window(.root)) diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 743e955e10..7ef3e0ad6f 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -18,6 +18,7 @@ public enum ParsedInternalPeerUrlParameter { case botStart(String) case groupBotStart(String) case channelMessage(Int32) + case replyThread(Int32, Int32) } public enum ParsedInternalUrl { @@ -243,7 +244,24 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { return nil } } else if let value = Int(pathComponents[1]) { - return .peerName(peerName, .channelMessage(Int32(value))) + var commentId: Int32? + if let queryItems = components.queryItems { + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "comment" { + if let intValue = Int32(value) { + commentId = intValue + break + } + } + } + } + } + if let commentId = commentId { + return .peerName(peerName, .replyThread(Int32(value), commentId)) + } else { + return .peerName(peerName, .channelMessage(Int32(value))) + } } else { return nil } @@ -269,26 +287,35 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } } } - |> map { peer -> ResolvedUrl? in + |> mapToSignal { peer -> Signal in if let peer = peer { if let parameter = parameter { switch parameter { case let .botStart(payload): - return .botStart(peerId: peer.id, payload: payload) + return .single(.botStart(peerId: peer.id, payload: payload)) case let .groupBotStart(payload): - return .groupBotStart(peerId: peer.id, payload: payload) + return .single(.groupBotStart(peerId: peer.id, payload: payload)) case let .channelMessage(id): - return .channelMessage(peerId: peer.id, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id)) + return .single(.channelMessage(peerId: peer.id, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id))) + case let .replyThread(id, replyId): + let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id) + return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId) + |> map { result -> ResolvedUrl? in + guard let result = result else { + return .channelMessage(peerId: peer.id, messageId: replyThreadMessageId) + } + return .replyThreadMessage(replyThreadMessageId: result.messageId, isChannelPost: true, maxReadMessageId: result.maxReadMessageId, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId)) + } } } else { if let peer = peer as? TelegramUser, peer.botInfo == nil { - return .peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)) + return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil))) } else { - return .peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)) + return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil))) } } } else { - return .peer(nil, .info) + return .single(.peer(nil, .info)) } } case let .peerId(peerId):