From 0ad61d8d631b7d356673b6c4e3c407d8ef587eca Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 23 Dec 2023 00:47:22 +0400 Subject: [PATCH] [WIP] Saved messages --- .../Sources/AccountContext.swift | 4 +- .../AccountContext/Sources/MediaManager.swift | 2 +- .../Sources/ChatListController.swift | 4 +- .../Sources/ChatListSearchListPaneNode.swift | 6 +- .../Sources/ChatListShimmerNode.swift | 6 +- .../Sources/Node/ChatListNodeLocation.swift | 202 ++++-- .../ChatPresentationInterfaceState.swift | 4 +- submodules/Postbox/Sources/Message.swift | 16 +- ...MessageHistorySavedMessagesIndexView.swift | 189 +++++ .../Sources/MessageThreadIndexTable.swift | 11 +- submodules/Postbox/Sources/Postbox.swift | 2 +- .../Postbox/Sources/SeedConfiguration.swift | 5 +- submodules/Postbox/Sources/ViewTracker.swift | 4 + submodules/Postbox/Sources/Views.swift | 11 + .../Sources/SearchPeerMembers.swift | 2 +- submodules/TelegramApi/Sources/Api0.swift | 14 +- submodules/TelegramApi/Sources/Api12.swift | 114 ++-- submodules/TelegramApi/Sources/Api19.swift | 46 ++ submodules/TelegramApi/Sources/Api22.swift | 56 ++ submodules/TelegramApi/Sources/Api28.swift | 142 +++- submodules/TelegramApi/Sources/Api29.swift | 140 ++-- submodules/TelegramApi/Sources/Api30.swift | 220 +++--- submodules/TelegramApi/Sources/Api31.swift | 138 ++++ submodules/TelegramApi/Sources/Api32.swift | 123 +++- .../ApiUtils/StoreMessage_Telegram.swift | 32 +- .../TelegramCore/Sources/ForumChannels.swift | 355 ++++++++-- .../PendingMessages/EnqueueMessage.swift | 10 +- .../StandaloneSendMessage.swift | 2 +- .../State/AccountStateManagementUtils.swift | 19 +- .../Sources/State/AccountStateManager.swift | 4 +- .../Sources/State/ApplyUpdateMessage.swift | 5 +- .../State/HistoryViewStateValidation.swift | 8 +- .../TelegramCore/Sources/State/Holes.swift | 644 ++++++++++-------- .../State/ManagedLocalInputActivities.swift | 4 +- .../Sources/State/Serialization.swift | 2 +- .../Sources/State/UpdateMessageService.swift | 4 +- .../Sources/State/UpdatesApiUtils.swift | 12 +- .../SyncCore_ReplyMessageAttribute.swift | 9 - ...yncCore_StandaloneAccountTransaction.swift | 7 + .../Messages/DeleteMessages.swift | 8 +- .../Messages/ReplyThreadHistory.swift | 106 ++- .../Messages/SearchMessages.swift | 101 ++- .../Messages/SparseMessageList.swift | 8 +- .../Messages/TelegramEngineMessages.swift | 24 +- .../Peers/RequestUserPhotos.swift | 2 +- .../Sources/Utils/UpdateMessageMedia.swift | 12 +- .../ChatChannelSubscriberInputPanelNode.swift | 2 +- .../ChatHistorySearchContainerNode.swift | 2 +- .../ChatMessageAnimatedStickerItemNode.swift | 8 +- .../Sources/ChatMessageBubbleItemNode.swift | 6 +- .../ChatMessageInstantVideoItemNode.swift | 4 +- ...atMessageInteractiveInstantVideoNode.swift | 4 +- .../Sources/ChatMessageItemCommon.swift | 2 +- .../Sources/ChatMessageStickerItemNode.swift | 8 +- .../Sources/PeerInfoChatListPaneNode.swift | 61 +- .../PeerInfoScreen/Sources/PeerInfoData.swift | 4 +- .../Sources/PeerInfoHeaderNode.swift | 4 +- .../Sources/PeerInfoScreen.swift | 4 +- .../Sources/StorageUsageScreen.swift | 4 +- .../TelegramUI/Sources/AccountContext.swift | 18 +- .../Sources/ApplicationContext.swift | 4 +- .../ChatControllerNavigateToMessage.swift | 10 +- .../TelegramUI/Sources/ChatController.swift | 75 +- .../Sources/ChatHistoryListNode.swift | 12 +- .../ChatInterfaceStateContextMenus.swift | 8 +- .../ChatInterfaceStateInputPanels.swift | 2 +- .../ChatInterfaceStateNavigationButtons.swift | 2 +- .../Sources/NavigateToChatController.swift | 2 +- .../TelegramUI/Sources/OpenResolvedUrl.swift | 4 +- .../TelegramUI/Sources/TextLinkHandling.swift | 4 +- .../UrlHandling/Sources/UrlHandling.swift | 10 +- 71 files changed, 2079 insertions(+), 1024 deletions(-) create mode 100644 submodules/Postbox/Sources/MessageHistorySavedMessagesIndexView.swift diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 0d2b212a0d..9b920f112c 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -423,7 +423,7 @@ public final class NavigateToChatControllerParams { case let .peer(peer): return peer.id case let .replyThread(message): - return message.messageId.peerId + return message.peerId } } @@ -432,7 +432,7 @@ public final class NavigateToChatControllerParams { case .peer: return nil case let .replyThread(message): - return Int64(message.messageId.id) + return message.threadId } } diff --git a/submodules/AccountContext/Sources/MediaManager.swift b/submodules/AccountContext/Sources/MediaManager.swift index 3281f18dee..6ca9d90f50 100644 --- a/submodules/AccountContext/Sources/MediaManager.swift +++ b/submodules/AccountContext/Sources/MediaManager.swift @@ -35,7 +35,7 @@ public enum PeerMessagesPlaylistLocation: Equatable, SharedMediaPlaylistLocation case let .peer(peerId): return .peer(peerId) case let .replyThread(replyThreaMessage): - return .peer(replyThreaMessage.messageId.peerId) + return .peer(replyThreaMessage.peerId) case let .feed(id): return .feed(id) } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 28071a2012..240e16e4fc 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1345,7 +1345,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let threadId = threadId { let source: ContextContentSource let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage( - messageId: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) @@ -1381,7 +1381,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } let source: ContextContentSource let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage( - messageId: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )), subject: nil, botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index ebcb16827f..19e8bf09e5 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -1539,9 +1539,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let searchLocations: [SearchMessagesLocation] if let options = options { if case let .forum(peerId) = location { - searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, topMsgId: nil, minDate: options.date?.0, maxDate: options.date?.1), .general(tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] + searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1), .general(tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] } else if let (peerId, _, _) = options.peer { - searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, topMsgId: nil, minDate: options.date?.0, maxDate: options.date?.1)] + searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1)] } else { if case let .chatList(groupId) = location, case .archive = groupId { searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)] @@ -1551,7 +1551,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } else { if case let .forum(peerId) = location { - searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, topMsgId: nil, minDate: nil, maxDate: nil), .general(tags: tagMask, minDate: nil, maxDate: nil)] + searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, threadId: nil, minDate: nil, maxDate: nil), .general(tags: tagMask, minDate: nil, maxDate: nil)] } else if case let .chatList(groupId) = location, case .archive = groupId { searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: nil, maxDate: nil)] } else { diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index d07e4174d9..5bcc75170c 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -124,13 +124,13 @@ final class ShimmerEffectNode: ASDisplayNode { } } -final class ChatListShimmerNode: ASDisplayNode { +public final class ChatListShimmerNode: ASDisplayNode { private let backgroundColorNode: ASDisplayNode private let effectNode: ShimmerEffectNode private let maskNode: ASImageNode private var currentParams: (size: CGSize, presentationData: PresentationData)? - override init() { + override public init() { self.backgroundColorNode = ASDisplayNode() self.effectNode = ShimmerEffectNode() self.maskNode = ASImageNode() @@ -144,7 +144,7 @@ final class ChatListShimmerNode: ASDisplayNode { self.addSubnode(self.maskNode) } - func update(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, size: CGSize, isInlineMode: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { + public func update(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, size: CGSize, isInlineMode: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { if self.currentParams?.size != size || self.currentParams?.presentationData !== presentationData { self.currentParams = (size, presentationData) diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift index 18353e76f1..a5c144fe88 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift @@ -312,65 +312,35 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil) } case .savedMessagesChats: - var isFirst = true - - return account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 1000, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Set([Namespaces.Message.ScheduledCloud, Namespaces.Message.ScheduledLocal])), orderStatistics: []) - |> map { view, _, _ -> ChatListNodeViewUpdate in - let isLoading = view.isLoading + if "".isEmpty { + let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: account.peerId) - var items: [EngineChatList.Item] = [] - - var topMessageByPeerId: [EnginePeer.Id: Message] = [:] - if !isLoading { - for entry in view.entries { - guard let threadId = entry.message.threadId else { - continue - } - let sourcePeerId = PeerId(threadId) - - if let currentTopMessage = topMessageByPeerId[sourcePeerId] { - if currentTopMessage.index < entry.index { - topMessageByPeerId[sourcePeerId] = entry.message - } - } else { - topMessageByPeerId[sourcePeerId] = entry.message - } + var isFirst = true + return account.postbox.combinedView(keys: [viewKey]) + |> map { views -> ChatListNodeViewUpdate in + guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else { + preconditionFailure() } - for (_, message) in topMessageByPeerId.sorted(by: { $0.value.index > $1.value.index }) { - guard let threadId = message.threadId else { + + var items: [EngineChatList.Item] = [] + for item in view.items { + guard let sourcePeer = item.peer else { continue } - let sourceId = PeerId(threadId) - var sourcePeer = message.peers[sourceId] - if sourcePeer == nil, let forwardInfo = message.forwardInfo, let authorSignature = forwardInfo.authorSignature { - sourcePeer = TelegramUser( - id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(1)), - accessHash: nil, - firstName: authorSignature, - lastName: nil, - username: nil, - phone: nil, - photo: [], - botInfo: nil, - restrictionInfo: nil, - flags: [], - emojiStatus: nil, - usernames: [], - storiesHidden: nil, - nameColor: nil, - backgroundEmojiId: nil, - profileColor: nil, - profileBackgroundEmojiId: nil - ) + + let sourceId = PeerId(item.id) + + var messages: [EngineMessage] = [] + if let topMessage = item.topMessage { + messages.append(EngineMessage(topMessage)) } - guard let sourcePeer else { - continue - } - let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: message.index.id.namespace, id: message.index.id.id), timestamp: message.index.timestamp) + + let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp) + items.append(EngineChatList.Item( id: .chatList(sourceId), - index: .chatList(ChatListIndex(pinningIndex: nil, messageIndex: mappedMessageIndex)), - messages: [EngineMessage(message)], + index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)), + messages: messages, readCounters: nil, isMuted: false, draft: nil, @@ -387,25 +357,121 @@ func chatListViewForLocation(chatListLocation: ChatListControllerLocation, locat storyStats: nil )) } + + let list = EngineChatList( + items: items.reversed(), + groupItems: [], + additionalItems: [], + hasEarlier: false, + hasLater: false, + isLoading: view.isLoading + ) + + let type: ViewUpdateType + if isFirst { + type = .Initial + } else { + type = .Generic + } + isFirst = false + return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil) } + } else { + var isFirst = true - let list = EngineChatList( - items: items.reversed(), - groupItems: [], - additionalItems: [], - hasEarlier: false, - hasLater: false, - isLoading: isLoading - ) - - let type: ViewUpdateType - if isFirst { - type = .Initial - } else { - type = .Generic + return account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 1000, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Set([Namespaces.Message.ScheduledCloud, Namespaces.Message.ScheduledLocal])), orderStatistics: []) + |> map { view, _, _ -> ChatListNodeViewUpdate in + let isLoading = view.isLoading + + var items: [EngineChatList.Item] = [] + + var topMessageByPeerId: [EnginePeer.Id: Message] = [:] + if !isLoading { + for entry in view.entries { + guard let threadId = entry.message.threadId else { + continue + } + let sourcePeerId = PeerId(threadId) + + if let currentTopMessage = topMessageByPeerId[sourcePeerId] { + if currentTopMessage.index < entry.index { + topMessageByPeerId[sourcePeerId] = entry.message + } + } else { + topMessageByPeerId[sourcePeerId] = entry.message + } + } + for (_, message) in topMessageByPeerId.sorted(by: { $0.value.index > $1.value.index }) { + guard let threadId = message.threadId else { + continue + } + let sourceId = PeerId(threadId) + var sourcePeer = message.peers[sourceId] + if sourcePeer == nil, let forwardInfo = message.forwardInfo, let authorSignature = forwardInfo.authorSignature { + sourcePeer = TelegramUser( + id: PeerId(namespace: Namespaces.Peer.Empty, id: PeerId.Id._internalFromInt64Value(1)), + accessHash: nil, + firstName: authorSignature, + lastName: nil, + username: nil, + phone: nil, + photo: [], + botInfo: nil, + restrictionInfo: nil, + flags: [], + emojiStatus: nil, + usernames: [], + storiesHidden: nil, + nameColor: nil, + backgroundEmojiId: nil, + profileColor: nil, + profileBackgroundEmojiId: nil + ) + } + guard let sourcePeer else { + continue + } + let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: message.index.id.namespace, id: message.index.id.id), timestamp: message.index.timestamp) + items.append(EngineChatList.Item( + id: .chatList(sourceId), + index: .chatList(ChatListIndex(pinningIndex: nil, messageIndex: mappedMessageIndex)), + messages: [EngineMessage(message)], + readCounters: nil, + isMuted: false, + draft: nil, + threadData: nil, + renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)), + presence: nil, + hasUnseenMentions: false, + hasUnseenReactions: false, + forumTopicData: nil, + topForumTopicItems: [], + hasFailed: false, + isContact: false, + autoremoveTimeout: nil, + storyStats: nil + )) + } + } + + let list = EngineChatList( + items: items.reversed(), + groupItems: [], + additionalItems: [], + hasEarlier: false, + hasLater: false, + isLoading: isLoading + ) + + let type: ViewUpdateType + if isFirst { + type = .Initial + } else { + type = .Generic + } + isFirst = false + return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil) } - isFirst = false - return ChatListNodeViewUpdate(list: list, type: type, scrollPosition: nil) } } } diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift index a37e42eff8..2809ca5db2 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift @@ -14,7 +14,7 @@ public extension ChatLocation { case let .peer(peerId): return peerId case let .replyThread(replyThreadMessage): - return replyThreadMessage.messageId.peerId + return replyThreadMessage.peerId case .feed: return nil } @@ -25,7 +25,7 @@ public extension ChatLocation { case .peer: return nil case let .replyThread(replyThreadMessage): - return Int64(replyThreadMessage.messageId.id) + return replyThreadMessage.threadId case .feed: return nil } diff --git a/submodules/Postbox/Sources/Message.swift b/submodules/Postbox/Sources/Message.swift index a1300a3ec5..af43f4f21b 100644 --- a/submodules/Postbox/Sources/Message.swift +++ b/submodules/Postbox/Sources/Message.swift @@ -869,14 +869,14 @@ public enum StoreMessageId { } } -public func makeMessageThreadId(_ messageId: MessageId) -> Int64 { - return (Int64(messageId.namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: messageId.id))) -} - -public func makeThreadIdMessageId(peerId: PeerId, threadId: Int64) -> MessageId { - let namespace = Int32((threadId >> 32) & 0x7fffffff) - let id = Int32(bitPattern: UInt32(threadId & 0xffffffff)) - return MessageId(peerId: peerId, namespace: namespace, id: id) +public struct MessageThreadKey: Hashable { + public var peerId: PeerId + public var threadId: Int64 + + public init(peerId: PeerId, threadId: Int64) { + self.peerId = peerId + self.threadId = threadId + } } public final class StoreMessage { diff --git a/submodules/Postbox/Sources/MessageHistorySavedMessagesIndexView.swift b/submodules/Postbox/Sources/MessageHistorySavedMessagesIndexView.swift new file mode 100644 index 0000000000..bc96d4c7e9 --- /dev/null +++ b/submodules/Postbox/Sources/MessageHistorySavedMessagesIndexView.swift @@ -0,0 +1,189 @@ +import Foundation + +final class MutableMessageHistorySavedMessagesIndexView: MutablePostboxView { + final class Item { + let id: Int64 + let peer: Peer? + let pinnedIndex: Int? + let index: MessageIndex + let topMessage: Message? + + init( + id: Int64, + peer: Peer?, + pinnedIndex: Int?, + index: MessageIndex, + topMessage: Message? + ) { + self.id = id + self.peer = peer + self.pinnedIndex = pinnedIndex + self.index = index + self.topMessage = topMessage + } + } + + fileprivate let peerId: PeerId + fileprivate var peer: Peer? + fileprivate var items: [Item] = [] + private var hole: ForumTopicListHolesEntry? + fileprivate var isLoading: Bool = false + + init(postbox: PostboxImpl, peerId: PeerId) { + self.peerId = peerId + + self.reload(postbox: postbox) + } + + private func reload(postbox: PostboxImpl) { + self.items.removeAll() + + self.peer = postbox.peerTable.get(self.peerId) + + let validIndexBoundary = postbox.peerThreadCombinedStateTable.get(peerId: peerId)?.validIndexBoundary + self.isLoading = validIndexBoundary == nil + + if let validIndexBoundary = validIndexBoundary { + if validIndexBoundary.messageId != 1 { + self.hole = ForumTopicListHolesEntry(peerId: self.peerId, index: validIndexBoundary) + } else { + self.hole = nil + } + } else { + self.hole = ForumTopicListHolesEntry(peerId: self.peerId, index: nil) + } + + if !self.isLoading { + let pinnedThreadIds = postbox.messageHistoryThreadPinnedTable.get(peerId: self.peerId) + + for item in postbox.messageHistoryThreadIndexTable.getAll(peerId: self.peerId) { + var pinnedIndex: Int? + if let index = pinnedThreadIds.firstIndex(of: item.threadId) { + pinnedIndex = index + } + + self.items.append(Item( + id: item.threadId, + peer: postbox.peerTable.get(PeerId(item.threadId)), + pinnedIndex: pinnedIndex, + index: item.index, + topMessage: postbox.getMessage(item.index.id) + )) + } + + self.items.sort(by: { lhs, rhs in + if let lhsPinnedIndex = lhs.pinnedIndex, let rhsPinnedIndex = rhs.pinnedIndex { + return lhsPinnedIndex < rhsPinnedIndex + } else if (lhs.pinnedIndex == nil) != (rhs.pinnedIndex == nil) { + if lhs.pinnedIndex != nil { + return true + } else { + return false + } + } + + return lhs.index > rhs.index + }) + } + } + + func replay(postbox: PostboxImpl, transaction: PostboxTransaction) -> Bool { + var updated = false + + if transaction.updatedMessageThreadPeerIds.contains(self.peerId) || transaction.updatedPinnedThreads.contains(self.peerId) || transaction.updatedPeerThreadCombinedStates.contains(self.peerId) || transaction.currentUpdatedMessageTagSummaries.contains(where: { $0.key.peerId == self.peerId }) || transaction.currentUpdatedMessageActionsSummaries.contains(where: { $0.key.peerId == self.peerId }) || transaction.currentUpdatedPeerChatListEmbeddedStates.contains(self.peerId) || transaction.currentUpdatedPeerNotificationSettings[self.peerId] != nil || transaction.updatedPinnedThreads.contains(self.peerId) { + self.reload(postbox: postbox) + updated = true + } + + return updated + } + + func topHole() -> ForumTopicListHolesEntry? { + return self.hole + } + + func refreshDueToExternalTransaction(postbox: PostboxImpl) -> Bool { + self.reload(postbox: postbox) + + return true + } + + func immutableView() -> PostboxView { + return MessageHistorySavedMessagesIndexView(self) + } +} + +public final class EngineMessageHistorySavedMessagesThread { + public final class Item: Equatable { + public let id: Int64 + public let peer: Peer? + public let pinnedIndex: Int? + public let index: MessageIndex + public let topMessage: Message? + + public init( + id: Int64, + peer: Peer?, + pinnedIndex: Int?, + index: MessageIndex, + topMessage: Message? + ) { + self.id = id + self.peer = peer + self.pinnedIndex = pinnedIndex + self.index = index + self.topMessage = topMessage + } + + public static func ==(lhs: Item, rhs: Item) -> Bool { + if lhs.id != rhs.id { + return false + } + if !arePeersEqual(lhs.peer, rhs.peer) { + return false + } + if lhs.pinnedIndex != rhs.pinnedIndex { + return false + } + if lhs.index != rhs.index { + return false + } + if let lhsMessage = lhs.topMessage, let rhsMessage = rhs.topMessage { + if lhsMessage.index != rhsMessage.index { + return false + } + if lhsMessage.stableVersion != rhsMessage.stableVersion { + return false + } + } else if (lhs.topMessage == nil) != (rhs.topMessage == nil) { + return false + } + + return true + } + } +} + +public final class MessageHistorySavedMessagesIndexView: PostboxView { + public let peer: Peer? + public let items: [EngineMessageHistorySavedMessagesThread.Item] + public let isLoading: Bool + + init(_ view: MutableMessageHistorySavedMessagesIndexView) { + self.peer = view.peer + + var items: [EngineMessageHistorySavedMessagesThread.Item] = [] + for item in view.items { + items.append(EngineMessageHistorySavedMessagesThread.Item( + id: item.id, + peer: item.peer, + pinnedIndex: item.pinnedIndex, + index: item.index, + topMessage: item.topMessage + )) + } + self.items = items + + self.isLoading = view.isLoading + } +} diff --git a/submodules/Postbox/Sources/MessageThreadIndexTable.swift b/submodules/Postbox/Sources/MessageThreadIndexTable.swift index e0243c980c..3f9dcc5e91 100644 --- a/submodules/Postbox/Sources/MessageThreadIndexTable.swift +++ b/submodules/Postbox/Sources/MessageThreadIndexTable.swift @@ -119,13 +119,15 @@ class MessageHistoryThreadIndexTable: Table { } private let reverseIndexTable: MessageHistoryThreadReverseIndexTable + private let seedConfiguration: SeedConfiguration private let sharedKey = ValueBoxKey(length: 8 + 4 + 8 + 4 + 4) private var updatedInfoItems: [MessageHistoryThreadsTable.ItemId: UpdatedEntry] = [:] - init(valueBox: ValueBox, table: ValueBoxTable, reverseIndexTable: MessageHistoryThreadReverseIndexTable, useCaches: Bool) { + init(valueBox: ValueBox, table: ValueBoxTable, reverseIndexTable: MessageHistoryThreadReverseIndexTable, seedConfiguration: SeedConfiguration, useCaches: Bool) { self.reverseIndexTable = reverseIndexTable + self.seedConfiguration = seedConfiguration super.init(valueBox: valueBox, table: table, useCaches: useCaches) } @@ -219,6 +221,13 @@ class MessageHistoryThreadIndexTable: Table { info = nil } } + if info == nil { + if let value = self.seedConfiguration.automaticThreadIndexInfo(itemId.peerId, itemId.threadId) { + let encoder = PostboxEncoder() + value.encode(encoder) + info = encoder.makeReadBufferAndReset() + } + } if let topIndex = topIndex, let info = info { if let previousIndex = previousIndex { diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 105bdbd22b..8e078ad6f0 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1746,7 +1746,7 @@ final class PostboxImpl { self.messageHistoryThreadTagsTable = MessageHistoryThreadTagsTable(valueBox: self.valueBox, table: MessageHistoryThreadTagsTable.tableSpec(71), useCaches: useCaches, seedConfiguration: self.seedConfiguration, summaryTable: self.messageHistoryTagsSummaryTable) self.messageHistoryThreadHoleIndexTable = MessageHistoryThreadHoleIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadHoleIndexTable.tableSpec(63), useCaches: useCaches, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration) self.messageHistoryThreadReverseIndexTable = MessageHistoryThreadReverseIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadReverseIndexTable.tableSpec(72), useCaches: useCaches) - self.messageHistoryThreadIndexTable = MessageHistoryThreadIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadIndexTable.tableSpec(73), reverseIndexTable: self.messageHistoryThreadReverseIndexTable, useCaches: useCaches) + self.messageHistoryThreadIndexTable = MessageHistoryThreadIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadIndexTable.tableSpec(73), reverseIndexTable: self.messageHistoryThreadReverseIndexTable, seedConfiguration: seedConfiguration, useCaches: useCaches) self.messageHistoryThreadPinnedTable = MessageHistoryThreadPinnedTable(valueBox: self.valueBox, table: MessageHistoryThreadPinnedTable.tableSpec(76), useCaches: useCaches) self.globalMessageHistoryTagsTable = GlobalMessageHistoryTagsTable(valueBox: self.valueBox, table: GlobalMessageHistoryTagsTable.tableSpec(39), useCaches: useCaches) self.localMessageHistoryTagsTable = LocalMessageHistoryTagsTable(valueBox: self.valueBox, table: GlobalMessageHistoryTagsTable.tableSpec(52), useCaches: useCaches) diff --git a/submodules/Postbox/Sources/SeedConfiguration.swift b/submodules/Postbox/Sources/SeedConfiguration.swift index 9b9912281d..1aff6733b7 100644 --- a/submodules/Postbox/Sources/SeedConfiguration.swift +++ b/submodules/Postbox/Sources/SeedConfiguration.swift @@ -78,6 +78,7 @@ public final class SeedConfiguration { public let decodeAutoremoveTimeout: (CachedPeerData) -> Int32? public let decodeDisplayPeerAsRegularChat: (CachedPeerData) -> Bool public let isPeerUpgradeMessage: (Message) -> Bool + public let automaticThreadIndexInfo: (PeerId, Int64) -> StoredMessageHistoryThreadInfo? public init( globalMessageIdsPeerIdNamespaces: Set, @@ -105,7 +106,8 @@ public final class SeedConfiguration { decodeMessageThreadInfo: @escaping (CodableEntry) -> Message.AssociatedThreadInfo?, decodeAutoremoveTimeout: @escaping (CachedPeerData) -> Int32?, decodeDisplayPeerAsRegularChat: @escaping (CachedPeerData) -> Bool, - isPeerUpgradeMessage: @escaping (Message) -> Bool + isPeerUpgradeMessage: @escaping (Message) -> Bool, + automaticThreadIndexInfo: @escaping (PeerId, Int64) -> StoredMessageHistoryThreadInfo? ) { self.globalMessageIdsPeerIdNamespaces = globalMessageIdsPeerIdNamespaces self.initializeChatListWithHole = initializeChatListWithHole @@ -129,5 +131,6 @@ public final class SeedConfiguration { self.decodeAutoremoveTimeout = decodeAutoremoveTimeout self.decodeDisplayPeerAsRegularChat = decodeDisplayPeerAsRegularChat self.isPeerUpgradeMessage = isPeerUpgradeMessage + self.automaticThreadIndexInfo = automaticThreadIndexInfo } } diff --git a/submodules/Postbox/Sources/ViewTracker.swift b/submodules/Postbox/Sources/ViewTracker.swift index d724c7bea9..fde70cefe6 100644 --- a/submodules/Postbox/Sources/ViewTracker.swift +++ b/submodules/Postbox/Sources/ViewTracker.swift @@ -453,6 +453,10 @@ final class ViewTracker { if let hole = view.topHole() { firstHoles.insert(hole) } + } else if case .savedMessagesIndex = key, let view = view as? MutableMessageHistorySavedMessagesIndexView { + if let hole = view.topHole() { + firstHoles.insert(hole) + } } } } diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index 779ce1c5e5..f90a0a7d9b 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -46,6 +46,7 @@ public enum PostboxViewKey: Hashable { case storyExpirationTimeItems case peerStoryStats(peerIds: Set) case story(id: StoryId) + case savedMessagesIndex(peerId: PeerId) public func hash(into hasher: inout Hasher) { switch self { @@ -153,6 +154,8 @@ public enum PostboxViewKey: Hashable { hasher.combine(peerIds) case let .story(id): hasher.combine(id) + case let .savedMessagesIndex(peerId): + hasher.combine(peerId) } } @@ -428,6 +431,12 @@ public enum PostboxViewKey: Hashable { } else { return false } + case let .savedMessagesIndex(peerId): + if case .savedMessagesIndex(peerId) = rhs { + return true + } else { + return false + } } } } @@ -524,5 +533,7 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost return MutablePeerStoryStatsView(postbox: postbox, peerIds: peerIds) case let .story(id): return MutableStoryView(postbox: postbox, id: id) + case let .savedMessagesIndex(peerId): + return MutableMessageHistorySavedMessagesIndexView(postbox: postbox, peerId: peerId) } } diff --git a/submodules/SearchPeerMembers/Sources/SearchPeerMembers.swift b/submodules/SearchPeerMembers/Sources/SearchPeerMembers.swift index 25645782d0..8cef0161a8 100644 --- a/submodules/SearchPeerMembers/Sources/SearchPeerMembers.swift +++ b/submodules/SearchPeerMembers/Sources/SearchPeerMembers.swift @@ -67,7 +67,7 @@ public func searchPeerMembers(context: AccountContext, peerId: EnginePeer.Id, ch disposable.dispose() } case let .replyThread(replyThreadMessage): - let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.mentions(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, threadMessageId: replyThreadMessage.messageId, searchQuery: normalizedQuery.isEmpty ? nil : normalizedQuery, updated: { state in + let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.mentions(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, threadMessageId: EngineMessage.Id(peerId: replyThreadMessage.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: replyThreadMessage.threadId)), searchQuery: normalizedQuery.isEmpty ? nil : normalizedQuery, updated: { state in if case .ready = state.loadingState { subscriber.putNext((state.list.compactMap { participant in if participant.peer.isDeleted { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index d3776d015d..44034adce3 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -472,7 +472,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) } dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) } dict[64088654] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) } - dict[940666592] = { return Api.Message.parse_message($0) } + dict[1992213009] = { return Api.Message.parse_message($0) } dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) } dict[721967202] = { return Api.Message.parse_messageService($0) } dict[-988359047] = { return Api.MessageAction.parse_messageActionBotAllowed($0) } @@ -748,6 +748,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1054465340] = { return Api.RichText.parse_textUnderline($0) } dict[1009288385] = { return Api.RichText.parse_textUrl($0) } dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) } + dict[-1115174036] = { return Api.SavedDialog.parse_savedDialog($0) } dict[-911191137] = { return Api.SearchResultsCalendarPeriod.parse_searchResultsCalendarPeriod($0) } dict[2137295719] = { return Api.SearchResultsPosition.parse_searchResultPosition($0) } dict[871426631] = { return Api.SecureCredentialsEncrypted.parse_secureCredentialsEncrypted($0) } @@ -939,6 +940,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1538885128] = { return Api.Update.parse_updatePinnedChannelMessages($0) } dict[-99664734] = { return Api.Update.parse_updatePinnedDialogs($0) } dict[-309990731] = { return Api.Update.parse_updatePinnedMessages($0) } + dict[1751942566] = { return Api.Update.parse_updatePinnedSavedDialogs($0) } dict[-298113238] = { return Api.Update.parse_updatePrivacy($0) } dict[861169551] = { return Api.Update.parse_updatePtsChanged($0) } dict[-693004986] = { return Api.Update.parse_updateReadChannelDiscussionInbox($0) } @@ -954,6 +956,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[821314523] = { return Api.Update.parse_updateRecentEmojiStatuses($0) } dict[1870160884] = { return Api.Update.parse_updateRecentReactions($0) } dict[-1706939360] = { return Api.Update.parse_updateRecentStickers($0) } + dict[-1364222348] = { return Api.Update.parse_updateSavedDialogPinned($0) } dict[-1821035490] = { return Api.Update.parse_updateSavedGifs($0) } dict[1960361625] = { return Api.Update.parse_updateSavedRingtones($0) } dict[2103604867] = { return Api.Update.parse_updateSentStoryReaction($0) } @@ -1166,6 +1169,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1334846497] = { return Api.messages.Reactions.parse_reactionsNotModified($0) } dict[-1999405994] = { return Api.messages.RecentStickers.parse_recentStickers($0) } dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) } + dict[-130358751] = { return Api.messages.SavedDialogs.parse_savedDialogs($0) } + dict[-1071681560] = { return Api.messages.SavedDialogs.parse_savedDialogsNotModified($0) } + dict[1153080793] = { return Api.messages.SavedDialogs.parse_savedDialogsSlice($0) } dict[-2069878259] = { return Api.messages.SavedGifs.parse_savedGifs($0) } dict[-402498398] = { return Api.messages.SavedGifs.parse_savedGifsNotModified($0) } dict[-398136321] = { return Api.messages.SearchCounter.parse_searchCounter($0) } @@ -1263,7 +1269,7 @@ public extension Api { return parser(reader) } else { - telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") + telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") return nil } } @@ -1745,6 +1751,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.SavedContact: _1.serialize(buffer, boxed) + case let _1 as Api.SavedDialog: + _1.serialize(buffer, boxed) case let _1 as Api.SearchResultsCalendarPeriod: _1.serialize(buffer, boxed) case let _1 as Api.SearchResultsPosition: @@ -2061,6 +2069,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.RecentStickers: _1.serialize(buffer, boxed) + case let _1 as Api.messages.SavedDialogs: + _1.serialize(buffer, boxed) case let _1 as Api.messages.SavedGifs: _1.serialize(buffer, boxed) case let _1 as Api.messages.SearchCounter: diff --git a/submodules/TelegramApi/Sources/Api12.swift b/submodules/TelegramApi/Sources/Api12.swift index d5155f12fb..c1740387cb 100644 --- a/submodules/TelegramApi/Sources/Api12.swift +++ b/submodules/TelegramApi/Sources/Api12.swift @@ -424,20 +424,21 @@ public extension Api { } public extension Api { indirect enum Message: TypeConstructorDescription { - case message(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, replyTo: Api.MessageReplyHeader?, 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?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?) + case message(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, replyTo: Api.MessageReplyHeader?, 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?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?) case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?) case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, ttlPeriod: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .message(let flags, let id, let fromId, let peerId, let fwdFrom, let viaBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod): + case .message(let flags, let id, let fromId, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod): if boxed { - buffer.appendInt32(940666592) + buffer.appendInt32(1992213009) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 8) != 0 {fromId!.serialize(buffer, true)} peerId.serialize(buffer, true) + if Int(flags) & Int(1 << 28) != 0 {savedPeerId!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)} if Int(flags) & Int(1 << 11) != 0 {serializeInt64(viaBotId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)} @@ -490,8 +491,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .message(let flags, let id, let fromId, let peerId, let fwdFrom, let viaBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod): - return ("message", [("flags", flags as Any), ("id", id as Any), ("fromId", fromId as Any), ("peerId", peerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any)]) + case .message(let flags, let id, let fromId, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod): + return ("message", [("flags", flags as Any), ("id", id as Any), ("fromId", fromId as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any)]) case .messageEmpty(let flags, let id, let peerId): return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)]) case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let ttlPeriod): @@ -512,79 +513,84 @@ public extension Api { if let signature = reader.readInt32() { _4 = Api.parse(reader, signature: signature) as? Api.Peer } - var _5: Api.MessageFwdHeader? + var _5: Api.Peer? + if Int(_1!) & Int(1 << 28) != 0 {if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.Peer + } } + var _6: Api.MessageFwdHeader? if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.MessageFwdHeader + _6 = Api.parse(reader, signature: signature) as? Api.MessageFwdHeader } } - var _6: Int64? - if Int(_1!) & Int(1 << 11) != 0 {_6 = reader.readInt64() } - var _7: Api.MessageReplyHeader? + var _7: Int64? + if Int(_1!) & Int(1 << 11) != 0 {_7 = reader.readInt64() } + var _8: Api.MessageReplyHeader? if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader + _8 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader } } - var _8: Int32? - _8 = reader.readInt32() - var _9: String? - _9 = parseString(reader) - var _10: Api.MessageMedia? + var _9: Int32? + _9 = reader.readInt32() + var _10: String? + _10 = parseString(reader) + var _11: Api.MessageMedia? if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.MessageMedia + _11 = Api.parse(reader, signature: signature) as? Api.MessageMedia } } - var _11: Api.ReplyMarkup? + var _12: Api.ReplyMarkup? if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup + _12 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup } } - var _12: [Api.MessageEntity]? + var _13: [Api.MessageEntity]? if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { - _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + _13 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) } } - var _13: Int32? - if Int(_1!) & Int(1 << 10) != 0 {_13 = reader.readInt32() } var _14: Int32? if Int(_1!) & Int(1 << 10) != 0 {_14 = reader.readInt32() } - var _15: Api.MessageReplies? + var _15: Int32? + if Int(_1!) & Int(1 << 10) != 0 {_15 = reader.readInt32() } + var _16: Api.MessageReplies? if Int(_1!) & Int(1 << 23) != 0 {if let signature = reader.readInt32() { - _15 = Api.parse(reader, signature: signature) as? Api.MessageReplies + _16 = Api.parse(reader, signature: signature) as? Api.MessageReplies } } - var _16: Int32? - if Int(_1!) & Int(1 << 15) != 0 {_16 = reader.readInt32() } - var _17: String? - if Int(_1!) & Int(1 << 16) != 0 {_17 = parseString(reader) } - var _18: Int64? - if Int(_1!) & Int(1 << 17) != 0 {_18 = reader.readInt64() } - var _19: Api.MessageReactions? + var _17: Int32? + if Int(_1!) & Int(1 << 15) != 0 {_17 = reader.readInt32() } + var _18: String? + if Int(_1!) & Int(1 << 16) != 0 {_18 = parseString(reader) } + var _19: Int64? + if Int(_1!) & Int(1 << 17) != 0 {_19 = reader.readInt64() } + var _20: Api.MessageReactions? if Int(_1!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() { - _19 = Api.parse(reader, signature: signature) as? Api.MessageReactions + _20 = Api.parse(reader, signature: signature) as? Api.MessageReactions } } - var _20: [Api.RestrictionReason]? + var _21: [Api.RestrictionReason]? if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() { - _20 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) + _21 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) } } - var _21: Int32? - if Int(_1!) & Int(1 << 25) != 0 {_21 = reader.readInt32() } + var _22: Int32? + if Int(_1!) & Int(1 << 25) != 0 {_22 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 8) == 0) || _3 != nil let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 11) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil - let _c8 = _8 != nil + let _c5 = (Int(_1!) & Int(1 << 28) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil + let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil let _c9 = _9 != nil - let _c10 = (Int(_1!) & Int(1 << 9) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 6) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 10) == 0) || _13 != nil + let _c10 = _10 != nil + let _c11 = (Int(_1!) & Int(1 << 9) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 7) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 10) == 0) || _14 != nil - let _c15 = (Int(_1!) & Int(1 << 23) == 0) || _15 != nil - let _c16 = (Int(_1!) & Int(1 << 15) == 0) || _16 != nil - let _c17 = (Int(_1!) & Int(1 << 16) == 0) || _17 != nil - let _c18 = (Int(_1!) & Int(1 << 17) == 0) || _18 != nil - let _c19 = (Int(_1!) & Int(1 << 20) == 0) || _19 != nil - let _c20 = (Int(_1!) & Int(1 << 22) == 0) || _20 != nil - let _c21 = (Int(_1!) & Int(1 << 25) == 0) || _21 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 { - return Api.Message.message(flags: _1!, id: _2!, fromId: _3, peerId: _4!, fwdFrom: _5, viaBotId: _6, replyTo: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, forwards: _14, replies: _15, editDate: _16, postAuthor: _17, groupedId: _18, reactions: _19, restrictionReason: _20, ttlPeriod: _21) + let _c15 = (Int(_1!) & Int(1 << 10) == 0) || _15 != nil + let _c16 = (Int(_1!) & Int(1 << 23) == 0) || _16 != nil + let _c17 = (Int(_1!) & Int(1 << 15) == 0) || _17 != nil + let _c18 = (Int(_1!) & Int(1 << 16) == 0) || _18 != nil + let _c19 = (Int(_1!) & Int(1 << 17) == 0) || _19 != nil + let _c20 = (Int(_1!) & Int(1 << 20) == 0) || _20 != nil + let _c21 = (Int(_1!) & Int(1 << 22) == 0) || _21 != nil + let _c22 = (Int(_1!) & Int(1 << 25) == 0) || _22 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 { + return Api.Message.message(flags: _1!, id: _2!, fromId: _3, peerId: _4!, savedPeerId: _5, fwdFrom: _6, viaBotId: _7, replyTo: _8, date: _9!, message: _10!, media: _11, replyMarkup: _12, entities: _13, views: _14, forwards: _15, replies: _16, editDate: _17, postAuthor: _18, groupedId: _19, reactions: _20, restrictionReason: _21, ttlPeriod: _22) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api19.swift b/submodules/TelegramApi/Sources/Api19.swift index 3b54211e63..61fa36ecb8 100644 --- a/submodules/TelegramApi/Sources/Api19.swift +++ b/submodules/TelegramApi/Sources/Api19.swift @@ -606,6 +606,52 @@ public extension Api { } } +public extension Api { + enum SavedDialog: TypeConstructorDescription { + case savedDialog(flags: Int32, peer: Api.Peer, topMessage: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedDialog(let flags, let peer, let topMessage): + if boxed { + buffer.appendInt32(-1115174036) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(topMessage, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedDialog(let flags, let peer, let topMessage): + return ("savedDialog", [("flags", flags as Any), ("peer", peer as Any), ("topMessage", topMessage as Any)]) + } + } + + public static func parse_savedDialog(_ reader: BufferReader) -> SavedDialog? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Peer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.SavedDialog.savedDialog(flags: _1!, peer: _2!, topMessage: _3!) + } + else { + return nil + } + } + + } +} public extension Api { enum SearchResultsCalendarPeriod: TypeConstructorDescription { case searchResultsCalendarPeriod(date: Int32, minMsgId: Int32, maxMsgId: Int32, count: Int32) diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index 2ed34c1d5b..bbc72d8b97 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -550,6 +550,7 @@ public extension Api { case updatePinnedChannelMessages(flags: Int32, channelId: Int64, messages: [Int32], pts: Int32, ptsCount: Int32) case updatePinnedDialogs(flags: Int32, folderId: Int32?, order: [Api.DialogPeer]?) case updatePinnedMessages(flags: Int32, peer: Api.Peer, messages: [Int32], pts: Int32, ptsCount: Int32) + case updatePinnedSavedDialogs(flags: Int32, order: [Api.DialogPeer]?) case updatePrivacy(key: Api.PrivacyKey, rules: [Api.PrivacyRule]) case updatePtsChanged case updateReadChannelDiscussionInbox(flags: Int32, channelId: Int64, topMsgId: Int32, readMaxId: Int32, broadcastId: Int64?, broadcastPost: Int32?) @@ -565,6 +566,7 @@ public extension Api { case updateRecentEmojiStatuses case updateRecentReactions case updateRecentStickers + case updateSavedDialogPinned(flags: Int32, peer: Api.DialogPeer) case updateSavedGifs case updateSavedRingtones case updateSentStoryReaction(peer: Api.Peer, storyId: Int32, reaction: Api.Reaction) @@ -1372,6 +1374,17 @@ public extension Api { serializeInt32(pts, buffer: buffer, boxed: false) serializeInt32(ptsCount, buffer: buffer, boxed: false) break + case .updatePinnedSavedDialogs(let flags, let order): + if boxed { + buffer.appendInt32(1751942566) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order!.count)) + for item in order! { + item.serialize(buffer, true) + }} + break case .updatePrivacy(let key, let rules): if boxed { buffer.appendInt32(-298113238) @@ -1497,6 +1510,13 @@ public extension Api { buffer.appendInt32(-1706939360) } + break + case .updateSavedDialogPinned(let flags, let peer): + if boxed { + buffer.appendInt32(-1364222348) + } + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) break case .updateSavedGifs: if boxed { @@ -1828,6 +1848,8 @@ public extension Api { return ("updatePinnedDialogs", [("flags", flags as Any), ("folderId", folderId as Any), ("order", order as Any)]) case .updatePinnedMessages(let flags, let peer, let messages, let pts, let ptsCount): return ("updatePinnedMessages", [("flags", flags as Any), ("peer", peer as Any), ("messages", messages as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updatePinnedSavedDialogs(let flags, let order): + return ("updatePinnedSavedDialogs", [("flags", flags as Any), ("order", order as Any)]) case .updatePrivacy(let key, let rules): return ("updatePrivacy", [("key", key as Any), ("rules", rules as Any)]) case .updatePtsChanged: @@ -1858,6 +1880,8 @@ public extension Api { return ("updateRecentReactions", []) case .updateRecentStickers: return ("updateRecentStickers", []) + case .updateSavedDialogPinned(let flags, let peer): + return ("updateSavedDialogPinned", [("flags", flags as Any), ("peer", peer as Any)]) case .updateSavedGifs: return ("updateSavedGifs", []) case .updateSavedRingtones: @@ -3538,6 +3562,22 @@ public extension Api { return nil } } + public static func parse_updatePinnedSavedDialogs(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.DialogPeer]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogPeer.self) + } } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + if _c1 && _c2 { + return Api.Update.updatePinnedSavedDialogs(flags: _1!, order: _2) + } + else { + return nil + } + } public static func parse_updatePrivacy(_ reader: BufferReader) -> Update? { var _1: Api.PrivacyKey? if let signature = reader.readInt32() { @@ -3751,6 +3791,22 @@ public extension Api { public static func parse_updateRecentStickers(_ reader: BufferReader) -> Update? { return Api.Update.updateRecentStickers } + public static func parse_updateSavedDialogPinned(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.DialogPeer? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DialogPeer + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.Update.updateSavedDialogPinned(flags: _1!, peer: _2!) + } + else { + return nil + } + } public static func parse_updateSavedGifs(_ reader: BufferReader) -> Update? { return Api.Update.updateSavedGifs } diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index afd23f9c90..639e18a7b2 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -1425,59 +1425,153 @@ public extension Api.messages { } } public extension Api.messages { - enum SavedGifs: TypeConstructorDescription { - case savedGifs(hash: Int64, gifs: [Api.Document]) - case savedGifsNotModified + enum SavedDialogs: TypeConstructorDescription { + case savedDialogs(dialogs: [Api.SavedDialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + case savedDialogsNotModified(count: Int32) + case savedDialogsSlice(count: Int32, dialogs: [Api.SavedDialog], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .savedGifs(let hash, let gifs): + case .savedDialogs(let dialogs, let messages, let chats, let users): if boxed { - buffer.appendInt32(-2069878259) + buffer.appendInt32(-130358751) } - serializeInt64(hash, buffer: buffer, boxed: false) buffer.appendInt32(481674261) - buffer.appendInt32(Int32(gifs.count)) - for item in gifs { + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { item.serialize(buffer, true) } break - case .savedGifsNotModified: + case .savedDialogsNotModified(let count): if boxed { - buffer.appendInt32(-402498398) + buffer.appendInt32(-1071681560) + } + serializeInt32(count, buffer: buffer, boxed: false) + break + case .savedDialogsSlice(let count, let dialogs, let messages, let chats, let users): + if boxed { + buffer.appendInt32(1153080793) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(dialogs.count)) + for item in dialogs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) } - break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .savedGifs(let hash, let gifs): - return ("savedGifs", [("hash", hash as Any), ("gifs", gifs as Any)]) - case .savedGifsNotModified: - return ("savedGifsNotModified", []) + case .savedDialogs(let dialogs, let messages, let chats, let users): + return ("savedDialogs", [("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + case .savedDialogsNotModified(let count): + return ("savedDialogsNotModified", [("count", count as Any)]) + case .savedDialogsSlice(let count, let dialogs, let messages, let chats, let users): + return ("savedDialogsSlice", [("count", count as Any), ("dialogs", dialogs as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) } } - public static func parse_savedGifs(_ reader: BufferReader) -> SavedGifs? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.Document]? + public static func parse_savedDialogs(_ reader: BufferReader) -> SavedDialogs? { + var _1: [Api.SavedDialog]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedDialog.self) + } + var _2: [Api.Message]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _3: [Api.Chat]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SavedGifs.savedGifs(hash: _1!, gifs: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.SavedDialogs.savedDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) } else { return nil } } - public static func parse_savedGifsNotModified(_ reader: BufferReader) -> SavedGifs? { - return Api.messages.SavedGifs.savedGifsNotModified + public static func parse_savedDialogsNotModified(_ reader: BufferReader) -> SavedDialogs? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.messages.SavedDialogs.savedDialogsNotModified(count: _1!) + } + else { + return nil + } + } + public static func parse_savedDialogsSlice(_ reader: BufferReader) -> SavedDialogs? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.SavedDialog]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SavedDialog.self) + } + var _3: [Api.Message]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _4: [Api.Chat]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.messages.SavedDialogs.savedDialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) + } + else { + return nil + } } } diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 0ff2e96975..c0a8e8f120 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -1,3 +1,61 @@ +public extension Api.messages { + enum SavedGifs: TypeConstructorDescription { + case savedGifs(hash: Int64, gifs: [Api.Document]) + case savedGifsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedGifs(let hash, let gifs): + if boxed { + buffer.appendInt32(-2069878259) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(gifs.count)) + for item in gifs { + item.serialize(buffer, true) + } + break + case .savedGifsNotModified: + if boxed { + buffer.appendInt32(-402498398) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedGifs(let hash, let gifs): + return ("savedGifs", [("hash", hash as Any), ("gifs", gifs as Any)]) + case .savedGifsNotModified: + return ("savedGifsNotModified", []) + } + } + + public static func parse_savedGifs(_ reader: BufferReader) -> SavedGifs? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.Document]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.SavedGifs.savedGifs(hash: _1!, gifs: _2!) + } + else { + return nil + } + } + public static func parse_savedGifsNotModified(_ reader: BufferReader) -> SavedGifs? { + return Api.messages.SavedGifs.savedGifsNotModified + } + + } +} public extension Api.messages { enum SearchCounter: TypeConstructorDescription { case searchCounter(flags: Int32, filter: Api.MessagesFilter, count: Int32) @@ -1482,85 +1540,3 @@ public extension Api.phone { } } -public extension Api.phone { - enum GroupCallStreamChannels: TypeConstructorDescription { - case groupCallStreamChannels(channels: [Api.GroupCallStreamChannel]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .groupCallStreamChannels(let channels): - if boxed { - buffer.appendInt32(-790330702) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(channels.count)) - for item in channels { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .groupCallStreamChannels(let channels): - return ("groupCallStreamChannels", [("channels", channels as Any)]) - } - } - - public static func parse_groupCallStreamChannels(_ reader: BufferReader) -> GroupCallStreamChannels? { - var _1: [Api.GroupCallStreamChannel]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallStreamChannel.self) - } - let _c1 = _1 != nil - if _c1 { - return Api.phone.GroupCallStreamChannels.groupCallStreamChannels(channels: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.phone { - enum GroupCallStreamRtmpUrl: TypeConstructorDescription { - case groupCallStreamRtmpUrl(url: String, key: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .groupCallStreamRtmpUrl(let url, let key): - if boxed { - buffer.appendInt32(767505458) - } - serializeString(url, buffer: buffer, boxed: false) - serializeString(key, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .groupCallStreamRtmpUrl(let url, let key): - return ("groupCallStreamRtmpUrl", [("url", url as Any), ("key", key as Any)]) - } - } - - public static func parse_groupCallStreamRtmpUrl(_ reader: BufferReader) -> GroupCallStreamRtmpUrl? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.phone.GroupCallStreamRtmpUrl.groupCallStreamRtmpUrl(url: _1!, key: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index 0f744475ba..472f613cd6 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -1,3 +1,85 @@ +public extension Api.phone { + enum GroupCallStreamChannels: TypeConstructorDescription { + case groupCallStreamChannels(channels: [Api.GroupCallStreamChannel]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .groupCallStreamChannels(let channels): + if boxed { + buffer.appendInt32(-790330702) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(channels.count)) + for item in channels { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .groupCallStreamChannels(let channels): + return ("groupCallStreamChannels", [("channels", channels as Any)]) + } + } + + public static func parse_groupCallStreamChannels(_ reader: BufferReader) -> GroupCallStreamChannels? { + var _1: [Api.GroupCallStreamChannel]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallStreamChannel.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.phone.GroupCallStreamChannels.groupCallStreamChannels(channels: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.phone { + enum GroupCallStreamRtmpUrl: TypeConstructorDescription { + case groupCallStreamRtmpUrl(url: String, key: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .groupCallStreamRtmpUrl(let url, let key): + if boxed { + buffer.appendInt32(767505458) + } + serializeString(url, buffer: buffer, boxed: false) + serializeString(key, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .groupCallStreamRtmpUrl(let url, let key): + return ("groupCallStreamRtmpUrl", [("url", url as Any), ("key", key as Any)]) + } + } + + public static func parse_groupCallStreamRtmpUrl(_ reader: BufferReader) -> GroupCallStreamRtmpUrl? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.phone.GroupCallStreamRtmpUrl.groupCallStreamRtmpUrl(url: _1!, key: _2!) + } + else { + return nil + } + } + + } +} public extension Api.phone { enum GroupParticipants: TypeConstructorDescription { case groupParticipants(count: Int32, participants: [Api.GroupCallParticipant], nextOffset: String, chats: [Api.Chat], users: [Api.User], version: Int32) @@ -1494,141 +1576,3 @@ public extension Api.stories { } } -public extension Api.stories { - enum StoryViews: TypeConstructorDescription { - case storyViews(views: [Api.StoryViews], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .storyViews(let views, let users): - if boxed { - buffer.appendInt32(-560009955) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(views.count)) - for item in views { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .storyViews(let views, let users): - return ("storyViews", [("views", views as Any), ("users", users as Any)]) - } - } - - public static func parse_storyViews(_ reader: BufferReader) -> StoryViews? { - var _1: [Api.StoryViews]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryViews.self) - } - var _2: [Api.User]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.stories.StoryViews.storyViews(views: _1!, users: _2!) - } - else { - return nil - } - } - - } -} -public extension Api.stories { - enum StoryViewsList: TypeConstructorDescription { - case storyViewsList(flags: Int32, count: Int32, viewsCount: Int32, forwardsCount: Int32, reactionsCount: Int32, views: [Api.StoryView], chats: [Api.Chat], users: [Api.User], nextOffset: String?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .storyViewsList(let flags, let count, let viewsCount, let forwardsCount, let reactionsCount, let views, let chats, let users, let nextOffset): - if boxed { - buffer.appendInt32(1507299269) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - serializeInt32(viewsCount, buffer: buffer, boxed: false) - serializeInt32(forwardsCount, buffer: buffer, boxed: false) - serializeInt32(reactionsCount, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(views.count)) - for item in views { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .storyViewsList(let flags, let count, let viewsCount, let forwardsCount, let reactionsCount, let views, let chats, let users, let nextOffset): - return ("storyViewsList", [("flags", flags as Any), ("count", count as Any), ("viewsCount", viewsCount as Any), ("forwardsCount", forwardsCount as Any), ("reactionsCount", reactionsCount as Any), ("views", views as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) - } - } - - public static func parse_storyViewsList(_ reader: BufferReader) -> StoryViewsList? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: [Api.StoryView]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryView.self) - } - var _7: [Api.Chat]? - if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _8: [Api.User]? - if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - var _9: String? - if Int(_1!) & Int(1 << 0) != 0 {_9 = parseString(reader) } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.stories.StoryViewsList.storyViewsList(flags: _1!, count: _2!, viewsCount: _3!, forwardsCount: _4!, reactionsCount: _5!, views: _6!, chats: _7!, users: _8!, nextOffset: _9) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api31.swift b/submodules/TelegramApi/Sources/Api31.swift index 0cfe25da84..e5b0c6e49a 100644 --- a/submodules/TelegramApi/Sources/Api31.swift +++ b/submodules/TelegramApi/Sources/Api31.swift @@ -1,3 +1,141 @@ +public extension Api.stories { + enum StoryViews: TypeConstructorDescription { + case storyViews(views: [Api.StoryViews], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyViews(let views, let users): + if boxed { + buffer.appendInt32(-560009955) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(views.count)) + for item in views { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyViews(let views, let users): + return ("storyViews", [("views", views as Any), ("users", users as Any)]) + } + } + + public static func parse_storyViews(_ reader: BufferReader) -> StoryViews? { + var _1: [Api.StoryViews]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryViews.self) + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.stories.StoryViews.storyViews(views: _1!, users: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.stories { + enum StoryViewsList: TypeConstructorDescription { + case storyViewsList(flags: Int32, count: Int32, viewsCount: Int32, forwardsCount: Int32, reactionsCount: Int32, views: [Api.StoryView], chats: [Api.Chat], users: [Api.User], nextOffset: String?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .storyViewsList(let flags, let count, let viewsCount, let forwardsCount, let reactionsCount, let views, let chats, let users, let nextOffset): + if boxed { + buffer.appendInt32(1507299269) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(viewsCount, buffer: buffer, boxed: false) + serializeInt32(forwardsCount, buffer: buffer, boxed: false) + serializeInt32(reactionsCount, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(views.count)) + for item in views { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .storyViewsList(let flags, let count, let viewsCount, let forwardsCount, let reactionsCount, let views, let chats, let users, let nextOffset): + return ("storyViewsList", [("flags", flags as Any), ("count", count as Any), ("viewsCount", viewsCount as Any), ("forwardsCount", forwardsCount as Any), ("reactionsCount", reactionsCount as Any), ("views", views as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)]) + } + } + + public static func parse_storyViewsList(_ reader: BufferReader) -> StoryViewsList? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: [Api.StoryView]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryView.self) + } + var _7: [Api.Chat]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _8: [Api.User]? + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + var _9: String? + if Int(_1!) & Int(1 << 0) != 0 {_9 = parseString(reader) } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.stories.StoryViewsList.storyViewsList(flags: _1!, count: _2!, viewsCount: _3!, forwardsCount: _4!, reactionsCount: _5!, views: _6!, chats: _7!, users: _8!, nextOffset: _9) + } + else { + return nil + } + } + + } +} public extension Api.updates { indirect enum ChannelDifference: TypeConstructorDescription { case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User]) diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index 6e161a1f2b..f4b884aef4 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -5670,6 +5670,21 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getPinnedSavedDialogs() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-700607264) + + return (FunctionDescription(name: "messages.getPinnedSavedDialogs", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SavedDialogs? in + let reader = BufferReader(buffer) + var result: Api.messages.SavedDialogs? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SavedDialogs + } + return result + }) + } +} public extension Api.functions.messages { static func getPollResults(peer: Api.InputPeer, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -5778,6 +5793,26 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getSavedDialogs(flags: Int32, offsetDate: Int32, offsetId: Int32, offsetPeer: Api.InputPeer, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1401016858) + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(offsetDate, buffer: buffer, boxed: false) + serializeInt32(offsetId, buffer: buffer, boxed: false) + offsetPeer.serialize(buffer, true) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getSavedDialogs", parameters: [("flags", String(describing: flags)), ("offsetDate", String(describing: offsetDate)), ("offsetId", String(describing: offsetId)), ("offsetPeer", String(describing: offsetPeer)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SavedDialogs? in + let reader = BufferReader(buffer) + var result: Api.messages.SavedDialogs? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.SavedDialogs + } + return result + }) + } +} public extension Api.functions.messages { static func getSavedGifs(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -5793,6 +5828,28 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getSavedHistory(peer: Api.InputPeer, offsetId: Int32, offsetDate: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1033519437) + peer.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(offsetDate, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getSavedHistory", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } +} public extension Api.functions.messages { static func getScheduledHistory(peer: Api.InputPeer, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -5830,18 +5887,19 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func getSearchCounters(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, filters: [Api.MessagesFilter]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.messages.SearchCounter]>) { + static func getSearchCounters(flags: Int32, peer: Api.InputPeer, savedPeerId: Api.InputPeer?, topMsgId: Int32?, filters: [Api.MessagesFilter]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.messages.SearchCounter]>) { let buffer = Buffer() - buffer.appendInt32(11435201) + buffer.appendInt32(465367808) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) + if Int(flags) & Int(1 << 2) != 0 {savedPeerId!.serialize(buffer, true)} if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} buffer.appendInt32(481674261) buffer.appendInt32(Int32(filters.count)) for item in filters { item.serialize(buffer, true) } - return (FunctionDescription(name: "messages.getSearchCounters", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("filters", String(describing: filters))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.messages.SearchCounter]? in + return (FunctionDescription(name: "messages.getSearchCounters", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("savedPeerId", String(describing: savedPeerId)), ("topMsgId", String(describing: topMsgId)), ("filters", String(describing: filters))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.messages.SearchCounter]? in let reader = BufferReader(buffer) var result: [Api.messages.SearchCounter]? if let _ = reader.readInt32() { @@ -5852,14 +5910,16 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func getSearchResultsCalendar(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, offsetDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func getSearchResultsCalendar(flags: Int32, peer: Api.InputPeer, savedPeerId: Api.InputPeer?, filter: Api.MessagesFilter, offsetId: Int32, offsetDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1240514025) + buffer.appendInt32(1789130429) + serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) + if Int(flags) & Int(1 << 2) != 0 {savedPeerId!.serialize(buffer, true)} filter.serialize(buffer, true) serializeInt32(offsetId, buffer: buffer, boxed: false) serializeInt32(offsetDate, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getSearchResultsCalendar", parameters: [("peer", String(describing: peer)), ("filter", String(describing: filter)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsCalendar? in + return (FunctionDescription(name: "messages.getSearchResultsCalendar", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("savedPeerId", String(describing: savedPeerId)), ("filter", String(describing: filter)), ("offsetId", String(describing: offsetId)), ("offsetDate", String(describing: offsetDate))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsCalendar? in let reader = BufferReader(buffer) var result: Api.messages.SearchResultsCalendar? if let signature = reader.readInt32() { @@ -5870,14 +5930,16 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func getSearchResultsPositions(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func getSearchResultsPositions(flags: Int32, peer: Api.InputPeer, savedPeerId: Api.InputPeer?, filter: Api.MessagesFilter, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1855292323) + buffer.appendInt32(-1669386480) + serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) + if Int(flags) & Int(1 << 2) != 0 {savedPeerId!.serialize(buffer, true)} filter.serialize(buffer, true) serializeInt32(offsetId, buffer: buffer, boxed: false) serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.getSearchResultsPositions", parameters: [("peer", String(describing: peer)), ("filter", String(describing: filter)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsPositions? in + return (FunctionDescription(name: "messages.getSearchResultsPositions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("savedPeerId", String(describing: savedPeerId)), ("filter", String(describing: filter)), ("offsetId", String(describing: offsetId)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsPositions? in let reader = BufferReader(buffer) var result: Api.messages.SearchResultsPositions? if let signature = reader.readInt32() { @@ -6384,6 +6446,26 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func reorderPinnedSavedDialogs(flags: Int32, order: [Api.InputDialogPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1955502713) + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "messages.reorderPinnedSavedDialogs", parameters: [("flags", String(describing: flags)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.messages { static func reorderStickerSets(flags: Int32, order: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -6646,13 +6728,14 @@ public extension Api.functions.messages { } } public extension Api.functions.messages { - static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputPeer?, topMsgId: Int32?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func search(flags: Int32, peer: Api.InputPeer, q: String, fromId: Api.InputPeer?, savedPeerId: Api.InputPeer?, topMsgId: Int32?, filter: Api.MessagesFilter, minDate: Int32, maxDate: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-1593989278) + buffer.appendInt32(-1481316055) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) serializeString(q, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {fromId!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {savedPeerId!.serialize(buffer, true)} if Int(flags) & Int(1 << 1) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)} filter.serialize(buffer, true) serializeInt32(minDate, buffer: buffer, boxed: false) @@ -6663,7 +6746,7 @@ public extension Api.functions.messages { serializeInt32(maxId, buffer: buffer, boxed: false) serializeInt32(minId, buffer: buffer, boxed: false) serializeInt64(hash, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.search", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("q", String(describing: q)), ("fromId", String(describing: fromId)), ("topMsgId", String(describing: topMsgId)), ("filter", String(describing: filter)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + return (FunctionDescription(name: "messages.search", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("q", String(describing: q)), ("fromId", String(describing: fromId)), ("savedPeerId", String(describing: savedPeerId)), ("topMsgId", String(describing: topMsgId)), ("filter", String(describing: filter)), ("minDate", String(describing: minDate)), ("maxDate", String(describing: maxDate)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in let reader = BufferReader(buffer) var result: Api.messages.Messages? if let signature = reader.readInt32() { @@ -7405,6 +7488,22 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func toggleSavedDialogPin(flags: Int32, peer: Api.InputDialogPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1400783906) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.toggleSavedDialogPin", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.messages { static func toggleStickerSets(flags: Int32, stickersets: [Api.InputStickerSet]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 60e9e51304..940503e49a 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -126,7 +126,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { switch messsage { - case let .message(_, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let chatPeerId = messagePeerId return chatPeerId.peerId case let .messageEmpty(_, _, peerId): @@ -142,7 +142,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { switch message { - case let .message(_, _, fromId, chatPeerId, fwdHeader, viaBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _): + case let .message(_, _, fromId, chatPeerId, savedPeerId, fwdHeader, viaBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _): let peerId: PeerId = chatPeerId.peerId var result = [peerId] @@ -168,6 +168,10 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { if let viaBotId = viaBotId { result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBotId))) } + + if let savedPeerId = savedPeerId { + result.append(savedPeerId.peerId) + } if let media = media { switch media { @@ -256,7 +260,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? { switch message { - case let .message(_, id, _, chatPeerId, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, id, _, chatPeerId, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _): if let replyTo = replyTo { let peerId: PeerId = chatPeerId.peerId @@ -582,7 +586,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes extension StoreMessage { convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) { switch apiMessage { - case let .message(flags, id, fromId, chatPeerId, fwdFrom, viaBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod): + case let .message(flags, id, fromId, chatPeerId, savedPeerId, fwdFrom, viaBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod): let resolvedFromId = fromId?.peerId ?? chatPeerId.peerId let peerId: PeerId @@ -622,17 +626,17 @@ extension StoreMessage { if isForumTopic { let threadIdValue = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToTopId) threadMessageId = threadIdValue - threadId = makeMessageThreadId(threadIdValue) + threadId = Int64(threadIdValue.id) } } else { if peerId.namespace == Namespaces.Peer.CloudChannel { let threadIdValue = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToTopId) threadMessageId = threadIdValue - threadId = makeMessageThreadId(threadIdValue) + threadId = Int64(threadIdValue.id) } else { let threadIdValue = MessageId(peerId: replyToPeerId?.peerId ?? peerId, namespace: Namespaces.Message.Cloud, id: replyToTopId) threadMessageId = threadIdValue - threadId = makeMessageThreadId(threadIdValue) + threadId = Int64(threadIdValue.id) } } } else if peerId.namespace == Namespaces.Peer.CloudChannel { @@ -641,11 +645,11 @@ extension StoreMessage { if peerIsForum { if isForumTopic { threadMessageId = threadIdValue - threadId = makeMessageThreadId(threadIdValue) + threadId = Int64(threadIdValue.id) } } else { threadMessageId = threadIdValue - threadId = makeMessageThreadId(threadIdValue) + threadId = Int64(threadIdValue.id) } } attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: threadMessageId, quote: quote, isQuote: isQuote)) @@ -663,7 +667,6 @@ extension StoreMessage { } var forwardInfo: StoreMessageForwardInfo? - var savedFromPeerId: PeerId? if let fwdFrom = fwdFrom { switch fwdFrom { case let .messageFwdHeader(flags, fromId, fromName, date, channelPost, postAuthor, savedFromPeer, savedFromMsgId, psaType): @@ -693,7 +696,6 @@ extension StoreMessage { if let savedFromPeer = savedFromPeer, let savedFromMsgId = savedFromMsgId { let peerId: PeerId = savedFromPeer.peerId - savedFromPeerId = peerId let messageId: MessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: savedFromMsgId) attributes.append(SourceReferenceMessageAttribute(messageId: messageId)) } @@ -708,8 +710,8 @@ extension StoreMessage { } } - if peerId == accountPeerId, let savedFromPeerId = savedFromPeerId { - threadId = savedFromPeerId.toInt64() + if peerId == accountPeerId, let savedPeerId = savedPeerId { + threadId = savedPeerId.peerId.toInt64() } let messageText = message @@ -911,12 +913,12 @@ extension StoreMessage { let threadIdValue = MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToTopId) threadMessageId = threadIdValue if replyPeerId == peerId { - threadId = makeMessageThreadId(threadIdValue) + threadId = Int64(threadIdValue.id) } } else if peerId.namespace == Namespaces.Peer.CloudChannel { let threadIdValue = MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId) threadMessageId = threadIdValue - threadId = makeMessageThreadId(threadIdValue) + threadId = Int64(threadIdValue.id) } switch action { case .messageActionTopicEdit: diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index db874759a9..69747f8cc4 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -516,7 +516,7 @@ func _internal_setChannelForumMode(postbox: Postbox, network: Network, stateMana struct LoadMessageHistoryThreadsResult { struct Item { var threadId: Int64 - var data: MessageHistoryThreadData + var data: MessageHistoryThreadData? var topMessage: Int32 var unreadMentionsCount: Int32 var unreadReactionsCount: Int32 @@ -524,7 +524,7 @@ struct LoadMessageHistoryThreadsResult { init( threadId: Int64, - data: MessageHistoryThreadData, + data: MessageHistoryThreadData?, topMessage: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32, @@ -570,95 +570,172 @@ enum LoadMessageHistoryThreadsError { case generic } -func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, query: String?, offsetIndex: StoredPeerThreadCombinedState.Index?, limit: Int) -> Signal { - let signal: Signal = postbox.transaction { transaction -> Api.InputChannel? in - guard let channel = transaction.getPeer(peerId) as? TelegramChannel else { - return nil - } - if !channel.flags.contains(.isForum) { - return nil - } - return apiInputChannel(channel) +func _internal_fillSavedMessageHistory(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal { + enum PassResult { + case restart } - |> castError(LoadMessageHistoryThreadsError.self) - |> mapToSignal { inputChannel -> Signal in - guard let inputChannel = inputChannel else { - return .fail(.generic) + return (postbox.transaction { transaction -> Range? in + let holes = transaction.getHoles(peerId: accountPeerId, namespace: Namespaces.Message.Cloud) + return holes.rangeView.last + } + |> castError(PassResult.self) + |> mapToSignal { range -> Signal in + if let range { + return fetchMessageHistoryHole( + accountPeerId: accountPeerId, + source: .network(network), + postbox: postbox, + peerInput: .direct(peerId: accountPeerId, threadId: nil), + namespace: Namespaces.Message.Cloud, + direction: .range( + start: MessageId(peerId: accountPeerId, namespace: Namespaces.Message.Cloud, id: Int32(range.upperBound) - 1), + end: MessageId(peerId: accountPeerId, namespace: Namespaces.Message.Cloud, id: Int32(range.lowerBound) - 1) + ), + space: .everywhere, + count: 100 + ) + |> ignoreValues + |> castError(PassResult.self) + |> then(.fail(.restart)) + } else { + return .complete() + } + }) + |> restartIfError +} + +func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, query: String?, offsetIndex: StoredPeerThreadCombinedState.Index?, limit: Int) -> Signal { + if peerId == accountPeerId { + if !"".isEmpty { + return _internal_fillSavedMessageHistory(accountPeerId: accountPeerId, postbox: postbox, network: network) + |> castError(LoadMessageHistoryThreadsError.self) + |> map { _ -> LoadMessageHistoryThreadsResult in + } + |> then(postbox.transaction { transaction -> LoadMessageHistoryThreadsResult in + var topMessages: [Int64: Message] = [:] + transaction.scanTopMessages(peerId: accountPeerId, namespace: Namespaces.Message.Cloud, limit: 100000, { message in + if let threadId = message.threadId { + if let current = topMessages[threadId] { + if current.id < message.id { + topMessages[threadId] = message + } + } else { + topMessages[threadId] = message + } + } + + return true + }) + + + var items: [LoadMessageHistoryThreadsResult.Item] = [] + for message in topMessages.values.sorted(by: { $0.id > $1.id }) { + guard let threadId = message.threadId else { + continue + } + items.append(LoadMessageHistoryThreadsResult.Item( + threadId: threadId, + data: MessageHistoryThreadData( + creationDate: 0, + isOwnedByMe: true, + author: accountPeerId, + info: EngineMessageHistoryThread.Info(title: "", icon: nil, iconColor: 0), + incomingUnreadCount: 0, + maxIncomingReadId: 0, + maxKnownMessageId: 0, + maxOutgoingReadId: 0, + isClosed: false, + isHidden: false, + notificationSettings: TelegramPeerNotificationSettings.defaultSettings + ), + topMessage: message.id.id, + unreadMentionsCount: 0, + unreadReactionsCount: 0, + index: StoredPeerThreadCombinedState.Index(timestamp: message.timestamp, threadId: threadId, messageId: message.id.id) + )) + } + + return LoadMessageHistoryThreadsResult( + peerId: accountPeerId, + items: items, + messages: [], + pinnedThreadIds: nil, + combinedState: PeerThreadCombinedState(validIndexBoundary: StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1)), + users: [], + chats: [] + ) + } + |> castError(LoadMessageHistoryThreadsError.self)) } - var flags: Int32 = 0 - if query != nil { - flags |= 1 << 0 - } + var flags: Int32 = 0 + flags = 0 var offsetDate: Int32 = 0 var offsetId: Int32 = 0 - var offsetTopic: Int32 = 0 + var offsetPeer: Api.InputPeer = .inputPeerEmpty if let offsetIndex = offsetIndex { offsetDate = offsetIndex.timestamp offsetId = offsetIndex.messageId - offsetTopic = Int32(clamping: offsetIndex.threadId) + //TODO:api + offsetPeer = .inputPeerEmpty } - let signal: Signal = network.request(Api.functions.channels.getForumTopics( + let signal: Signal = network.request(Api.functions.messages.getSavedDialogs( flags: flags, - channel: inputChannel, - q: query, offsetDate: offsetDate, offsetId: offsetId, - offsetTopic: offsetTopic, - limit: Int32(limit) + offsetPeer: offsetPeer, + limit: Int32(limit), + hash: 0 )) |> mapError { _ -> LoadMessageHistoryThreadsError in return .generic } |> mapToSignal { result -> Signal in switch result { - case let .forumTopics(_, _, topics, messages, chats, users, pts): + case .savedDialogs(let dialogs, let messages, let chats, let users), .savedDialogsSlice(_, let dialogs, let messages, let chats, let users): var items: [LoadMessageHistoryThreadsResult.Item] = [] var pinnedIds: [Int64] = [] let addedMessages = messages.compactMap { message -> StoreMessage? in - return StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: true) + return StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: false) } - let _ = pts var minIndex: StoredPeerThreadCombinedState.Index? - for topic in topics { - switch topic { - case let .forumTopic(flags, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId, notifySettings, draft): - let _ = draft - - if (flags & (1 << 3)) != 0 { - pinnedIds.append(Int64(id)) + for dialog in dialogs { + switch dialog { + case let .savedDialog(flags, peer, topMessage): + if (flags & (1 << 2)) != 0 { + pinnedIds.append(peer.peerId.toInt64()) } let data = MessageHistoryThreadData( - creationDate: date, - isOwnedByMe: (flags & (1 << 1)) != 0, - author: fromId.peerId, + creationDate: 0, + isOwnedByMe: true, + author: peer.peerId, info: EngineMessageHistoryThread.Info( - title: title, - icon: iconEmojiId == 0 ? nil : iconEmojiId, - iconColor: iconColor + title: "", + icon: nil, + iconColor: 0 ), - incomingUnreadCount: unreadCount, - maxIncomingReadId: readInboxMaxId, + incomingUnreadCount: 0, + maxIncomingReadId: 0, maxKnownMessageId: topMessage, - maxOutgoingReadId: readOutboxMaxId, - isClosed: (flags & (1 << 2)) != 0, - isHidden: (flags & (1 << 6)) != 0, - notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings) + maxOutgoingReadId: 0, + isClosed: false, + isHidden: false, + notificationSettings: TelegramPeerNotificationSettings.defaultSettings ) - var topTimestamp = date + var topTimestamp: Int32 = 1 for message in addedMessages { - if message.id.peerId == peerId && message.threadId == Int64(id) { + if message.id.peerId == peerId && message.threadId == peer.peerId.toInt64() { topTimestamp = max(topTimestamp, message.timestamp) } } - let topicIndex = StoredPeerThreadCombinedState.Index(timestamp: topTimestamp, threadId: Int64(id), messageId: topMessage) + let topicIndex = StoredPeerThreadCombinedState.Index(timestamp: topTimestamp, threadId: peer.peerId.toInt64(), messageId: topMessage) if let minIndexValue = minIndex { if topicIndex < minIndexValue { minIndex = topicIndex @@ -668,15 +745,13 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post } items.append(LoadMessageHistoryThreadsResult.Item( - threadId: Int64(id), + threadId: peer.peerId.toInt64(), data: data, topMessage: topMessage, - unreadMentionsCount: unreadMentionsCount, - unreadReactionsCount: unreadReactionsCount, + unreadMentionsCount: 0, + unreadReactionsCount: 0, index: topicIndex )) - case .forumTopicDeleted: - break } } @@ -686,7 +761,7 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post } var nextIndex: StoredPeerThreadCombinedState.Index - if topics.count != 0 { + if dialogs.count != 0 { nextIndex = minIndex ?? StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1) } else { nextIndex = StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1) @@ -706,12 +781,154 @@ func _internal_requestMessageHistoryThreads(accountPeerId: PeerId, postbox: Post users: users, chats: chats )) + case .savedDialogsNotModified: + return .complete() } } + return signal + } else { + let signal: Signal = postbox.transaction { transaction -> Api.InputChannel? in + guard let channel = transaction.getPeer(peerId) as? TelegramChannel else { + return nil + } + if !channel.flags.contains(.isForum) { + return nil + } + return apiInputChannel(channel) + } + |> castError(LoadMessageHistoryThreadsError.self) + |> mapToSignal { inputChannel -> Signal in + guard let inputChannel = inputChannel else { + return .fail(.generic) + } + var flags: Int32 = 0 + + if query != nil { + flags |= 1 << 0 + } + + var offsetDate: Int32 = 0 + var offsetId: Int32 = 0 + var offsetTopic: Int32 = 0 + if let offsetIndex = offsetIndex { + offsetDate = offsetIndex.timestamp + offsetId = offsetIndex.messageId + offsetTopic = Int32(clamping: offsetIndex.threadId) + } + let signal: Signal = network.request(Api.functions.channels.getForumTopics( + flags: flags, + channel: inputChannel, + q: query, + offsetDate: offsetDate, + offsetId: offsetId, + offsetTopic: offsetTopic, + limit: Int32(limit) + )) + |> mapError { _ -> LoadMessageHistoryThreadsError in + return .generic + } + |> mapToSignal { result -> Signal in + switch result { + case let .forumTopics(_, _, topics, messages, chats, users, pts): + var items: [LoadMessageHistoryThreadsResult.Item] = [] + var pinnedIds: [Int64] = [] + + let addedMessages = messages.compactMap { message -> StoreMessage? in + return StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: true) + } + + let _ = pts + var minIndex: StoredPeerThreadCombinedState.Index? + + for topic in topics { + switch topic { + case let .forumTopic(flags, id, date, title, iconColor, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, unreadReactionsCount, fromId, notifySettings, draft): + let _ = draft + + if (flags & (1 << 3)) != 0 { + pinnedIds.append(Int64(id)) + } + + let data = MessageHistoryThreadData( + creationDate: date, + isOwnedByMe: (flags & (1 << 1)) != 0, + author: fromId.peerId, + info: EngineMessageHistoryThread.Info( + title: title, + icon: iconEmojiId == 0 ? nil : iconEmojiId, + iconColor: iconColor + ), + incomingUnreadCount: unreadCount, + maxIncomingReadId: readInboxMaxId, + maxKnownMessageId: topMessage, + maxOutgoingReadId: readOutboxMaxId, + isClosed: (flags & (1 << 2)) != 0, + isHidden: (flags & (1 << 6)) != 0, + notificationSettings: TelegramPeerNotificationSettings(apiSettings: notifySettings) + ) + + var topTimestamp = date + for message in addedMessages { + if message.id.peerId == peerId && message.threadId == Int64(id) { + topTimestamp = max(topTimestamp, message.timestamp) + } + } + + let topicIndex = StoredPeerThreadCombinedState.Index(timestamp: topTimestamp, threadId: Int64(id), messageId: topMessage) + if let minIndexValue = minIndex { + if topicIndex < minIndexValue { + minIndex = topicIndex + } + } else { + minIndex = topicIndex + } + + items.append(LoadMessageHistoryThreadsResult.Item( + threadId: Int64(id), + data: data, + topMessage: topMessage, + unreadMentionsCount: unreadMentionsCount, + unreadReactionsCount: unreadReactionsCount, + index: topicIndex + )) + case .forumTopicDeleted: + break + } + } + + var pinnedThreadIds: [Int64]? + if offsetIndex == nil { + pinnedThreadIds = pinnedIds + } + + var nextIndex: StoredPeerThreadCombinedState.Index + if topics.count != 0 { + nextIndex = minIndex ?? StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1) + } else { + nextIndex = StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1) + } + if let offsetIndex = offsetIndex, nextIndex == offsetIndex { + nextIndex = StoredPeerThreadCombinedState.Index(timestamp: 0, threadId: 0, messageId: 1) + } + + let combinedState = PeerThreadCombinedState(validIndexBoundary: nextIndex) + + return .single(LoadMessageHistoryThreadsResult( + peerId: peerId, + items: items, + messages: addedMessages, + pinnedThreadIds: pinnedThreadIds, + combinedState: combinedState, + users: users, + chats: chats + )) + } + } + return signal + } + return signal } - - return signal } func applyLoadMessageHistoryThreadsResults(accountPeerId: PeerId, transaction: Transaction, results: [LoadMessageHistoryThreadsResult]) { @@ -722,9 +939,16 @@ func applyLoadMessageHistoryThreadsResults(accountPeerId: PeerId, transaction: T let _ = InternalAccountState.addMessages(transaction: transaction, messages: result.messages, location: .Random) for item in result.items { - guard let info = StoredMessageHistoryThreadInfo(item.data) else { + let info: StoredMessageHistoryThreadInfo? + if let data = item.data { + info = StoredMessageHistoryThreadInfo(data) + } else { + info = telegramPostboxSeedConfiguration.automaticThreadIndexInfo(result.peerId, item.threadId) + } + guard let info else { continue } + transaction.setMessageHistoryThreadInfo(peerId: result.peerId, threadId: item.threadId, info: info) transaction.replaceMessageTagSummary(peerId: result.peerId, threadId: item.threadId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: item.unreadMentionsCount, maxId: item.topMessage) @@ -864,6 +1088,9 @@ public func _internal_searchForumTopics(account: Account, peerId: EnginePeer.Id, guard let index = item.index else { continue } + guard let itemData = item.data else { + continue + } items.append(EngineChatList.Item( id: .forum(item.threadId), index: .forum(pinnedIndex: .none, timestamp: index.timestamp, threadId: index.threadId, namespace: Namespaces.Message.Cloud, id: index.messageId), @@ -878,10 +1105,10 @@ public func _internal_searchForumTopics(account: Account, peerId: EnginePeer.Id, hasUnseenReactions: false, forumTopicData: EngineChatList.ForumTopicData( id: item.threadId, - title: item.data.info.title, - iconFileId: item.data.info.icon, - iconColor: item.data.info.iconColor, - maxOutgoingReadMessageId: EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: item.data.maxOutgoingReadId), + title: itemData.info.title, + iconFileId: itemData.info.icon, + iconColor: itemData.info.iconColor, + maxOutgoingReadMessageId: EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: itemData.maxOutgoingReadId), isUnread: false ), topForumTopicItems: [], diff --git a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift index affd5685d6..3601676e83 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift @@ -573,7 +573,9 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, var quote = replyToMessageId.quote let isQuote = quote != nil if let replyMessage = transaction.getMessage(replyToMessageId.messageId) { - threadMessageId = replyMessage.effectiveReplyThreadMessageId + if replyMessage.id.namespace == Namespaces.Message.Cloud, let threadId = replyMessage.threadId { + threadMessageId = MessageId(peerId: replyMessage.id.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)) + } if quote == nil, replyToMessageId.messageId.peerId != peerId { let nsText = replyMessage.text as NSString var replyMedia: Media? @@ -712,14 +714,14 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, threadId = threadIdValue } else { if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { - threadId = makeMessageThreadId(replyToMessageId.messageId) + threadId = Int64(replyToMessageId.messageId.id) } } } else { threadId = threadIdValue } } else if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { - threadId = makeMessageThreadId(replyToMessageId.messageId) + threadId = Int64(replyToMessageId.messageId.id) } } } @@ -902,7 +904,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, } } else if let attribute = attribute as? ReplyMessageAttribute { if let threadMessageId = attribute.threadMessageId { - threadId = makeMessageThreadId(threadMessageId) + threadId = Int64(threadMessageId.id) } } else if let attribute = attribute as? SendAsMessageAttribute { if let peer = transaction.getPeer(attribute.peerId) { diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index 5e928d1c81..2ca7884bc2 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -320,7 +320,7 @@ private func sendUploadedMessageContent( var forwardSourceInfoAttribute: ForwardSourceInfoAttribute? var messageEntities: [Api.MessageEntity]? var replyMessageId: Int32? = threadId.flatMap { threadId in - makeThreadIdMessageId(peerId: peerId, threadId: threadId).id + return Int32(clamping: threadId) } var replyToStoryId: StoryId? var scheduleTime: Int32? diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 9853581311..0610c48e20 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1444,7 +1444,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: case let .updateChannelUserTyping(_, channelId, topMsgId, userId, type): if let date = updatesDate, date + 60 > serverTime { let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)) - let threadId = topMsgId.flatMap { makeMessageThreadId(MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: $0)) } + let threadId = topMsgId.flatMap { Int64($0) } let activity = PeerInputActivity(apiType: type, peerId: nil, timestamp: date) var category: PeerActivitySpace.Category = .global @@ -3480,16 +3480,16 @@ func replayFinalState( var removedCount: Int = 0 var peers: [ReplyThreadUserMessage] = [] } - var messageThreadStatsDifferences: [MessageId: MessageThreadStatsRecord] = [:] - func addMessageThreadStatsDifference(threadMessageId: MessageId, remove: Int, addedMessagePeer: PeerId?, addedMessageId: MessageId?, isOutgoing: Bool) { - if let value = messageThreadStatsDifferences[threadMessageId] { + var messageThreadStatsDifferences: [MessageThreadKey: MessageThreadStatsRecord] = [:] + func addMessageThreadStatsDifference(threadKey: MessageThreadKey, remove: Int, addedMessagePeer: PeerId?, addedMessageId: MessageId?, isOutgoing: Bool) { + if let value = messageThreadStatsDifferences[threadKey] { value.removedCount += remove if let addedMessagePeer = addedMessagePeer, let addedMessageId = addedMessageId { value.peers.append(ReplyThreadUserMessage(id: addedMessagePeer, messageId: addedMessageId, isOutgoing: isOutgoing)) } } else { let value = MessageThreadStatsRecord() - messageThreadStatsDifferences[threadMessageId] = value + messageThreadStatsDifferences[threadKey] = value value.removedCount = remove if let addedMessagePeer = addedMessagePeer, let addedMessageId = addedMessageId { value.peers.append(ReplyThreadUserMessage(id: addedMessagePeer, messageId: addedMessageId, isOutgoing: isOutgoing)) @@ -3540,10 +3540,9 @@ func replayFinalState( } } - let messageThreadId = makeThreadIdMessageId(peerId: message.id.peerId, threadId: threadId) if id.peerId.namespace == Namespaces.Peer.CloudChannel { if !transaction.messageExists(id: id) { - addMessageThreadStatsDifference(threadMessageId: messageThreadId, remove: 0, addedMessagePeer: message.authorId, addedMessageId: id, isOutgoing: !message.flags.contains(.Incoming)) + addMessageThreadStatsDifference(threadKey: MessageThreadKey(peerId: message.id.peerId, threadId: threadId), remove: 0, addedMessagePeer: message.authorId, addedMessageId: id, isOutgoing: !message.flags.contains(.Incoming)) } } @@ -3736,7 +3735,7 @@ func replayFinalState( deletedMessageIds.append(contentsOf: ids.map { .global($0) }) case let .DeleteMessages(ids): _internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, manualAddMessageThreadStatsDifference: { id, add, remove in - addMessageThreadStatsDifference(threadMessageId: id, remove: remove, addedMessagePeer: nil, addedMessageId: nil, isOutgoing: false) + addMessageThreadStatsDifference(threadKey: id, remove: remove, addedMessagePeer: nil, addedMessageId: nil, isOutgoing: false) }) deletedMessageIds.append(contentsOf: ids.map { .messageId($0) }) case let .UpdateMinAvailableMessage(id): @@ -4784,8 +4783,8 @@ func replayFinalState( // } // } - for (threadMessageId, difference) in messageThreadStatsDifferences { - updateMessageThreadStats(transaction: transaction, threadMessageId: threadMessageId, removedCount: difference.removedCount, addedMessagePeers: difference.peers) + for (threadKey, difference) in messageThreadStatsDifferences { + updateMessageThreadStats(transaction: transaction, threadKey: threadKey, removedCount: difference.removedCount, addedMessagePeers: difference.peers) } if !peerActivityTimestamps.isEmpty { diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index 9948ef3083..b630929c57 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -1088,7 +1088,7 @@ public final class AccountStateManager { for attr in first.attributes { if let attribute = attr as? ReplyMessageAttribute { if let threadId = attribute.threadMessageId { - threadData = transaction.getMessageHistoryThreadInfo(peerId: first.id.peerId, threadId: makeMessageThreadId(threadId))?.data.get(MessageHistoryThreadData.self) + threadData = transaction.getMessageHistoryThreadInfo(peerId: first.id.peerId, threadId: Int64(threadId.id))?.data.get(MessageHistoryThreadData.self) } } } @@ -1999,7 +1999,7 @@ public func messagesForNotification(transaction: Transaction, id: MessageId, alw } if let attribute = attribute as? ReplyMessageAttribute { if let threadId = attribute.threadMessageId { - threadData = transaction.getMessageHistoryThreadInfo(peerId: message.id.peerId, threadId: makeMessageThreadId(threadId))?.data.get(MessageHistoryThreadData.self) + threadData = transaction.getMessageHistoryThreadInfo(peerId: message.id.peerId, threadId: Int64(threadId.id))?.data.get(MessageHistoryThreadData.self) } } } diff --git a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift index 5f6ac5c48e..cd37ccc932 100644 --- a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift +++ b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift @@ -96,7 +96,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes var updatedTimestamp: Int32? if let apiMessage = apiMessage { switch apiMessage { - case let .message(_, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _): updatedTimestamp = date case .messageEmpty: break @@ -287,9 +287,8 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes if let updatedMessage = updatedMessage, case let .Id(updatedId) = updatedMessage.id { if message.id.namespace == Namespaces.Message.Local && updatedId.namespace == Namespaces.Message.Cloud && updatedId.peerId.namespace == Namespaces.Peer.CloudChannel { if let threadId = updatedMessage.threadId { - let messageThreadId = makeThreadIdMessageId(peerId: updatedMessage.id.peerId, threadId: threadId) if let authorId = updatedMessage.authorId { - updateMessageThreadStats(transaction: transaction, threadMessageId: messageThreadId, removedCount: 0, addedMessagePeers: [ReplyThreadUserMessage(id: authorId, messageId: updatedId, isOutgoing: true)]) + updateMessageThreadStats(transaction: transaction, threadKey: MessageThreadKey(peerId: updatedMessage.id.peerId, threadId: threadId), removedCount: 0, addedMessagePeers: [ReplyThreadUserMessage(id: authorId, messageId: updatedId, isOutgoing: true)]) } } } diff --git a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift index 266e2c6803..9a81013195 100644 --- a/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift +++ b/submodules/TelegramCore/Sources/State/HistoryViewStateValidation.swift @@ -234,7 +234,7 @@ final class HistoryViewStateValidationContexts { context.batchReferences[messageId] = batch } - disposable.set((validateReplyThreadMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: peerId, threadMessageId: makeThreadIdMessageId(peerId: peerId, threadId: threadId).id, tag: view.tagMask, messageIds: messages) + disposable.set((validateReplyThreadMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: peerId, threadMessageId: Int32(clamping: threadId), tag: view.tagMask, messageIds: messages) |> deliverOn(self.queue)).start(completed: { [weak self, weak batch] in if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch { var completedMessageIds: [MessageId] = [] @@ -512,7 +512,7 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac } else if tag == MessageTags.unseenReaction { requestSignal = network.request(Api.functions.messages.getUnreadReactions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1)) } else if let filter = messageFilterForTagMask(tag) { - requestSignal = network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) + requestSignal = network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) } else { assertionFailure() requestSignal = .complete() @@ -582,7 +582,7 @@ private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network var flags: Int32 = 0 flags |= (1 << 1) - requestSignal = network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, topMsgId: threadMessageId, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) + requestSignal = network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, topMsgId: threadMessageId, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) } else { return .complete() } @@ -621,7 +621,7 @@ private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network return .complete() } - return validateReplyThreadBatch(postbox: postbox, network: network, transaction: transaction, accountPeerId: accountPeerId, peerId: peerId, threadId: makeMessageThreadId(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: threadMessageId)), signal: signal, previous: previous, messageNamespace: Namespaces.Message.Cloud) + return validateReplyThreadBatch(postbox: postbox, network: network, transaction: transaction, accountPeerId: accountPeerId, peerId: peerId, threadId: Int64(threadMessageId), signal: signal, previous: previous, messageNamespace: Namespaces.Message.Cloud) } |> switchToLatest } diff --git a/submodules/TelegramCore/Sources/State/Holes.swift b/submodules/TelegramCore/Sources/State/Holes.swift index d8537f8d10..7d4c93c6ea 100644 --- a/submodules/TelegramCore/Sources/State/Holes.swift +++ b/submodules/TelegramCore/Sources/State/Holes.swift @@ -335,16 +335,29 @@ enum FetchMessageHistoryHoleThreadInput: CustomStringConvertible { } } - var requestThreadId: MessageId? { + func requestThreadId(accountPeerId: PeerId) -> Int64? { switch self { case let .direct(peerId, threadId): - if let threadId = threadId { - return makeThreadIdMessageId(peerId: peerId, threadId: threadId) + if let threadId = threadId, peerId != accountPeerId { + return threadId } else { return nil } case let .threadFromChannel(channelMessageId): - return channelMessageId + return Int64(channelMessageId.id) + } + } + + func requestSubPeerId(accountPeerId: PeerId) -> PeerId? { + switch self { + case let .direct(peerId, threadId): + if let threadId = threadId, peerId == accountPeerId { + return PeerId(threadId) + } else { + return nil + } + case .threadFromChannel: + return nil } } } @@ -360,10 +373,6 @@ struct FetchMessageHistoryHoleResult: Equatable { func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryHoleSource, postbox: Postbox, peerInput: FetchMessageHistoryHoleThreadInput, namespace: MessageId.Namespace, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, count rawCount: Int) -> Signal { let count = min(100, rawCount) - if peerInput.requestThreadId != nil, case .everywhere = space, case .aroundId = direction { - assert(true) - } - return postbox.stateView() |> mapToSignal { view -> Signal in if let state = view.state as? AuthorizedAccountState { @@ -374,15 +383,15 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH } |> take(1) |> mapToSignal { _ -> Signal in - return postbox.transaction { transaction -> (Peer?, Int64) in + return postbox.transaction { transaction -> (Peer?, Int64, Peer?) in switch peerInput { case let .direct(peerId, _): - return (transaction.getPeer(peerId), 0) + return (transaction.getPeer(peerId), 0, peerInput.requestSubPeerId(accountPeerId: accountPeerId).flatMap(transaction.getPeer)) case let .threadFromChannel(channelMessageId): - return (transaction.getPeer(channelMessageId.peerId), 0) + return (transaction.getPeer(channelMessageId.peerId), 0, nil) } } - |> mapToSignal { (peer, hash) -> Signal in + |> mapToSignal { (peer, hash, subPeer) -> Signal in guard let peer = peer else { return .single(FetchMessageHistoryHoleResult(removedIndices: IndexSet(), strictRemovedIndices: IndexSet(), actualPeerId: nil, actualThreadId: nil, ids: [])) } @@ -397,284 +406,345 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH let minMaxRange: ClosedRange switch space { - case .everywhere: - if let requestThreadId = peerInput.requestThreadId { - let offsetId: Int32 - let addOffset: Int32 - let selectedLimit = count - let maxId: Int32 - let minId: Int32 - - switch direction { - case let .range(start, end): - if start.id <= end.id { - offsetId = start.id <= 1 ? 1 : (start.id - 1) - addOffset = Int32(-selectedLimit) - maxId = end.id - minId = start.id - 1 - - let rangeStartId = start.id - let rangeEndId = min(end.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } else { - offsetId = start.id == Int32.max ? start.id : (start.id + 1) - addOffset = 0 - maxId = start.id == Int32.max ? start.id : (start.id + 1) - minId = end.id - - let rangeStartId = end.id - let rangeEndId = min(start.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } - case let .aroundId(id): - offsetId = id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 + case .everywhere: + if let requestThreadId = peerInput.requestThreadId(accountPeerId: accountPeerId) { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = count + let maxId: Int32 + let minId: Int32 + + switch direction { + case let .range(start, end): + if start.id <= end.id { + offsetId = start.id <= 1 ? 1 : (start.id - 1) + addOffset = Int32(-selectedLimit) + maxId = end.id + minId = start.id - 1 - minMaxRange = 1 ... (Int32.max - 1) - } - - request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: requestThreadId.id, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash)) - } else { - let offsetId: Int32 - let addOffset: Int32 - let selectedLimit = count - let maxId: Int32 - let minId: Int32 - - switch direction { - case let .range(start, end): - if start.id <= end.id { - offsetId = start.id <= 1 ? 1 : (start.id - 1) - addOffset = Int32(-selectedLimit) - maxId = end.id - minId = start.id - 1 - - let rangeStartId = start.id - let rangeEndId = min(end.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } else { - offsetId = start.id == Int32.max ? start.id : (start.id + 1) - addOffset = 0 - maxId = start.id == Int32.max ? start.id : (start.id + 1) - minId = end.id == 1 ? 0 : end.id - - let rangeStartId = end.id - let rangeEndId = min(start.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } - case let .aroundId(id): - offsetId = id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - minMaxRange = 1 ... Int32.max - 1 - } - - request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) - } - case let .tag(tag): - assert(tag.containsSingleElement) - if tag == .unseenPersonalMessage { - let offsetId: Int32 - let addOffset: Int32 - let selectedLimit = count - let maxId: Int32 - let minId: Int32 - - switch direction { - case let .range(start, end): - if start.id <= end.id { - offsetId = start.id <= 1 ? 1 : (start.id - 1) - addOffset = Int32(-selectedLimit) - maxId = end.id - minId = start.id - 1 - - let rangeStartId = start.id - let rangeEndId = min(end.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } else { - offsetId = start.id == Int32.max ? start.id : (start.id + 1) - addOffset = 0 - maxId = start.id == Int32.max ? start.id : (start.id + 1) - minId = end.id - - let rangeStartId = end.id - let rangeEndId = min(start.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } - case let .aroundId(id): - offsetId = id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 + let rangeStartId = start.id + let rangeEndId = min(end.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } else { + offsetId = start.id == Int32.max ? start.id : (start.id + 1) + addOffset = 0 + maxId = start.id == Int32.max ? start.id : (start.id + 1) + minId = end.id - minMaxRange = 1 ... Int32.max - 1 + let rangeStartId = end.id + let rangeEndId = min(start.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } } + case let .aroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 - var flags: Int32 = 0 - var topMsgId: Int32? - if let threadId = peerInput.requestThreadId { - flags |= (1 << 1) - topMsgId = threadId.id - } - - request = source.request(Api.functions.messages.getUnreadMentions(flags: flags, peer: inputPeer, topMsgId: topMsgId, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) - } else if tag == .unseenReaction { - let offsetId: Int32 - let addOffset: Int32 - let selectedLimit = count - let maxId: Int32 - let minId: Int32 - - switch direction { - case let .range(start, end): - if start.id <= end.id { - offsetId = start.id <= 1 ? 1 : (start.id - 1) - addOffset = Int32(-selectedLimit) - maxId = end.id - minId = start.id - 1 - - let rangeStartId = start.id - let rangeEndId = min(end.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } else { - offsetId = start.id == Int32.max ? start.id : (start.id + 1) - addOffset = 0 - maxId = start.id == Int32.max ? start.id : (start.id + 1) - minId = end.id - - let rangeStartId = end.id - let rangeEndId = min(start.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } - case let .aroundId(id): - offsetId = id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - - minMaxRange = 1 ... Int32.max - 1 - } - - var flags: Int32 = 0 - var topMsgId: Int32? - if let threadId = peerInput.requestThreadId { - flags |= (1 << 0) - topMsgId = threadId.id - } - - request = source.request(Api.functions.messages.getUnreadReactions(flags: flags, peer: inputPeer, topMsgId: topMsgId, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) - } else if tag == .liveLocation { - let selectedLimit = count - - switch direction { - case .aroundId, .range: - implicitelyFillHole = true - } minMaxRange = 1 ... (Int32.max - 1) - request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit), hash: 0)) - } else if let filter = messageFilterForTagMask(tag) { - let offsetId: Int32 - let addOffset: Int32 - let selectedLimit = count - let maxId: Int32 - let minId: Int32 - - switch direction { - case let .range(start, end): - if start.id <= end.id { - offsetId = start.id <= 1 ? 1 : (start.id - 1) - addOffset = Int32(-selectedLimit) - maxId = end.id - minId = start.id - 1 - - let rangeStartId = start.id - let rangeEndId = min(end.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } else { - offsetId = start.id == Int32.max ? start.id : (start.id + 1) - addOffset = 0 - maxId = start.id == Int32.max ? start.id : (start.id + 1) - minId = end.id - - let rangeStartId = end.id - let rangeEndId = min(start.id, Int32.max - 1) - if rangeStartId <= rangeEndId { - minMaxRange = rangeStartId ... rangeEndId - } else { - minMaxRange = rangeStartId ... rangeStartId - assertionFailure() - } - } - case let .aroundId(id): - offsetId = id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - - minMaxRange = 1 ... (Int32.max - 1) - } - - var flags: Int32 = 0 - var topMsgId: Int32? - if let threadId = peerInput.requestThreadId { - flags |= (1 << 1) - topMsgId = threadId.id - } - - request = source.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) - } else { - assertionFailure() - minMaxRange = 1 ... 1 - request = .never() } + + request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: Int32(clamping: requestThreadId), offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash)) + } else if let subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId) { + guard let subPeer, subPeer.id == subPeerId, let inputSubPeer = apiInputPeer(subPeer) else { + Logger.shared.log("fetchMessageHistoryHole", "subPeer not available") + return .never() + } + + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = count + let maxId: Int32 + let minId: Int32 + + switch direction { + case let .range(start, end): + if start.id <= end.id { + offsetId = start.id <= 1 ? 1 : (start.id - 1) + addOffset = Int32(-selectedLimit) + maxId = end.id + minId = start.id - 1 + + let rangeStartId = start.id + let rangeEndId = min(end.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } else { + offsetId = start.id == Int32.max ? start.id : (start.id + 1) + addOffset = 0 + maxId = start.id == Int32.max ? start.id : (start.id + 1) + minId = end.id + + let rangeStartId = end.id + let rangeEndId = min(start.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } + case let .aroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + + minMaxRange = 1 ... (Int32.max - 1) + } + + request = source.request(Api.functions.messages.getSavedHistory(peer: inputSubPeer, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash)) + } else { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = count + let maxId: Int32 + let minId: Int32 + + switch direction { + case let .range(start, end): + if start.id <= end.id { + offsetId = start.id <= 1 ? 1 : (start.id - 1) + addOffset = Int32(-selectedLimit) + maxId = end.id + minId = start.id - 1 + + let rangeStartId = start.id + let rangeEndId = min(end.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } else { + offsetId = start.id == Int32.max ? start.id : (start.id + 1) + addOffset = 0 + maxId = start.id == Int32.max ? start.id : (start.id + 1) + minId = end.id == 1 ? 0 : end.id + + let rangeStartId = end.id + let rangeEndId = min(start.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } + case let .aroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + minMaxRange = 1 ... Int32.max - 1 + } + + request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) + } + case let .tag(tag): + assert(tag.containsSingleElement) + if tag == .unseenPersonalMessage { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = count + let maxId: Int32 + let minId: Int32 + + switch direction { + case let .range(start, end): + if start.id <= end.id { + offsetId = start.id <= 1 ? 1 : (start.id - 1) + addOffset = Int32(-selectedLimit) + maxId = end.id + minId = start.id - 1 + + let rangeStartId = start.id + let rangeEndId = min(end.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } else { + offsetId = start.id == Int32.max ? start.id : (start.id + 1) + addOffset = 0 + maxId = start.id == Int32.max ? start.id : (start.id + 1) + minId = end.id + + let rangeStartId = end.id + let rangeEndId = min(start.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } + case let .aroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + + minMaxRange = 1 ... Int32.max - 1 + } + + var flags: Int32 = 0 + var topMsgId: Int32? + if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId) { + flags |= (1 << 1) + topMsgId = Int32(clamping: threadId) + } + + request = source.request(Api.functions.messages.getUnreadMentions(flags: flags, peer: inputPeer, topMsgId: topMsgId, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) + } else if tag == .unseenReaction { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = count + let maxId: Int32 + let minId: Int32 + + switch direction { + case let .range(start, end): + if start.id <= end.id { + offsetId = start.id <= 1 ? 1 : (start.id - 1) + addOffset = Int32(-selectedLimit) + maxId = end.id + minId = start.id - 1 + + let rangeStartId = start.id + let rangeEndId = min(end.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } else { + offsetId = start.id == Int32.max ? start.id : (start.id + 1) + addOffset = 0 + maxId = start.id == Int32.max ? start.id : (start.id + 1) + minId = end.id + + let rangeStartId = end.id + let rangeEndId = min(start.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } + case let .aroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + + minMaxRange = 1 ... Int32.max - 1 + } + + var flags: Int32 = 0 + var topMsgId: Int32? + if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId) { + flags |= (1 << 0) + topMsgId = Int32(clamping: threadId) + } + + request = source.request(Api.functions.messages.getUnreadReactions(flags: flags, peer: inputPeer, topMsgId: topMsgId, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) + } else if tag == .liveLocation { + let selectedLimit = count + + switch direction { + case .aroundId, .range: + implicitelyFillHole = true + } + minMaxRange = 1 ... (Int32.max - 1) + request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit), hash: 0)) + } else if let filter = messageFilterForTagMask(tag) { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = count + let maxId: Int32 + let minId: Int32 + + switch direction { + case let .range(start, end): + if start.id <= end.id { + offsetId = start.id <= 1 ? 1 : (start.id - 1) + addOffset = Int32(-selectedLimit) + maxId = end.id + minId = start.id - 1 + + let rangeStartId = start.id + let rangeEndId = min(end.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } else { + offsetId = start.id == Int32.max ? start.id : (start.id + 1) + addOffset = 0 + maxId = start.id == Int32.max ? start.id : (start.id + 1) + minId = end.id + + let rangeStartId = end.id + let rangeEndId = min(start.id, Int32.max - 1) + if rangeStartId <= rangeEndId { + minMaxRange = rangeStartId ... rangeEndId + } else { + minMaxRange = rangeStartId ... rangeStartId + assertionFailure() + } + } + case let .aroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + + minMaxRange = 1 ... (Int32.max - 1) + } + + var flags: Int32 = 0 + var topMsgId: Int32? + if let threadId = peerInput.requestThreadId(accountPeerId: accountPeerId) { + flags |= (1 << 1) + topMsgId = Int32(clamping: threadId) + } + + var savedPeerId: Api.InputPeer? + if let subPeerId = peerInput.requestSubPeerId(accountPeerId: accountPeerId), let subPeer = subPeer, subPeer.id == subPeerId { + if let inputPeer = apiInputPeer(subPeer) { + flags |= 1 << 2 + savedPeerId = inputPeer + } + } + + request = source.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: savedPeerId, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) + } else { + assertionFailure() + minMaxRange = 1 ... 1 + request = .never() + } } return request @@ -784,7 +854,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH } print("fetchMessageHistoryHole for \(peerInput) space \(space) done") - if peerInput.requestThreadId != nil, case .everywhere = space, case .aroundId = direction { + if peerInput.requestThreadId(accountPeerId: accountPeerId) != nil, case .everywhere = space, case .aroundId = direction { assert(true) } @@ -797,7 +867,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH case let .aroundId(aroundId): filledRange = min(aroundId.id, messageRange.lowerBound) ... max(aroundId.id, messageRange.upperBound) strictFilledIndices = IndexSet(integersIn: Int(min(aroundId.id, messageRange.lowerBound)) ... Int(max(aroundId.id, messageRange.upperBound))) - if peerInput.requestThreadId != nil { + if peerInput.requestThreadId(accountPeerId: accountPeerId) != nil { if ids.count <= count / 2 - 1 { filledRange = minMaxRange } @@ -973,7 +1043,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId offset = single((holeIndex.timestamp, min(holeIndex.id.id, Int32.max - 1) + 1, Api.InputPeer.inputPeerEmpty), NoError.self) return offset |> mapToSignal { (timestamp, id, peer) -> Signal in - let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, topMsgId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0, hash: 0)) + let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, savedPeerId: nil, topMsgId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0, hash: 0)) |> retryRequest |> mapToSignal { result -> Signal in let messages: [Api.Message] diff --git a/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift b/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift index 8b8a6809a0..c2fa13e3c8 100644 --- a/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift +++ b/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift @@ -177,11 +177,11 @@ private func requestActivity(postbox: Postbox, network: Network, accountPeerId: if let inputPeer = apiInputPeer(peer) { var flags: Int32 = 0 - let topMessageId = threadId.flatMap { makeThreadIdMessageId(peerId: peerId, threadId: $0) } + let topMessageId = threadId.flatMap { Int32(clamping: $0) } if topMessageId != nil { flags |= 1 << 0 } - return network.request(Api.functions.messages.setTyping(flags: flags, peer: inputPeer, topMsgId: topMessageId?.id, action: actionFromActivity(activity))) + return network.request(Api.functions.messages.setTyping(flags: flags, peer: inputPeer, topMsgId: topMessageId, action: actionFromActivity(activity))) |> `catch` { _ -> Signal in return .single(.boolFalse) } diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index 1db7e2bbec..d02ad2476c 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 169 + return 170 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/State/UpdateMessageService.swift b/submodules/TelegramCore/Sources/State/UpdateMessageService.swift index ea5a7ddb52..6d559c42e3 100644 --- a/submodules/TelegramCore/Sources/State/UpdateMessageService.swift +++ b/submodules/TelegramCore/Sources/State/UpdateMessageService.swift @@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService { self.putNext(groups) } case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod): - let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: .peerUser(userId: fromId), peerId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod) + let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: .peerUser(userId: fromId), peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod) 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 { @@ -74,7 +74,7 @@ class UpdateMessageService: NSObject, MTMessageService { let generatedPeerId = Api.Peer.peerUser(userId: userId) - let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, peerId: generatedPeerId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod) + let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod) 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/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift index 534baeba6b..b1eedff4e5 100644 --- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift @@ -104,7 +104,7 @@ extension Api.MessageMedia { extension Api.Message { var rawId: Int32 { switch self { - case let .message(_, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return id case let .messageEmpty(_, id, _): return id @@ -115,7 +115,7 @@ extension Api.Message { func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? { switch self { - case let .message(_, id, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, id, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let peerId: PeerId = messagePeerId.peerId return MessageId(peerId: peerId, namespace: namespace, id: id) case let .messageEmpty(_, id, peerId): @@ -132,7 +132,7 @@ extension Api.Message { var peerId: PeerId? { switch self { - case let .message(_, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let peerId: PeerId = messagePeerId.peerId return peerId case let .messageEmpty(_, _, peerId): @@ -145,7 +145,7 @@ extension Api.Message { var timestamp: Int32? { switch self { - case let .message(_, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _): return date case let .messageService(_, _, _, _, _, date, _, _): return date @@ -156,7 +156,7 @@ extension Api.Message { var preCachedResources: [(MediaResource, Data)]? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _): return media?.preCachedResources default: return nil @@ -165,7 +165,7 @@ extension Api.Message { var preCachedStories: [StoryId: Api.StoryItem]? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _): return media?.preCachedStories default: return nil diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift index b5918615dd..d4d99db781 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift @@ -138,12 +138,3 @@ public class ReplyStoryAttribute: MessageAttribute { encoder.encode(self.storyId, forKey: "i") } } - -public extension Message { - var effectiveReplyThreadMessageId: MessageId? { - if let threadId = self.threadId { - return makeThreadIdMessageId(peerId: self.id.peerId, threadId: threadId) - } - return nil - } -} diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift index 566c9ca0bd..f939085aa8 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_StandaloneAccountTransaction.swift @@ -190,6 +190,13 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { } } return false + }, + automaticThreadIndexInfo: { peerId, _ in + if peerId.namespace == Namespaces.Peer.CloudUser { + return StoredMessageHistoryThreadInfo(data: CodableEntry(data: Data()), summary: StoredMessageHistoryThreadInfo.Summary(totalUnreadCount: 0, mutedUntil: nil)) + } else { + return nil + } } ) }() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/DeleteMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/DeleteMessages.swift index 4c8c2638ee..fd85bd5d4b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/DeleteMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/DeleteMessages.swift @@ -22,7 +22,7 @@ func addMessageMediaResourceIdsToRemove(message: Message, resourceIds: inout [Me } } -public func _internal_deleteMessages(transaction: Transaction, mediaBox: MediaBox, ids: [MessageId], deleteMedia: Bool = true, manualAddMessageThreadStatsDifference: ((MessageId, Int, Int) -> Void)? = nil) { +public func _internal_deleteMessages(transaction: Transaction, mediaBox: MediaBox, ids: [MessageId], deleteMedia: Bool = true, manualAddMessageThreadStatsDifference: ((MessageThreadKey, Int, Int) -> Void)? = nil) { var resourceIds: [MediaResourceId] = [] if deleteMedia { for id in ids { @@ -40,12 +40,12 @@ public func _internal_deleteMessages(transaction: Transaction, mediaBox: MediaBo if id.peerId.namespace == Namespaces.Peer.CloudChannel && id.namespace == Namespaces.Message.Cloud { if let message = transaction.getMessage(id) { if let threadId = message.threadId { - let messageThreadId = makeThreadIdMessageId(peerId: message.id.peerId, threadId: threadId) + let messageThreadKey = MessageThreadKey(peerId: message.id.peerId, threadId: threadId) if id.peerId.namespace == Namespaces.Peer.CloudChannel { if let manualAddMessageThreadStatsDifference = manualAddMessageThreadStatsDifference { - manualAddMessageThreadStatsDifference(messageThreadId, 0, 1) + manualAddMessageThreadStatsDifference(messageThreadKey, 0, 1) } else { - updateMessageThreadStats(transaction: transaction, threadMessageId: messageThreadId, removedCount: 1, addedMessagePeers: []) + updateMessageThreadStats(transaction: transaction, threadKey: messageThreadKey, removedCount: 1, addedMessagePeers: []) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift index a55459cb39..0695e05c0c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift @@ -17,7 +17,8 @@ private struct DiscussionMessage { private class ReplyThreadHistoryContextImpl { private let queue: Queue private let account: Account - private let messageId: MessageId + private let peerId: PeerId + private let threadId: Int64 private var currentHole: (MessageHistoryHolesViewEntry, Disposable)? @@ -68,7 +69,10 @@ private class ReplyThreadHistoryContextImpl { init(queue: Queue, account: Account, data: ChatReplyThreadMessage) { self.queue = queue self.account = account - self.messageId = data.messageId + self.peerId = data.peerId + self.threadId = data.threadId + + let referencedMessageId = MessageId(peerId: data.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: data.threadId)) self.maxReadOutgoingMessageIdValue = data.maxReadOutgoingMessageId self.maxReadOutgoingMessageId.set(.single(self.maxReadOutgoingMessageIdValue)) @@ -79,7 +83,7 @@ private class ReplyThreadHistoryContextImpl { self.unreadCount.set(.single(self.unreadCountValue)) self.initialStateDisposable = (account.postbox.transaction { transaction -> State in - var indices = transaction.getThreadIndexHoles(peerId: data.messageId.peerId, threadId: makeMessageThreadId(data.messageId), namespace: Namespaces.Message.Cloud) + var indices = transaction.getThreadIndexHoles(peerId: data.peerId, threadId: data.threadId, namespace: Namespaces.Message.Cloud) indices.subtract(data.initialFilledHoles) /*let isParticipant = transaction.getPeerChatListIndex(data.messageId.peerId) != nil @@ -96,7 +100,7 @@ private class ReplyThreadHistoryContextImpl { indices.removeAll() } - return State(messageId: data.messageId, holeIndices: [Namespaces.Message.Cloud: indices], maxReadIncomingMessageId: data.maxReadIncomingMessageId, maxReadOutgoingMessageId: data.maxReadOutgoingMessageId) + return State(messageId: referencedMessageId, holeIndices: [Namespaces.Message.Cloud: indices], maxReadIncomingMessageId: data.maxReadIncomingMessageId, maxReadOutgoingMessageId: data.maxReadOutgoingMessageId) } |> deliverOn(self.queue)).start(next: { [weak self] state in guard let strongSelf = self else { @@ -106,7 +110,7 @@ private class ReplyThreadHistoryContextImpl { strongSelf.state.set(.single(state)) }) - let threadId = makeMessageThreadId(messageId) + let threadId = self.threadId self.holesDisposable = (account.postbox.messageHistoryHolesView() |> map { view -> MessageHistoryHolesViewEntry? in @@ -133,15 +137,15 @@ private class ReplyThreadHistoryContextImpl { guard let strongSelf = self else { return } - if let value = outgoing[data.messageId] { - strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: data.messageId.peerId, namespace: Namespaces.Message.Cloud, id: value) + if let value = outgoing[referencedMessageId] { + strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: data.peerId, namespace: Namespaces.Message.Cloud, id: value) } }) let accountPeerId = account.peerId let updateInitialState: Signal = account.postbox.transaction { transaction -> Peer? in - return transaction.getPeer(data.messageId.peerId) + return transaction.getPeer(data.peerId) } |> castError(FetchChannelReplyThreadMessageError.self) |> mapToSignal { peer -> Signal in @@ -151,8 +155,8 @@ private class ReplyThreadHistoryContextImpl { guard let inputPeer = apiInputPeer(peer) else { return .fail(.generic) } - - return account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: data.messageId.id)) + + return account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: Int32(clamping: data.threadId))) |> mapError { _ -> FetchChannelReplyThreadMessageError in return .generic } @@ -308,9 +312,10 @@ private class ReplyThreadHistoryContextImpl { } func applyMaxReadIndex(messageIndex: MessageIndex) { - let messageId = self.messageId + let peerId = self.peerId + let threadId = self.threadId - if messageIndex.id.namespace != messageId.namespace { + if messageIndex.id.namespace != Namespaces.Message.Cloud { return } @@ -326,16 +331,16 @@ private class ReplyThreadHistoryContextImpl { let account = self.account let _ = (self.account.postbox.transaction { transaction -> (Api.InputPeer?, MessageId?, Int?) in - if var data = transaction.getMessageHistoryThreadInfo(peerId: messageId.peerId, threadId: Int64(messageId.id))?.data.get(MessageHistoryThreadData.self) { + if var data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) { if messageIndex.id.id >= data.maxIncomingReadId { - if let count = transaction.getThreadMessageCount(peerId: messageId.peerId, threadId: Int64(messageId.id), namespace: messageId.namespace, fromIdExclusive: data.maxIncomingReadId, toIndex: messageIndex) { + if let count = transaction.getThreadMessageCount(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, fromIdExclusive: data.maxIncomingReadId, toIndex: messageIndex) { data.incomingUnreadCount = max(0, data.incomingUnreadCount - Int32(count)) data.maxIncomingReadId = messageIndex.id.id } - if let topMessageIndex = transaction.getMessageHistoryThreadTopMessage(peerId: messageId.peerId, threadId: Int64(messageId.id), namespaces: Set([Namespaces.Message.Cloud])) { + if let topMessageIndex = transaction.getMessageHistoryThreadTopMessage(peerId: peerId, threadId: threadId, namespaces: Set([Namespaces.Message.Cloud])) { if messageIndex.id.id >= topMessageIndex.id.id { - let containingHole = transaction.getThreadIndexHole(peerId: messageId.peerId, threadId: Int64(messageId.id), namespace: topMessageIndex.id.namespace, containing: topMessageIndex.id.id) + let containingHole = transaction.getThreadIndexHole(peerId: peerId, threadId: threadId, namespace: topMessageIndex.id.namespace, containing: topMessageIndex.id.id) if let _ = containingHole[.everywhere] { } else { data.incomingUnreadCount = 0 @@ -346,12 +351,13 @@ private class ReplyThreadHistoryContextImpl { data.maxKnownMessageId = max(data.maxKnownMessageId, messageIndex.id.id) if let entry = StoredMessageHistoryThreadInfo(data) { - transaction.setMessageHistoryThreadInfo(peerId: messageId.peerId, threadId: Int64(messageId.id), info: entry) + transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: entry) } } } - if let message = transaction.getMessage(messageId) { + let referencedMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)) + if let message = transaction.getMessage(referencedMessageId) { for attribute in message.attributes { if let attribute = attribute as? SourceReferenceMessageAttribute { if let sourceMessage = transaction.getMessage(attribute.messageId) { @@ -388,8 +394,8 @@ private class ReplyThreadHistoryContextImpl { } let inputPeer = transaction.getPeer(messageIndex.id.peerId).flatMap(apiInputPeer) - let readCount = transaction.getThreadMessageCount(peerId: messageId.peerId, threadId: makeMessageThreadId(messageId), namespace: messageId.namespace, fromIdExclusive: fromIdExclusive, toIndex: toIndex) - let topMessageId = transaction.getMessagesWithThreadId(peerId: messageId.peerId, namespace: messageId.namespace, threadId: makeMessageThreadId(messageId), from: MessageIndex.upperBound(peerId: messageId.peerId, namespace: messageId.namespace), includeFrom: false, to: MessageIndex.lowerBound(peerId: messageId.peerId, namespace: messageId.namespace), limit: 1).first?.id + let readCount = transaction.getThreadMessageCount(peerId: peerId, threadId: threadId, namespace: Namespaces.Message.Cloud, fromIdExclusive: fromIdExclusive, toIndex: toIndex) + let topMessageId = transaction.getMessagesWithThreadId(peerId: peerId, namespace: Namespaces.Message.Cloud, threadId: threadId, from: MessageIndex.upperBound(peerId: peerId, namespace: Namespaces.Message.Cloud), includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: Namespaces.Message.Cloud), limit: 1).first?.id return (inputPeer, topMessageId, readCount) } @@ -434,17 +440,17 @@ private class ReplyThreadHistoryContextImpl { } } - var signal = strongSelf.account.network.request(Api.functions.messages.readDiscussion(peer: inputPeer, msgId: messageId.id, readMaxId: messageIndex.id.id)) + var signal = strongSelf.account.network.request(Api.functions.messages.readDiscussion(peer: inputPeer, msgId: Int32(clamping: threadId), readMaxId: messageIndex.id.id)) |> `catch` { _ -> Signal in return .single(.boolFalse) } |> ignoreValues if revalidate { - let validateSignal = strongSelf.account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: messageId.id)) + let validateSignal = strongSelf.account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: Int32(clamping: threadId))) |> map { result -> (MessageId?, Int) in switch result { case let .discussionMessage(_, _, _, readInboxMaxId, _, unreadCount, _, _): - return (readInboxMaxId.flatMap({ MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: $0) }), Int(unreadCount)) + return (readInboxMaxId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }), Int(unreadCount)) } } |> `catch` { _ -> Signal<(MessageId?, Int)?, NoError> in @@ -494,7 +500,7 @@ public class ReplyThreadHistoryContext { self.impl.with { impl in let stateDisposable = impl.state.get().start(next: { state in subscriber.putNext(MessageHistoryViewExternalInput( - content: .thread(peerId: state.messageId.peerId, id: makeMessageThreadId(state.messageId), holes: state.holeIndices), + content: .thread(peerId: state.messageId.peerId, id: Int64(state.messageId.id), holes: state.holeIndices), maxReadIncomingMessageId: state.maxReadIncomingMessageId, maxReadOutgoingMessageId: state.maxReadOutgoingMessageId )) @@ -554,7 +560,7 @@ public struct ChatReplyThreadMessage: Equatable { case lowerBoundMessage(MessageIndex) } - public var messageId: MessageId + public var peerId: PeerId public var threadId: Int64 public var channelMessageId: MessageId? public var isChannelPost: Bool @@ -567,8 +573,16 @@ public struct ChatReplyThreadMessage: Equatable { public var initialAnchor: Anchor public var isNotAvailable: Bool - public init(messageId: MessageId, threadId: Int64, channelMessageId: MessageId?, isChannelPost: Bool, isForumPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, unreadCount: Int, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) { - self.messageId = messageId + public var effectiveMessageId: MessageId? { + if self.peerId.namespace == Namespaces.Peer.CloudChannel { + return MessageId(peerId: self.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: self.threadId)) + } else { + return nil + } + } + + public init(peerId: PeerId, threadId: Int64, channelMessageId: MessageId?, isChannelPost: Bool, isForumPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, unreadCount: Int, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) { + self.peerId = peerId self.threadId = threadId self.channelMessageId = channelMessageId self.isChannelPost = isChannelPost @@ -584,7 +598,7 @@ public struct ChatReplyThreadMessage: Equatable { public var normalized: ChatReplyThreadMessage { if self.isForumPost { - return ChatReplyThreadMessage(messageId: self.messageId, threadId: self.threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false) + return ChatReplyThreadMessage(peerId: self.peerId, threadId: self.threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false) } else { return self } @@ -760,32 +774,6 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa |> mapToSignal { threadData -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, Anchor, MessageId?), FetchChannelReplyThreadMessageError> in if let _ = threadData, !"".isEmpty { return .fail(.generic) - /*return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, Anchor, MessageId?) in - var threadInput: FetchMessageHistoryHoleThreadInput = .threadFromChannel(channelMessageId: messageId) - var threadMessageId: MessageId? - transaction.scanMessageAttributes(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in - for attribute in attributes { - if let attribute = attribute as? SourceReferenceMessageAttribute { - if attribute.messageId == messageId { - threadMessageId = id - threadInput = .direct(peerId: id.peerId, threadId: makeMessageThreadId(id)) - return false - } - } - } - return true - }) - let anchor: Anchor - if let atMessageId = atMessageId { - anchor = .message(atMessageId) - } else if let maxReadIncomingMessageId = replyInfo.maxReadIncomingMessageId { - anchor = .message(maxReadIncomingMessageId) - } else { - anchor = .lowerBound - } - return (threadInput, replyInfo.commentsPeerId, threadMessageId, anchor, replyInfo.maxMessageId) - } - |> castError(FetchChannelReplyThreadMessageError.self)*/ } else { return discussionMessage.get() |> take(1) @@ -805,7 +793,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa } else { anchor = .lowerBound } - return .single((.direct(peerId: commentsPeerId, threadId: makeMessageThreadId(topMessageId)), commentsPeerId, discussionMessage.messageId, anchor, discussionMessage.maxMessage)) + return .single((.direct(peerId: commentsPeerId, threadId: Int64(topMessageId.id)), commentsPeerId, discussionMessage.messageId, anchor, discussionMessage.maxMessage)) } } } @@ -820,7 +808,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa } return account.postbox.transaction { transaction -> Signal<(FetchMessageHistoryHoleResult?, ChatReplyThreadMessage.Anchor), FetchChannelReplyThreadMessageError> in if let threadMessageId = threadMessageId { - var holes = transaction.getThreadIndexHoles(peerId: threadMessageId.peerId, threadId: makeMessageThreadId(threadMessageId), namespace: Namespaces.Message.Cloud) + var holes = transaction.getThreadIndexHoles(peerId: threadMessageId.peerId, threadId: Int64(threadMessageId.id), namespace: Namespaces.Message.Cloud) holes.remove(integersIn: Int(maxMessageId.id + 1) ..< Int(Int32.max)) let isParticipant = transaction.getPeerChatListIndex(commentsPeerId) != nil @@ -843,7 +831,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa input: .external(MessageHistoryViewExternalInput( content: .thread( peerId: commentsPeerId, - id: makeMessageThreadId(threadMessageId), + id: Int64(threadMessageId.id), holes: [ Namespaces.Message.Cloud: holes ] @@ -932,12 +920,12 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa return account.postbox.transaction { transaction -> Signal in if let initialFilledHoles = initialFilledHoles { for range in initialFilledHoles.strictRemovedIndices.rangeView { - transaction.removeHole(peerId: discussionMessage.messageId.peerId, threadId: makeMessageThreadId(discussionMessage.messageId), namespace: Namespaces.Message.Cloud, space: .everywhere, range: Int32(range.lowerBound) ... Int32(range.upperBound)) + transaction.removeHole(peerId: discussionMessage.messageId.peerId, threadId: Int64(discussionMessage.messageId.id), namespace: Namespaces.Message.Cloud, space: .everywhere, range: Int32(range.lowerBound) ... Int32(range.upperBound)) } } return .single(ChatReplyThreadMessage( - messageId: discussionMessage.messageId, + peerId: discussionMessage.messageId.peerId, threadId: Int64(discussionMessage.messageId.id), channelMessageId: discussionMessage.channelMessageId, isChannelPost: discussionMessage.isChannelPost, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift index 2793b3d89c..7b8ad48961 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SearchMessages.swift @@ -8,7 +8,7 @@ import MtProtoKit public enum SearchMessagesLocation: Equatable { case general(tags: MessageTags?, minDate: Int32?, maxDate: Int32?) case group(groupId: PeerGroupId, tags: MessageTags?, minDate: Int32?, maxDate: Int32?) - case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?, topMsgId: MessageId?, minDate: Int32?, maxDate: Int32?) + case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?, threadId: Int64?, minDate: Int32?, maxDate: Int32?) case sentMedia(tags: MessageTags?) } @@ -139,7 +139,7 @@ private func mergedState(transaction: Transaction, seedConfiguration: SeedConfig for attribute in renderedMessage.attributes { if let attribute = attribute as? ReplyMessageAttribute { if let threadMessageId = attribute.threadMessageId { - let threadId = makeMessageThreadId(threadMessageId) + let threadId = Int64(threadMessageId.id) if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) { threadInfo[renderedMessage.id] = data break @@ -224,7 +224,7 @@ private func mergedResult(_ state: SearchMessagesState) -> SearchMessagesResult func _internal_searchMessages(account: Account, location: SearchMessagesLocation, query: String, state: SearchMessagesState?, limit: Int32 = 100) -> Signal<(SearchMessagesResult, SearchMessagesState), NoError> { let remoteSearchResult: Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> switch location { - case let .peer(peerId, fromId, tags, topMsgId, minDate, maxDate): + case let .peer(peerId, fromId, tags, threadId, minDate, maxDate): if peerId.namespace == Namespaces.Peer.SecretChat { return account.postbox.transaction { transaction -> (SearchMessagesResult, SearchMessagesState) in var readStates: [PeerId: CombinedPeerReadState] = [:] @@ -238,7 +238,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation for attribute in message.attributes { if let attribute = attribute as? ReplyMessageAttribute { if let threadMessageId = attribute.threadMessageId { - let threadId = makeMessageThreadId(threadMessageId) + let threadId = Int64(threadMessageId.id) if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) { threadInfo[message.id] = data break @@ -253,7 +253,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation } let filter: Api.MessagesFilter = tags.flatMap { messageFilterForTagMask($0) } ?? .inputMessagesFilterEmpty - remoteSearchResult = account.postbox.transaction { transaction -> (peer: Peer, additionalPeer: Peer?, from: Peer?)? in + remoteSearchResult = account.postbox.transaction { transaction -> (peer: Peer, additionalPeer: Peer?, from: Peer?, subPeer: Peer?)? in guard let peer = transaction.getPeer(peerId) else { return nil } @@ -261,10 +261,12 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation if let _ = peer as? TelegramChannel, let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let migrationReference = cachedData.migrationReference { additionalPeer = transaction.getPeer(migrationReference.maxMessageId.peerId) } - if let fromId = fromId { - return (peer: peer, additionalPeer: additionalPeer, from: transaction.getPeer(fromId)) + var subPeer: Peer? + if peerId == account.peerId, let threadId = threadId { + subPeer = transaction.getPeer(PeerId(threadId)) } - return (peer: peer, additionalPeer: additionalPeer, from: nil) + + return (peer: peer, additionalPeer: additionalPeer, from: fromId.flatMap(transaction.getPeer), subPeer) } |> mapToSignal { values -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in guard let values = values else { @@ -282,8 +284,18 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation flags |= (1 << 0) } } - if let _ = topMsgId { + var inputSavedPeer: Api.InputPeer? = nil + if let subPeer = values.subPeer { + if let inputPeer = apiInputPeer(subPeer) { + inputSavedPeer = inputPeer + flags |= (1 << 2) + } + } + var topMsgId: Int32? + if peerId == account.peerId { + } else if let threadId = threadId { flags |= (1 << 1) + topMsgId = Int32(clamping: threadId) } let peerMessages: Signal if let completed = state?.main.completed, completed { @@ -294,7 +306,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation if peer.id.namespace == Namespaces.Peer.CloudChannel && query.isEmpty && fromId == nil && tags == nil && minDate == nil && maxDate == nil { signal = account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: lowerBound?.id.id ?? 0, offsetDate: 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) } else { - signal = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, topMsgId: topMsgId?.id, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) + signal = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, savedPeerId: inputSavedPeer, topMsgId: topMsgId, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) } peerMessages = signal |> map(Optional.init) @@ -310,7 +322,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation additionalPeerMessages = .single(nil) } else if mainCompleted || !hasAdditional { let lowerBound = state?.additional?.messages.last.flatMap({ $0.index }) - additionalPeerMessages = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, topMsgId: topMsgId?.id, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) + additionalPeerMessages = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputPeer, savedPeerId: inputSavedPeer, topMsgId: topMsgId, filter: filter, minDate: minDate ?? 0, maxDate: maxDate ?? (Int32.max - 1), offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -397,7 +409,7 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation for attribute in message.attributes { if let attribute = attribute as? ReplyMessageAttribute { if let threadMessageId = attribute.threadMessageId { - let threadId = makeMessageThreadId(threadMessageId) + let threadId = Int64(threadMessageId.id) if let data = transaction.getMessageHistoryThreadInfo(peerId: message.id.peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) { threadInfo[message.id] = data break @@ -603,10 +615,11 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre return .single(transaction.findClosestMessageIdByTimestamp(peerId: peerId, timestamp: timestamp)) } else if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { if let threadId = threadId { - let primaryIndex = account.network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: makeThreadIdMessageId(peerId: peerId, threadId: threadId).id, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0)) - |> map { result -> MessageIndex? in - let messages: [Api.Message] - switch result { + if peerId.namespace == Namespaces.Peer.CloudChannel { + let primaryIndex = account.network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: Int32(clamping: threadId), offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0)) + |> map { result -> MessageIndex? in + let messages: [Api.Message] + switch result { case let .messages(apiMessages, _, _): messages = apiMessages case let .channelMessages(_, _, _, _, apiMessages, _, _, _): @@ -615,21 +628,55 @@ func _internal_searchMessageIdByTimestamp(account: Account, peerId: PeerId, thre messages = apiMessages case .messagesNotModified: messages = [] - } - for message in messages { - if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForum) { - return message.index } + for message in messages { + if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForum) { + return message.index + } + } + return nil } - return nil - } - |> `catch` { _ -> Signal in + |> `catch` { _ -> Signal in + return .single(nil) + } + return primaryIndex + |> map { primaryIndex -> MessageId? in + return primaryIndex?.id + } + } else if peerId == account.peerId { + guard let subPeer = transaction.getPeer(PeerId(threadId)), let inputSubPeer = apiInputPeer(subPeer) else { + return .single(nil) + } + let primaryIndex = account.network.request(Api.functions.messages.getSavedHistory(peer: inputSubPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0)) + |> map { result -> MessageIndex? in + let messages: [Api.Message] + switch result { + case let .messages(apiMessages, _, _): + messages = apiMessages + case let .channelMessages(_, _, _, _, apiMessages, _, _, _): + messages = apiMessages + case let .messagesSlice(_, _, _, _, apiMessages, _, _): + messages = apiMessages + case .messagesNotModified: + messages = [] + } + for message in messages { + if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: peer.isForum) { + return message.index + } + } + return nil + } + |> `catch` { _ -> Signal in + return .single(nil) + } + return primaryIndex + |> map { primaryIndex -> MessageId? in + return primaryIndex?.id + } + } else { return .single(nil) } - return primaryIndex - |> map { primaryIndex -> MessageId? in - return primaryIndex?.id - } } else { var secondaryIndex: Signal = .single(nil) if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let migrationReference = cachedData.migrationReference, let secondaryPeer = transaction.getPeer(migrationReference.maxMessageId.peerId), let inputSecondaryPeer = apiInputPeer(secondaryPeer) { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift index 0ddbc9f351..b62f85bb8b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift @@ -105,14 +105,15 @@ public final class SparseMessageList { self.sparseItemsDisposable = (self.account.postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } - |> mapToSignal { inputPeer -> Signal in + |> mapToSignal { inputPeer -> Signal in guard let inputPeer = inputPeer else { return .single(SparseItems(items: [])) } guard let messageFilter = messageFilterForTagMask(messageTag) else { return .single(SparseItems(items: [])) } - return account.network.request(Api.functions.messages.getSearchResultsPositions(peer: inputPeer, filter: messageFilter, offsetId: 0, limit: 1000)) + //TODO:api + return account.network.request(Api.functions.messages.getSearchResultsPositions(flags: 0, peer: inputPeer, savedPeerId: nil, filter: messageFilter, offsetId: 0, limit: 1000)) |> map { result -> SparseItems in switch result { case let .searchResultsPositions(totalCount, positions): @@ -817,7 +818,8 @@ public final class SparseMessageCalendar { guard let messageFilter = messageFilterForTagMask(messageTag) else { return .single(LoadResult(messagesByDay: [:], nextOffset: nil, minMessageId: nil, minTimestamp: nil)) } - return self.account.network.request(Api.functions.messages.getSearchResultsCalendar(peer: inputPeer, filter: messageFilter, offsetId: nextRequestOffset, offsetDate: 0)) + //TODO:api + return self.account.network.request(Api.functions.messages.getSearchResultsCalendar(flags: 0, peer: inputPeer, savedPeerId: nil, filter: messageFilter, offsetId: nextRequestOffset, offsetDate: 0)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 58ef3e29a8..2c37eb7f79 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -415,10 +415,16 @@ public extension TelegramEngine { public func refreshMessageTagStats(peerId: EnginePeer.Id, threadId: Int64?, tags: [EngineMessage.Tags]) -> Signal { let account = self.account - return self.account.postbox.transaction { transaction -> Api.InputPeer? in - return transaction.getPeer(peerId).flatMap(apiInputPeer) + return self.account.postbox.transaction { transaction -> (Api.InputPeer?, Api.InputPeer?) in + var inputSavedPeer: Api.InputPeer? + if let threadId = threadId { + if peerId == account.peerId { + inputSavedPeer = transaction.getPeer(PeerId(threadId)).flatMap(apiInputPeer) + } + } + return (transaction.getPeer(peerId).flatMap(apiInputPeer), inputSavedPeer) } - |> mapToSignal { inputPeer -> Signal in + |> mapToSignal { inputPeer, inputSavedPeer -> Signal in guard let inputPeer = inputPeer else { return .complete() } @@ -432,11 +438,17 @@ public extension TelegramEngine { var flags: Int32 = 0 var topMsgId: Int32? if let threadId = threadId { - flags |= (1 << 1) - topMsgId = Int32(clamping: threadId) + if peerId == account.peerId { + if inputSavedPeer != nil { + flags |= (1 << 2) + } + } else { + flags |= (1 << 1) + topMsgId = Int32(clamping: threadId) + } } - signals.append(self.account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0)) + signals.append(self.account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: "", fromId: nil, savedPeerId: inputSavedPeer, topMsgId: topMsgId, filter: filter, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1, maxId: 0, minId: 0, hash: 0)) |> map { result -> (count: Int32?, topId: Int32?) in switch result { case let .messagesSlice(_, count, _, _, messages, _, _): diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift index 06eb921e8a..e96e1ee6ba 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RequestUserPhotos.swift @@ -68,7 +68,7 @@ func _internal_requestPeerPhotos(accountPeerId: PeerId, postbox: Postbox, networ } } } else if let peer = peer, let inputPeer = apiInputPeer(peer) { - return network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, topMsgId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0)) + return network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, savedPeerId: nil, topMsgId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/submodules/TelegramCore/Sources/Utils/UpdateMessageMedia.swift b/submodules/TelegramCore/Sources/Utils/UpdateMessageMedia.swift index a5d8c84011..a8e92b7032 100644 --- a/submodules/TelegramCore/Sources/Utils/UpdateMessageMedia.swift +++ b/submodules/TelegramCore/Sources/Utils/UpdateMessageMedia.swift @@ -34,12 +34,12 @@ struct ReplyThreadUserMessage { var isOutgoing: Bool } -func updateMessageThreadStats(transaction: Transaction, threadMessageId: MessageId, removedCount: Int, addedMessagePeers: [ReplyThreadUserMessage]) { - updateMessageThreadStatsInternal(transaction: transaction, threadMessageId: threadMessageId, removedCount: removedCount, addedMessagePeers: addedMessagePeers, allowChannel: false) +func updateMessageThreadStats(transaction: Transaction, threadKey: MessageThreadKey, removedCount: Int, addedMessagePeers: [ReplyThreadUserMessage]) { + updateMessageThreadStatsInternal(transaction: transaction, threadKey: threadKey, removedCount: removedCount, addedMessagePeers: addedMessagePeers, allowChannel: false) } -private func updateMessageThreadStatsInternal(transaction: Transaction, threadMessageId: MessageId, removedCount: Int, addedMessagePeers: [ReplyThreadUserMessage], allowChannel: Bool) { - guard let channel = transaction.getPeer(threadMessageId.peerId) as? TelegramChannel else { +private func updateMessageThreadStatsInternal(transaction: Transaction, threadKey: MessageThreadKey, removedCount: Int, addedMessagePeers: [ReplyThreadUserMessage], allowChannel: Bool) { + guard let channel = transaction.getPeer(threadKey.peerId) as? TelegramChannel else { return } var isGroup = true @@ -75,7 +75,7 @@ private func updateMessageThreadStatsInternal(transaction: Transaction, threadMe return current } - transaction.updateMessage(threadMessageId, update: { currentMessage in + transaction.updateMessage(MessageId(peerId: threadKey.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadKey.threadId)), update: { currentMessage in var attributes = currentMessage.attributes loop: for j in 0 ..< attributes.count { if let attribute = attributes[j] as? ReplyThreadMessageAttribute { @@ -117,6 +117,6 @@ private func updateMessageThreadStatsInternal(transaction: Transaction, threadMe }) if let channelThreadMessageId = channelThreadMessageId { - updateMessageThreadStatsInternal(transaction: transaction, threadMessageId: channelThreadMessageId, removedCount: removedCount, addedMessagePeers: addedMessagePeers, allowChannel: true) + updateMessageThreadStatsInternal(transaction: transaction, threadKey: MessageThreadKey(peerId: channelThreadMessageId.peerId, threadId: Int64(channelThreadMessageId.id)), removedCount: removedCount, addedMessagePeers: addedMessagePeers, allowChannel: true) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift index 0e18d44fe8..31675f6772 100644 --- a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift @@ -56,7 +56,7 @@ private func titleAndColorForAction(_ action: SubscriberAction, theme: Presentat } private func actionForPeer(context: AccountContext, peer: Peer, interfaceState: ChatPresentationInterfaceState, isJoining: Bool, isMuted: Bool) -> SubscriberAction? { - if case let .replyThread(message) = interfaceState.chatLocation, message.messageId.peerId == context.account.peerId { + if case let .replyThread(message) = interfaceState.chatLocation, message.peerId == context.account.peerId { if let peer = interfaceState.savedMessagesTopicPeer { if case let .channel(channel) = peer { if case .broadcast = channel.info { diff --git a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift index 2dd9173888..a6f74c60e9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift @@ -196,7 +196,7 @@ public final class ChatHistorySearchContainerNode: SearchDisplayControllerConten if let strongSelf = self { let signal: Signal<([ChatHistorySearchEntry], [MessageId: Message])?, NoError> if let query = query, !query.isEmpty { - let foundRemoteMessages: Signal<[Message], NoError> = context.engine.messages.searchMessages(location: .peer(peerId: peerId, fromId: nil, tags: tagMask, topMsgId: threadId.flatMap { makeThreadIdMessageId(peerId: peerId, threadId: $0) }, minDate: nil, maxDate: nil), query: query, state: nil) + let foundRemoteMessages: Signal<[Message], NoError> = context.engine.messages.searchMessages(location: .peer(peerId: peerId, fromId: nil, tags: tagMask, threadId: threadId, minDate: nil, maxDate: nil), query: query, state: nil) |> map { $0.0.messages } |> delay(0.2, queue: Queue.concurrentDefaultQueue()) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index b442a2b113..327955d2fa 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -313,7 +313,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { return false } - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId.peerId != item.content.firstMessage.id.peerId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.peerId != item.content.firstMessage.id.peerId { return false } @@ -826,8 +826,8 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { hasAvatar = true } case let .replyThread(replyThreadMessage): - if replyThreadMessage.messageId.peerId != item.context.account.peerId { - if replyThreadMessage.messageId.peerId.isGroupOrChannel && item.message.author != nil { + if replyThreadMessage.peerId != item.context.account.peerId { + if replyThreadMessage.peerId.isGroupOrChannel && item.message.author != nil { var isBroadcastChannel = false if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { isBroadcastChannel = true @@ -1102,7 +1102,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } if let replyAttribute = attribute as? ReplyMessageAttribute { - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, Int32(clamping: replyThreadMessage.threadId) == replyAttribute.messageId.id { } else { replyMessage = item.message.associatedMessages[replyAttribute.messageId] } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 3404f827fe..135bb3d145 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -1215,7 +1215,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } } - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId.peerId != item.content.firstMessage.id.peerId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.peerId != item.content.firstMessage.id.peerId { return false } @@ -1664,7 +1664,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI inlineBotNameString = attribute.title } } else if let attribute = attribute as? ReplyMessageAttribute { - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == attribute.messageId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, Int32(clamping: replyThreadMessage.threadId) == attribute.messageId.id { } else { replyMessage = firstMessage.associatedMessages[attribute.messageId] } @@ -4185,7 +4185,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let item = self.item { for attribute in item.message.attributes { if let attribute = attribute as? ReplyMessageAttribute { - if let threadId = item.message.threadId, makeThreadIdMessageId(peerId: item.message.id.peerId, threadId: threadId) == attribute.messageId, let quotedReply = item.message.attributes.first(where: { $0 is QuotedReplyMessageAttribute }) as? QuotedReplyMessageAttribute { + if let threadId = item.message.threadId, Int32(clamping: threadId) == attribute.messageId.id, let quotedReply = item.message.attributes.first(where: { $0 is QuotedReplyMessageAttribute }) as? QuotedReplyMessageAttribute { return .action(InternalBubbleTapAction.Action({ [weak self, weak replyInfoNode] in guard let self, let item = self.item, let replyInfoNode else { return diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift index e0e3ebe07f..55a4c35309 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift @@ -221,7 +221,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco return false } - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId.peerId != item.content.firstMessage.id.peerId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.peerId != item.content.firstMessage.id.peerId { return false } @@ -495,7 +495,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco } if let replyAttribute = attribute as? ReplyMessageAttribute { - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, Int32(clamping: replyThreadMessage.threadId) == replyAttribute.messageId.id { } else { replyMessage = item.message.associatedMessages[replyAttribute.messageId] } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index 46440364f0..0b314362d7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -363,7 +363,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } if let replyAttribute = attribute as? ReplyMessageAttribute { - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, Int32(clamping: replyThreadMessage.threadId) == replyAttribute.messageId.id { } else { replyMessage = item.message.associatedMessages[replyAttribute.messageId] } @@ -376,7 +376,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } if replyMessage != nil || replyForward != nil || replyStory != nil { - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyMessage?.id { + if case let .replyThread(replyThreadMessage) = item.chatLocation, Int32(clamping: replyThreadMessage.threadId) == replyMessage?.id.id { } else { replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments( presentationData: item.presentationData, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift index 23f849fb39..b2ef550c96 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemCommon/Sources/ChatMessageItemCommon.swift @@ -234,7 +234,7 @@ public func isPollEffectivelyClosed(message: Message, poll: TelegramMediaPoll) - public extension ChatReplyThreadMessage { var effectiveTopId: MessageId { - return self.channelMessageId ?? self.messageId + return self.channelMessageId ?? MessageId(peerId: self.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: self.threadId)) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index d279d61542..e99ad854f4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -253,7 +253,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { return false } - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId.peerId != item.content.firstMessage.id.peerId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.peerId != item.content.firstMessage.id.peerId { return false } @@ -466,8 +466,8 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { hasAvatar = true } case let .replyThread(replyThreadMessage): - if replyThreadMessage.messageId.peerId != item.context.account.peerId { - if replyThreadMessage.messageId.peerId.isGroupOrChannel && item.message.author != nil { + if replyThreadMessage.peerId != item.context.account.peerId { + if replyThreadMessage.peerId.isGroupOrChannel && item.message.author != nil { var isBroadcastChannel = false if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { isBroadcastChannel = true @@ -689,7 +689,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { if let replyAttribute = attribute as? ReplyMessageAttribute { - if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId { + if case let .replyThread(replyThreadMessage) = item.chatLocation, Int32(clamping: replyThreadMessage.threadId) == replyAttribute.messageId.id { } else { replyMessage = item.message.associatedMessages[replyAttribute.messageId] } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift index 7fac23a272..8b3ae7e569 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift @@ -44,6 +44,10 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI private var presentationDataDisposable: Disposable? private let chatListNode: ChatListNode + + private var emptyShimmerEffectNode: ChatListShimmerNode? + private var shimmerNodeOffset: CGFloat = 0.0 + private var floatingHeaderOffset: CGFloat? public init(context: AccountContext, navigationController: @escaping () -> NavigationController?) { self.context = context @@ -94,10 +98,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI navigationController: navigationController, context: self.context, chatLocation: .replyThread(ChatReplyThreadMessage( - messageId: makeThreadIdMessageId( - peerId: self.context.account.peerId, - threadId: peer.id.toInt64() - ), + peerId: self.context.account.peerId, threadId: peer.id.toInt64(), channelMessageId: nil, isChannelPost: false, @@ -115,6 +116,53 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI )) self.chatListNode.clearHighlightAnimated(true) } + + self.chatListNode.isEmptyUpdated = { [weak self] isEmptyState, _, transition in + guard let self else { + return + } + var needsShimmerNode = false + let shimmerNodeOffset: CGFloat = 0.0 + + switch isEmptyState { + case let .empty(isLoadingValue, _): + if isLoadingValue { + needsShimmerNode = true + } + case .notEmpty: + break + } + + if needsShimmerNode { + self.shimmerNodeOffset = shimmerNodeOffset + if self.emptyShimmerEffectNode == nil { + let emptyShimmerEffectNode = ChatListShimmerNode() + self.emptyShimmerEffectNode = emptyShimmerEffectNode + self.insertSubnode(emptyShimmerEffectNode, belowSubnode: self.chatListNode) + if let currentParams = self.currentParams, let offset = self.floatingHeaderOffset { + self.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: currentParams.size, insets: UIEdgeInsets(top: currentParams.topInset, left: currentParams.sideInset, bottom: currentParams.bottomInset, right: currentParams.sideInset), verticalOffset: offset + self.shimmerNodeOffset, transition: .immediate) + } + } + } else if let emptyShimmerEffectNode = self.emptyShimmerEffectNode { + self.emptyShimmerEffectNode = nil + let emptyNodeTransition = transition.isAnimated ? transition : .animated(duration: 0.3, curve: .easeInOut) + emptyNodeTransition.updateAlpha(node: emptyShimmerEffectNode, alpha: 0.0, completion: { [weak emptyShimmerEffectNode] _ in + emptyShimmerEffectNode?.removeFromSupernode() + }) + self.chatListNode.alpha = 0.0 + emptyNodeTransition.updateAlpha(node: self.chatListNode, alpha: 1.0) + } + } + + self.chatListNode.updateFloatingHeaderOffset = { [weak self] offset, transition in + guard let self else { + return + } + self.floatingHeaderOffset = offset + if let currentParams = self.currentParams, let emptyShimmerEffectNode = self.emptyShimmerEffectNode { + self.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: currentParams.size, insets: UIEdgeInsets(top: currentParams.topInset, left: currentParams.sideInset, bottom: currentParams.bottomInset, right: currentParams.sideInset), verticalOffset: offset + self.shimmerNodeOffset, transition: transition) + } + } } deinit { @@ -164,6 +212,11 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, UI return true } + private func layoutEmptyShimmerEffectNode(node: ChatListShimmerNode, size: CGSize, insets: UIEdgeInsets, verticalOffset: CGFloat, transition: ContainedViewLayoutTransition) { + node.update(context: self.context, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, size: size, isInlineMode: false, presentationData: self.presentationData, transition: .immediate) + transition.updateFrameAdditive(node: node, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: size)) + } + public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer.state != .failed, let otherGestureRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer { let _ = otherGestureRecognizer diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index 0573c39edd..54efd21ca2 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -830,9 +830,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen } } - /*if peerId == context.account.peerId, case .peer = chatLocation { + if peerId == context.account.peerId, case .peer = chatLocation { availablePanes?.insert(.savedMessagesChats, at: 0) - }*/ + } } else { availablePanes = nil } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 1f9f270ce1..f9372267e0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -450,7 +450,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { private var currentPanelStatusData: PeerInfoStatusData? func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) -> CGFloat { var threadData = threadData - if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId.peerId == self.context.account.peerId { + if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.peerId == self.context.account.peerId { threadData = nil } @@ -1606,7 +1606,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { if self.isSettings { expandablePart += 20.0 } else { - if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId.peerId == self.context.account.peerId { + if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.peerId == self.context.account.peerId { expandablePart = 0.0 } else if peer?.id == self.context.account.peerId { expandablePart = 0.0 diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 794ec2aa17..3e36375104 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -6009,7 +6009,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro private func openChatWithMessageSearch() { if let navigationController = (self.controller?.navigationController as? NavigationController) { if case let .replyThread(currentMessage) = self.chatLocation, let current = navigationController.viewControllers.first(where: { controller in - if let controller = controller as? ChatController, case let .replyThread(message) = controller.chatLocation, message.messageId == currentMessage.messageId { + if let controller = controller as? ChatController, case let .replyThread(message) = controller.chatLocation, message.peerId == currentMessage.peerId, message.threadId == currentMessage.threadId { return true } return false @@ -10922,7 +10922,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc let navigateChatLocation: NavigateToChatControllerParams.Location if let threadId = item.threadId { navigateChatLocation = .replyThread(ChatReplyThreadMessage( - messageId: MessageId(peerId: item.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: item.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )) } else { navigateChatLocation = .peer(itemPeer) diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index 6d2f00474d..5e70824422 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -2563,7 +2563,7 @@ final class StorageUsageScreenComponent: Component { var chatLocation: NavigateToChatControllerParams.Location = .peer(peer) if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId = message.threadId { - chatLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( @@ -2666,7 +2666,7 @@ final class StorageUsageScreenComponent: Component { var chatLocation: NavigateToChatControllerParams.Location = .peer(peer) if case let .channel(channel) = peer, channel.flags.contains(.isForum), let threadId = message.threadId { - chatLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams( diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift index 5441f218b8..a7dce56b2e 100644 --- a/submodules/TelegramUI/Sources/AccountContext.swift +++ b/submodules/TelegramUI/Sources/AccountContext.swift @@ -477,11 +477,11 @@ public final class AccountContextImpl: AccountContext { case let .peer(peerId): return .peer(peerId: peerId, threadId: nil) case let .replyThread(data): - if data.isForumPost { - return .peer(peerId: data.messageId.peerId, threadId: Int64(data.messageId.id)) + if data.isForumPost || data.peerId.namespace != Namespaces.Peer.CloudChannel { + return .peer(peerId: data.peerId, threadId: data.threadId) } else { let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) - return .thread(peerId: data.messageId.peerId, threadId: makeMessageThreadId(data.messageId), data: context.state) + return .thread(peerId: data.peerId, threadId: data.threadId, data: context.state) } case .feed: preconditionFailure() @@ -494,7 +494,7 @@ public final class AccountContextImpl: AccountContext { return .single(nil) case let .replyThread(data): if data.isForumPost, let peerId = location.peerId { - let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: data.messageId.peerId, threadId: Int64(data.messageId.id)) + let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: data.peerId, threadId: data.threadId) return self.account.postbox.combinedView(keys: [viewKey]) |> map { views -> MessageId? in if let threadInfo = views.views[viewKey] as? MessageHistoryThreadInfoView, let data = threadInfo.info?.data.get(MessageHistoryThreadData.self) { @@ -503,9 +503,11 @@ public final class AccountContextImpl: AccountContext { return nil } } - } else { + } else if data.peerId.namespace == Namespaces.Peer.CloudChannel { let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) return context.maxReadOutgoingMessageId + } else { + return .single(nil) } case .feed: return .single(nil) @@ -530,7 +532,7 @@ public final class AccountContextImpl: AccountContext { } case let .replyThread(data): if data.isForumPost { - let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: data.messageId.peerId, threadId: Int64(data.messageId.id)) + let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: data.peerId, threadId: data.threadId) return self.account.postbox.combinedView(keys: [viewKey]) |> map { views -> Int in if let threadInfo = views.views[viewKey] as? MessageHistoryThreadInfoView, let data = threadInfo.info?.data.get(MessageHistoryThreadData.self) { @@ -539,6 +541,8 @@ public final class AccountContextImpl: AccountContext { return 0 } } + } else if data.peerId.namespace != Namespaces.Peer.CloudChannel { + return .single(0) } else { let context = chatLocationContext(holder: contextHolder, account: self.account, data: data) return context.unreadCount @@ -722,7 +726,7 @@ private final class ChatLocationReplyContextHolderImpl: ChatLocationContextHolde let context: ReplyThreadHistoryContext init(account: Account, data: ChatReplyThreadMessage) { - self.context = ReplyThreadHistoryContext(account: account, peerId: data.messageId.peerId, data: data) + self.context = ReplyThreadHistoryContext(account: account, peerId: data.peerId, data: data) } } diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 3426f8ca31..9d69dc76cd 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -313,7 +313,7 @@ final class AuthorizedApplicationContext { let chatLocation: NavigateToChatControllerParams.Location if let _ = threadData, let threadId = firstMessage.threadId { chatLocation = .replyThread(ChatReplyThreadMessage( - messageId: MessageId(peerId: firstMessage.id.peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: firstMessage.id.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false ).normalized) } else { guard let peer = firstMessage.peers[firstMessage.id.peerId] else { @@ -905,7 +905,7 @@ final class AuthorizedApplicationContext { let chatLocation: NavigateToChatControllerParams.Location if let threadId = threadId { chatLocation = .replyThread(ChatReplyThreadMessage( - messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false + peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false )) } else { chatLocation = .peer(peer) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift index f33d0ca9d4..d250a02135 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigateToMessage.swift @@ -18,9 +18,9 @@ extension ChatControllerImpl { params: NavigateToMessageParams ) { var id = id - if case let .replyThread(message) = self.chatLocation { + if case let .replyThread(message) = self.chatLocation, let effectiveMessageId = message.effectiveMessageId { if let channelMessageId = message.channelMessageId, id == channelMessageId { - id = message.messageId + id = effectiveMessageId } } @@ -130,7 +130,7 @@ extension ChatControllerImpl { let navigateToLocation: NavigateToChatControllerParams.Location if let message = messages.first, let threadId = message.threadId, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) { - navigateToLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + navigateToLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } else { navigateToLocation = .peer(peer) } @@ -148,7 +148,7 @@ extension ChatControllerImpl { if let navigationController = self.effectiveNavigationController { var chatLocation: NavigateToChatControllerParams.Location = .peer(peer) if case let .channel(channel) = peer, channel.flags.contains(.isForum), let message = message, let threadId = message.threadId { - chatLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + chatLocation = .replyThread(ChatReplyThreadMessage(peerId: peer.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } var quote: ChatControllerSubject.MessageHighlight.Quote? @@ -221,7 +221,7 @@ extension ChatControllerImpl { let searchLocation: ChatHistoryInitialSearchLocation switch messageLocation { case let .id(id, _): - if case let .replyThread(message) = self.chatLocation, id == message.messageId { + if case let .replyThread(message) = self.chatLocation, id == message.effectiveMessageId { searchLocation = .index(.absoluteLowerBound()) } else { searchLocation = .id(id) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 3d3e25bdb3..65c4d20d62 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -608,13 +608,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G locationBroadcastPanelSource = .none groupCallPanelSource = .none let promise = Promise() - promise.set(context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.Message(id: replyThreadMessage.messageId)) - |> map { message -> Message? in - guard let message = message else { - return nil - } - return message._asMessage() - }) + if let effectiveMessageId = replyThreadMessage.effectiveMessageId { + promise.set(context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.Message(id: effectiveMessageId)) + |> map { message -> Message? in + guard let message = message else { + return nil + } + return message._asMessage() + }) + } else { + promise.set(.single(nil)) + } self.chatLocationInfoData = .replyThread(promise) case .feed: locationBroadcastPanelSource = .none @@ -1038,11 +1042,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let openChatLocation = strongSelf.chatLocation - /*if case let .replyThread(replyThreadMessage) = openChatLocation { - if message.threadId != makeMessageThreadId(replyThreadMessage.messageId) { - openChatLocation = .peer(id: message.id.peerId) - } - }*/ return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: openChatLocation, chatLocationContextHolder: strongSelf.chatLocationContextHolder, message: message, standalone: false, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.effectiveNavigationController, dismissInput: { self?.chatDisplayNode.dismissInput() @@ -2804,12 +2803,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if isCopyLink, let channel = channel as? TelegramChannel { - var threadMessageId: MessageId? + var threadId: Int64? if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation { - threadMessageId = replyThreadMessage.messageId + threadId = replyThreadMessage.threadId } - let _ = (context.engine.messages.exportMessageLink(peerId: messageId.peerId, messageId: messageId, isThread: threadMessageId != nil) + let _ = (context.engine.messages.exportMessageLink(peerId: messageId.peerId, messageId: messageId, isThread: threadId != nil) |> map { result -> String? in return result } @@ -2995,7 +2994,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if let strongSelf = self { - if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation, replyThreadMessage.messageId == message.id { + if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation, replyThreadMessage.effectiveMessageId == message.id { return .none } if case .peer = strongSelf.chatLocation, let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { @@ -3027,7 +3026,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G 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(replyThreadMessage): - let peerId = replyThreadMessage.messageId.peerId + let peerId = replyThreadMessage.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, forceInCurrentChat: true, animated: true, completion: nil) case .feed: break @@ -5705,7 +5704,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .peer: replyThreadType = .replies case let .replyThread(replyThreadMessage): - if replyThreadMessage.messageId.peerId == context.account.peerId { + if replyThreadMessage.peerId == context.account.peerId { replyThreadId = replyThreadMessage.threadId replyThreadType = .replies } else { @@ -5751,8 +5750,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let savedMessagesPeerId: PeerId? - if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.messageId.peerId == context.account.peerId { - savedMessagesPeerId = PeerId(makeMessageThreadId(replyThreadMessage.messageId)) + if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.peerId == context.account.peerId { + savedMessagesPeerId = PeerId(replyThreadMessage.threadId) } else { savedMessagesPeerId = nil } @@ -6302,7 +6301,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .peer(peerId): activitySpace = PeerActivitySpace(peerId: peerId, category: .global) case let .replyThread(replyThreadMessage): - activitySpace = PeerActivitySpace(peerId: replyThreadMessage.messageId.peerId, category: .thread(makeMessageThreadId(replyThreadMessage.messageId))) + activitySpace = PeerActivitySpace(peerId: replyThreadMessage.peerId, category: .thread(replyThreadMessage.threadId)) case .feed: activitySpace = nil } @@ -6868,7 +6867,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let chatLocation: ChatLocation if let threadId { - chatLocation = .replyThread(message: ChatReplyThreadMessage(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) + chatLocation = .replyThread(message: ChatReplyThreadMessage(peerId: peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } else { chatLocation = .peer(id: peerId) } @@ -7176,7 +7175,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var closeOnEmpty = false if case .pinnedMessages = self.presentationInterfaceState.subject { closeOnEmpty = true - } else if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId.peerId == self.context.account.peerId { + } else if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.peerId == self.context.account.peerId { closeOnEmpty = true } @@ -10999,7 +10998,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .peer(peerId): activitySpace = PeerActivitySpace(peerId: peerId, category: .global) case let .replyThread(replyThreadMessage): - activitySpace = PeerActivitySpace(peerId: replyThreadMessage.messageId.peerId, category: .thread(makeMessageThreadId(replyThreadMessage.messageId))) + activitySpace = PeerActivitySpace(peerId: replyThreadMessage.peerId, category: .thread(replyThreadMessage.threadId)) case .feed: activitySpace = nil } @@ -11315,7 +11314,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if case let .replyThread(message) = self.chatLocation, message.isForumPost { if self.keepMessageCountersSyncrhonizedDisposable == nil { - self.keepMessageCountersSyncrhonizedDisposable = self.context.engine.messages.keepMessageCountersSyncrhonized(peerId: message.messageId.peerId, threadId: Int64(message.messageId.id)).startStrict() + self.keepMessageCountersSyncrhonizedDisposable = self.context.engine.messages.keepMessageCountersSyncrhonized(peerId: message.peerId, threadId: message.threadId).startStrict() } } @@ -11851,8 +11850,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .peer(peerIdValue): peerId = peerIdValue case let .replyThread(replyThreadMessage): - peerId = replyThreadMessage.messageId.peerId - threadId = makeMessageThreadId(replyThreadMessage.messageId) + peerId = replyThreadMessage.peerId + threadId = replyThreadMessage.threadId case .feed: return } @@ -12392,7 +12391,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } })) case .replyThread: - if let peer = self.presentationInterfaceState.renderedPeer?.peer, case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId.peerId == self.context.account.peerId { + if let peer = self.presentationInterfaceState.renderedPeer?.peer, case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.peerId == self.context.account.peerId { if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: peer, mode: .forumTopic(thread: replyThreadMessage), avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) { self.effectiveNavigationController?.pushViewController(infoController) } @@ -14948,7 +14947,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .peer: break case let .replyThread(replyThreadMessage): - defaultReplyMessageSubject = EngineMessageReplySubject(messageId: replyThreadMessage.messageId, quote: nil) + if let effectiveMessageId = replyThreadMessage.effectiveMessageId { + defaultReplyMessageSubject = EngineMessageReplySubject(messageId: effectiveMessageId, quote: nil) + } case .feed: break } @@ -15651,22 +15652,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } return nil } - var searchTopMsgId: MessageId? + var threadId: Int64? switch self.chatLocation { case .peer: break case let .replyThread(replyThreadMessage): - searchTopMsgId = replyThreadMessage.messageId + threadId = replyThreadMessage.threadId case .feed: break } switch search.domain { case .everything: - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil, topMsgId: searchTopMsgId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: nil, tags: nil, threadId: threadId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) case .members: derivedSearchState = nil case let .member(peer): - derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: peer.id, tags: nil, topMsgId: searchTopMsgId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) + derivedSearchState = ChatSearchState(query: search.query, location: .peer(peerId: peerId, fromId: peer.id, tags: nil, threadId: threadId, minDate: nil, maxDate: nil), loadMoreState: loadMoreStateFromResultsState(search.resultsState)) } } @@ -15972,8 +15973,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G peerId = peerIdValue threadId = nil case let .replyThread(replyThreadMessage): - peerId = replyThreadMessage.messageId.peerId - threadId = makeMessageThreadId(replyThreadMessage.messageId) + peerId = replyThreadMessage.peerId + threadId = replyThreadMessage.threadId case .feed: return } @@ -16009,8 +16010,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G peerId = peerIdValue threadId = nil case let .replyThread(replyThreadMessage): - peerId = replyThreadMessage.messageId.peerId - threadId = makeMessageThreadId(replyThreadMessage.messageId) + peerId = replyThreadMessage.peerId + threadId = replyThreadMessage.threadId case .feed: return } diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index cba436a799..0b90e15cc2 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -383,7 +383,7 @@ private func extractAssociatedData( } } } else if case let .replyThread(message) = chatLocation, message.isForumPost { - automaticDownloadPeerId = message.messageId.peerId + automaticDownloadPeerId = message.peerId } return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes) @@ -1145,11 +1145,11 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto additionalData.append(.totalUnreadState) } if case let .replyThread(replyThreadMessage) = self.chatLocation { - additionalData.append(.cachedPeerData(replyThreadMessage.messageId.peerId)) - additionalData.append(.peerNotificationSettings(replyThreadMessage.messageId.peerId)) - if replyThreadMessage.messageId.peerId.namespace == Namespaces.Peer.CloudChannel { - additionalData.append(.cacheEntry(cachedChannelAdminRanksEntryId(peerId: replyThreadMessage.messageId.peerId))) - additionalData.append(.peer(replyThreadMessage.messageId.peerId)) + additionalData.append(.cachedPeerData(replyThreadMessage.peerId)) + additionalData.append(.peerNotificationSettings(replyThreadMessage.peerId)) + if replyThreadMessage.peerId.namespace == Namespaces.Peer.CloudChannel { + additionalData.append(.cacheEntry(cachedChannelAdminRanksEntryId(peerId: replyThreadMessage.peerId))) + additionalData.append(.peer(replyThreadMessage.peerId)) } additionalData.append(.message(replyThreadMessage.effectiveTopId)) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 8e52379e67..f89b573d53 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -718,7 +718,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState let readCounters: Signal if case let .replyThread(threadMessage) = chatPresentationInterfaceState.chatLocation, threadMessage.isForumPost { - readCounters = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThreadData(id: threadMessage.messageId.peerId, threadId: Int64(threadMessage.messageId.id))) + readCounters = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThreadData(id: threadMessage.peerId, threadId: threadMessage.threadId)) |> map { threadData -> Bool in guard let threadData else { return false @@ -1248,7 +1248,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } else { for attribute in messages[0].attributes { if let attribute = attribute as? ReplyThreadMessageAttribute, attribute.count > 0 { - threadId = makeMessageThreadId(messages[0].id) + threadId = Int64(messages[0].id.id) threadMessageCount = Int(attribute.count) } } @@ -1256,7 +1256,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } else { for attribute in messages[0].attributes { if let attribute = attribute as? ReplyThreadMessageAttribute, attribute.count > 0 { - threadId = makeMessageThreadId(messages[0].id) + threadId = Int64(messages[0].id.id) threadMessageCount = Int(attribute.count) } } @@ -1404,7 +1404,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }, action: { _, f in var threadMessageId: MessageId? if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation { - threadMessageId = replyThreadMessage.messageId + threadMessageId = replyThreadMessage.effectiveMessageId } let _ = (context.engine.messages.exportMessageLink(peerId: message.id.peerId, messageId: message.id, isThread: threadMessageId != nil) |> map { result -> String? in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index c3ae2dde73..c85ae97d29 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -117,7 +117,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState } } - if case let .replyThread(message) = chatPresentationInterfaceState.chatLocation, message.messageId.peerId == context.account.peerId { + if case let .replyThread(message) = chatPresentationInterfaceState.chatLocation, message.peerId == context.account.peerId { 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 90475e41d2..69647525d7 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateNavigationButtons.swift @@ -71,7 +71,7 @@ func rightNavigationButtonForChatInterfaceState(context: AccountContext, present } } - if case let .replyThread(message) = presentationInterfaceState.chatLocation, message.messageId.peerId == context.account.peerId { + if case let .replyThread(message) = presentationInterfaceState.chatLocation, message.peerId == context.account.peerId { return chatInfoNavigationButton } diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index f181e3c929..77d57425b1 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -268,7 +268,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam return true } case let .replyThread(replyThreadMessage): - if message.id.peerId == replyThreadMessage.messageId.peerId { + if message.id.peerId == replyThreadMessage.peerId { return true } } diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index d304b4a47d..364f5b6103 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -206,10 +206,10 @@ func openResolvedUrlImpl( case let .channelMessage(peer, messageId, timecode): openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: timecode), peekData: nil)) case let .replyThreadMessage(replyThreadMessage, messageId): - if let navigationController = navigationController { + if let navigationController = navigationController, let effectiveMessageId = replyThreadMessage.effectiveMessageId { let _ = ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { c, a in present(c, a) - }, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true).startStandalone() + }, messageId: effectiveMessageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true).startStandalone() } case let .replyThread(messageId): if let navigationController = navigationController { diff --git a/submodules/TelegramUI/Sources/TextLinkHandling.swift b/submodules/TelegramUI/Sources/TextLinkHandling.swift index e925d46e54..4d862fbebd 100644 --- a/submodules/TelegramUI/Sources/TextLinkHandling.swift +++ b/submodules/TelegramUI/Sources/TextLinkHandling.swift @@ -72,10 +72,10 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: EnginePeer.Id?, n context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(EnginePeer(peer)), subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: timecode))) } case let .replyThreadMessage(replyThreadMessage, messageId): - if let navigationController = controller.navigationController as? NavigationController { + if let navigationController = controller.navigationController as? NavigationController, let effectiveMessageId = replyThreadMessage.effectiveMessageId { let _ = ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { [weak controller] c, a in controller?.present(c, in: .window(.root), with: a) - }, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true).start() + }, messageId: effectiveMessageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true).start() } case let .replyThread(messageId): if let navigationController = controller.navigationController as? NavigationController { diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index b6179adffd..361bed480d 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -719,7 +719,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .progress case let .result(info): if let _ = info { - return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) + return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) } else { return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) } @@ -744,7 +744,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .progress case let .result(info): if let _ = info { - return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: replyThreadMessageId.id)), threadId: Int64(replyThreadMessageId.id), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: replyId))) + return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(replyThreadMessageId.id), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: replyId))) } else { return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) } @@ -760,7 +760,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) guard let result = result else { return .result(.channelMessage(peer: peer._asPeer(), messageId: replyThreadMessageId, timecode: nil)) } - return .result(.replyThreadMessage(replyThreadMessage: result, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId))) + return .result(.replyThreadMessage(replyThreadMessage: result, messageId: MessageId(peerId: result.peerId, namespace: Namespaces.Message.Cloud, id: replyId))) }) } case let .voiceChat(invite): @@ -825,7 +825,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .progress case let .result(info): if let _ = info { - return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: Int64(threadId), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) + return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: Int64(threadId), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) } else { return .result(.peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) } @@ -849,7 +849,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) return .progress case let .result(info): if let _ = info { - return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(messageId: MessageId(peerId: channel.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) + return .result(.replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage(peerId: channel.id, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false), messageId: messageId)) } else { return .result(.peer(peer?._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) }