diff --git a/TelegramCore/AccountStateManagementUtils.swift b/TelegramCore/AccountStateManagementUtils.swift index d1ea7fc81f..66d4beb2bd 100644 --- a/TelegramCore/AccountStateManagementUtils.swift +++ b/TelegramCore/AccountStateManagementUtils.swift @@ -1940,6 +1940,14 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, transaction: Tr transaction.applyMarkUnread(peerId: peerId, namespace: namespace, value: value, interactive: false) case let .ResetMessageTagSummary(peerId, namespace, count, range): transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: namespace, count: count, maxId: range.maxId) + if count == 0 { + let ids = transaction.getMessageIndicesWithTag(peerId: peerId, tag: .unseenPersonalMessage).map({ $0.id }) + for id in ids { + if id.namespace == namespace { + markUnseenPersonalMessage(transaction: transaction, id: id, addSynchronizeAction: false) + } + } + } case let .UpdateState(state): let currentState = transaction.getState() as! AuthorizedAccountState transaction.setState(currentState.changedState(state)) diff --git a/TelegramCore/AccountViewTracker.swift b/TelegramCore/AccountViewTracker.swift index 2b1c6c2f46..a628d51218 100644 --- a/TelegramCore/AccountViewTracker.swift +++ b/TelegramCore/AccountViewTracker.swift @@ -465,7 +465,12 @@ public final class AccountViewTracker { let _ = (account.postbox.transaction { transaction -> Set in let ids = Set(transaction.getMessageIndicesWithTag(peerId: peerId, tag: .unseenPersonalMessage).map({ $0.id })) if let summary = transaction.getMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud), summary.count > 0 { - transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: 0, maxId: summary.range.maxId) + var maxId: Int32 = summary.range.maxId + if let index = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) { + maxId = index.id.id + } + + transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: 0, maxId: maxId) addSynchronizeMarkAllUnseenPersonalMessagesOperation(transaction: transaction, peerId: peerId, maxId: summary.range.maxId) } diff --git a/TelegramCore/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift b/TelegramCore/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift index 11ddb3ca8e..dc1ebe52ad 100644 --- a/TelegramCore/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift +++ b/TelegramCore/ManagedSynchronizeMarkAllUnseenPersonalMessagesOperations.swift @@ -125,50 +125,70 @@ private func synchronizeMarkAllUnseen(transaction: Transaction, postbox: Postbox return .complete() } let inputChannel = transaction.getPeer(peerId).flatMap(apiInputChannel) - let oneOperation: Signal = network.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: 0, addOffset: 0, limit: 100, maxId: 0, minId: 0)) - |> mapToSignal { result -> Signal<[MessageId], MTRpcError> in - switch result { - case let .messages(messages, _, _): - return .single(messages.compactMap({ $0.id })) - case let .channelMessages(channelMessages): - return .single(channelMessages.messages.compactMap({ $0.id })) - case .messagesNotModified: - return .single([]) - case let .messagesSlice(messagesSlice): - return .single(messagesSlice.messages.compactMap({ $0.id })) + let limit: Int32 = 100 + let oneOperation: (Int32) -> Signal = { maxId in + return network.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: maxId, addOffset: maxId == 0 ? 0 : -1, limit: limit, maxId: maxId == 0 ? 0 : (maxId + 1), minId: 1)) + |> mapToSignal { result -> Signal<[MessageId], MTRpcError> in + switch result { + case let .messages(messages, _, _): + return .single(messages.compactMap({ $0.id })) + case let .channelMessages(channelMessages): + return .single(channelMessages.messages.compactMap({ $0.id })) + case .messagesNotModified: + return .single([]) + case let .messagesSlice(messagesSlice): + return .single(messagesSlice.messages.compactMap({ $0.id })) + } } - } - |> mapToSignal { ids -> Signal in - if peerId.namespace == Namespaces.Peer.CloudChannel { - guard let inputChannel = inputChannel else { - return .single(true) + |> mapToSignal { ids -> Signal in + let filteredIds = ids.filter { $0.id <= operation.maxId } + if filteredIds.isEmpty { + return .single(ids.min()?.id) } - return network.request(Api.functions.channels.readMessageContents(channel: inputChannel, id: ids.map { $0.id })) - |> mapToSignal { result -> Signal in - return .single(true) - } - } else { - return network.request(Api.functions.messages.readMessageContents(id: ids.map { $0.id })) - |> mapToSignal { result -> Signal in - switch result { - case let .affectedMessages(pts, ptsCount): - stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + if peerId.namespace == Namespaces.Peer.CloudChannel { + guard let inputChannel = inputChannel else { + return .single(nil) + } + return network.request(Api.functions.channels.readMessageContents(channel: inputChannel, id: filteredIds.map { $0.id })) + |> map { result -> Int32? in + if ids.count < limit { + return nil + } else { + return ids.min()?.id + } + } + } else { + return network.request(Api.functions.messages.readMessageContents(id: filteredIds.map { $0.id })) + |> map { result -> Int32? in + switch result { + case let .affectedMessages(pts, ptsCount): + stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + } + if ids.count < limit { + return nil + } else { + return ids.min()?.id + } } - return .single(true) } } } + let currentMaxId = Atomic(value: 0) let loopOperations: Signal = ( - (oneOperation - |> `catch` { error -> Signal in + ( + deferred { + return oneOperation(currentMaxId.with { $0 }) + } + |> `catch` { error -> Signal in return .fail(.error(error)) } ) - |> mapToSignal { result -> Signal in - if result { - return .fail(.done) - } else { + |> mapToSignal { resultId -> Signal in + if let resultId = resultId { + let _ = currentMaxId.swap(resultId) return .complete() + } else { + return .fail(.done) } } |> `catch` { error -> Signal in @@ -184,3 +204,31 @@ private func synchronizeMarkAllUnseen(transaction: Transaction, postbox: Postbox return .complete() } } + +func markUnseenPersonalMessage(transaction: Transaction, id: MessageId, addSynchronizeAction: Bool) { + if let message = transaction.getMessage(id) { + var consume = false + inner: for attribute in message.attributes { + if let attribute = attribute as? ConsumablePersonalMentionMessageAttribute, !attribute.consumed, !attribute.pending { + consume = true + break inner + } + } + if consume { + transaction.updateMessage(id, update: { currentMessage in + var attributes = currentMessage.attributes + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ConsumablePersonalMentionMessageAttribute { + attributes[j] = ConsumablePersonalMentionMessageAttribute(consumed: attribute.consumed, pending: true) + 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: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + + if addSynchronizeAction { + transaction.setPendingMessageAction(type: .consumeUnseenPersonalMessage, id: id, action: ConsumePersonalMessageAction()) + } + } + } +}