diff --git a/submodules/TelegramCore/Sources/AccountViewTracker.swift b/submodules/TelegramCore/Sources/AccountViewTracker.swift index 22f1564068..85ad8ffd43 100644 --- a/submodules/TelegramCore/Sources/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/AccountViewTracker.swift @@ -253,6 +253,10 @@ public final class AccountViewTracker { private var nextUpdatedViewCountDisposableId: Int32 = 0 private var updatedViewCountDisposables = DisposableDict() + private var updatedReactionsMessageIdsAndTimestamps: [MessageId: Int32] = [:] + private var nextUpdatedReactionsDisposableId: Int32 = 0 + private var updatedReactionsDisposables = DisposableDict() + private var updatedSeenLiveLocationMessageIdsAndTimestamps: [MessageId: Int32] = [:] private var nextSeenLiveLocationDisposableId: Int32 = 0 private var seenLiveLocationDisposables = DisposableDict() @@ -303,6 +307,7 @@ public final class AccountViewTracker { deinit { self.updatedViewCountDisposables.dispose() + self.updatedReactionsDisposables.dispose() self.externallyUpdatedPeerIdDisposable.dispose() } @@ -631,6 +636,89 @@ public final class AccountViewTracker { } } + public func updateReactionsForMessageIds(messageIds: Set) { + self.queue.async { + var addedMessageIds: [MessageId] = [] + let timestamp = Int32(CFAbsoluteTimeGetCurrent()) + for messageId in messageIds { + let messageTimestamp = self.updatedReactionsMessageIdsAndTimestamps[messageId] + if messageTimestamp == nil || messageTimestamp! < timestamp - 5 * 60 { + self.updatedReactionsMessageIdsAndTimestamps[messageId] = timestamp + addedMessageIds.append(messageId) + } + } + if !addedMessageIds.isEmpty { + for (peerId, messageIds) in messagesIdsGroupedByPeerId(Set(addedMessageIds)) { + let disposableId = self.nextUpdatedReactionsDisposableId + self.nextUpdatedReactionsDisposableId += 1 + + if let account = self.account { + let signal = (account.postbox.transaction { transaction -> Signal in + if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { + return account.network.request(Api.functions.messages.getMessagesReactions(peer: inputPeer, id: messageIds.map { $0.id })) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { updates -> Signal in + guard let updates = updates else { + return .complete() + } + return account.postbox.transaction { transaction -> Void in + let updateList: [Api.Update] + switch updates { + case let .updates(updates, _, _, _, _): + updateList = updates + case let .updatesCombined(updates, _, _, _, _, _): + updateList = updates + case let .updateShort(update, _): + updateList = [update] + default: + updateList = [] + } + for update in updateList { + switch update { + case let .updateMessageReactions(peer, msgId, reactions): + transaction.updateMessage(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), update: { currentMessage in + + let updatedReactions = ReactionsMessageAttribute(apiReactions: reactions) + + let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ReactionsMessageAttribute { + if updatedReactions.reactions == attribute.reactions { + return .skip + } + attributes[j] = updatedReactions + break loop + } + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + default: + break + } + } + } + } + } else { + return .complete() + } + } + |> switchToLatest) + |> afterDisposed { [weak self] in + self?.queue.async { + self?.updatedReactionsDisposables.set(nil, forKey: disposableId) + } + } + self.updatedReactionsDisposables.set(signal.start(), forKey: disposableId) + } + } + } + } + } + public func updateSeenLiveLocationForMessageIds(messageIds: Set) { self.queue.async { var addedMessageIds: [MessageId] = [] diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 265c185dff..8d8807ab29 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -457,6 +457,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { private let galleryHiddenMesageAndMediaDisposable = MetaDisposable() private let messageProcessingManager = ChatMessageThrottledProcessingManager() + private let messageReactionsProcessingManager = ChatMessageThrottledProcessingManager() private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager() private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager() private let messageMentionProcessingManager = ChatMessageThrottledProcessingManager(delay: 0.2) @@ -531,6 +532,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.messageProcessingManager.process = { [weak context] messageIds in context?.account.viewTracker.updateViewCountForMessageIds(messageIds: messageIds) } + self.messageReactionsProcessingManager.process = { [weak context] messageIds in + context?.account.viewTracker.updateReactionsForMessageIds(messageIds: messageIds) + } self.seenLiveLocationProcessingManager.process = { [weak context] messageIds in context?.account.viewTracker.updateSeenLiveLocationForMessageIds(messageIds: messageIds) } @@ -984,6 +988,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let toLaterRange = (historyView.filteredEntries.count - 1 - (visible.firstIndex - 1), historyView.filteredEntries.count - 1) var messageIdsWithViewCount: [MessageId] = [] + var messageIdsWithUpdateableReactions: [MessageId] = [] var messageIdsWithLiveLocation: [MessageId] = [] var messageIdsWithUnsupportedMedia: [MessageId] = [] var messageIdsWithUnseenPersonalMention: [MessageId] = [] @@ -1015,6 +1020,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { contentRequiredValidation = true } } + var isAction = false for media in message.media { if let _ = media as? TelegramMediaUnsupported { contentRequiredValidation = true @@ -1023,8 +1029,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if message.timestamp + liveBroadcastingTimeout > timestamp { messageIdsWithLiveLocation.append(message.id) } + } else if let _ = media as? TelegramMediaAction { + isAction = true } } + if !isAction && message.id.peerId.namespace == Namespaces.Peer.CloudChannel { + messageIdsWithUpdateableReactions.append(message.id) + } if contentRequiredValidation { messageIdsWithUnsupportedMedia.append(message.id) } @@ -1127,6 +1138,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if !messageIdsWithViewCount.isEmpty { self.messageProcessingManager.add(messageIdsWithViewCount) } + if !messageIdsWithUpdateableReactions.isEmpty { + self.messageReactionsProcessingManager.add(messageIdsWithUpdateableReactions) + } if !messageIdsWithLiveLocation.isEmpty { self.seenLiveLocationProcessingManager.add(messageIdsWithLiveLocation) }