diff --git a/submodules/Postbox/Sources/FailedMessagesView.swift b/submodules/Postbox/Sources/FailedMessagesView.swift new file mode 100644 index 0000000000..1a1105f41e --- /dev/null +++ b/submodules/Postbox/Sources/FailedMessagesView.swift @@ -0,0 +1,30 @@ +final class MutableFailedMessageIdsView { + let peerId: PeerId + var ids: Set + + init(peerId: PeerId, ids: [MessageId]) { + self.peerId = peerId + self.ids = Set(ids) + } + func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { + let ids = transaction.updatedFailedMessageIds.filter { $0.peerId == self.peerId } + let updated = ids != self.ids + self.ids = ids + return updated + } + + func immutableView() -> FailedMessageIdsView { + return FailedMessageIdsView(self.ids) + } + +} + + + +public final class FailedMessageIdsView { + public let ids: Set + + fileprivate init(_ ids: Set) { + self.ids = ids + } +} diff --git a/submodules/Postbox/Sources/MessageHistoryFailedTable.swift b/submodules/Postbox/Sources/MessageHistoryFailedTable.swift index c1cc3cb8f2..057e785ba4 100644 --- a/submodules/Postbox/Sources/MessageHistoryFailedTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryFailedTable.swift @@ -8,7 +8,8 @@ final class MessageHistoryFailedTable: Table { private let sharedKey = ValueBoxKey(length: 8 + 4 + 4) private(set) var updatedPeerIds = Set() - + private(set) var updatedMessageIds = Set() + private func key(_ id: MessageId) -> ValueBoxKey { self.sharedKey.setInt64(0, value: id.peerId.toInt64()) self.sharedKey.setInt32(8, value: id.namespace) @@ -32,11 +33,30 @@ final class MessageHistoryFailedTable: Table { func add(_ id: MessageId) { self.valueBox.set(self.table, key: self.key(id), value: MemoryBuffer()) self.updatedPeerIds.insert(id.peerId) + self.updatedMessageIds.insert(id) } func remove(_ id: MessageId) { self.valueBox.remove(self.table, key: self.key(id), secure: false) self.updatedPeerIds.insert(id.peerId) + self.updatedMessageIds.remove(id) + } + + func get(peerId:PeerId) -> [MessageId] { + var ids:[MessageId] = [] + self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), keys: { key in + + let peerId = PeerId(key.getInt64(0)) + let namespace = key.getInt32(8) + let id = key.getInt32(8 + 4) + ids.append(MessageId(peerId: peerId, namespace: namespace, id: id)) + + return false + }, limit: 100) + + self.updatedMessageIds = self.updatedMessageIds.union(ids) + + return ids } func contains(peerId: PeerId) -> Bool { diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 55db3c8a29..9e333e6f9f 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -107,6 +107,14 @@ public final class Transaction { return [] } } + public func failedMessageIds(for peerId: PeerId) -> [MessageId] { + assert(!self.disposed) + if let postbox = self.postbox { + return postbox.failedMessageIds(for: peerId) + } else { + return [] + } + } public func deleteMessagesWithGlobalIds(_ ids: [Int32], forEachMedia: (Media) -> Void) { assert(!self.disposed) @@ -1703,7 +1711,7 @@ public final class Postbox { let transactionParticipationInTotalUnreadCountUpdates = self.peerNotificationSettingsTable.transactionParticipationInTotalUnreadCountUpdates(postbox: self) self.chatListIndexTable.commitWithTransaction(postbox: self, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, updatedPeers: updatedPeers, transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, updatedRootUnreadState: &self.currentUpdatedTotalUnreadState, updatedGroupTotalUnreadSummaries: &self.currentUpdatedGroupTotalUnreadSummaries, currentUpdatedGroupSummarySynchronizeOperations: &self.currentUpdatedGroupSummarySynchronizeOperations) - let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadState: self.currentUpdatedTotalUnreadState, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds) + let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadState: self.currentUpdatedTotalUnreadState, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds, updatedFailedMessageIds: self.messageHistoryFailedTable.updatedMessageIds) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? if !transaction.isEmpty { @@ -1772,6 +1780,9 @@ public final class Postbox { } return result } + fileprivate func failedMessageIds(for peerId: PeerId) -> [MessageId] { + return self.messageHistoryFailedTable.get(peerId: peerId) + } fileprivate func messageIdForGloballyUniqueMessageId(peerId: PeerId, id: Int64) -> MessageId? { return self.globallyUniqueMessageIdsTable.get(peerId: peerId, globallyUniqueId: id) @@ -3185,4 +3196,25 @@ public final class Postbox { self.currentUpdatedGroupTotalUnreadSummaries[groupId] = summary } } + + public func failedMessageIdsView(peerId: PeerId) -> Signal { + return self.transactionSignal { subscriber, transaction in + let view = MutableFailedMessageIdsView(peerId: peerId, ids: self.failedMessageIds(for: peerId)) + let (index, signal) = self.viewTracker.addFailedMessageIdsView(view) + subscriber.putNext(view.immutableView()) + let disposable = signal.start(next: { next in + subscriber.putNext(next) + }) + + return ActionDisposable { [weak self] in + disposable.dispose() + if let strongSelf = self { + strongSelf.queue.async { + strongSelf.viewTracker.removeFailedMessageIdsView(index) + } + } + } + } + } + } diff --git a/submodules/Postbox/Sources/PostboxTransaction.swift b/submodules/Postbox/Sources/PostboxTransaction.swift index 056d9523ad..ab12fb1cb1 100644 --- a/submodules/Postbox/Sources/PostboxTransaction.swift +++ b/submodules/Postbox/Sources/PostboxTransaction.swift @@ -41,6 +41,9 @@ final class PostboxTransaction { let updatedNoticeEntryKeys: Set let updatedCacheEntryKeys: Set let updatedFailedMessagePeerIds: Set + let updatedFailedMessageIds: Set + + var isEmpty: Bool { if currentUpdatedState != nil { @@ -163,10 +166,13 @@ final class PostboxTransaction { if !updatedFailedMessagePeerIds.isEmpty { return false } + if !updatedFailedMessageIds.isEmpty { + return false + } return true } - init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadState: ChatListTotalUnreadState?, currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set) { + init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadState: ChatListTotalUnreadState?, currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set, updatedFailedMessageIds: Set) { self.currentUpdatedState = currentUpdatedState self.currentPeerHoleOperations = currentPeerHoleOperations self.currentOperationsByPeerId = currentOperationsByPeerId @@ -206,5 +212,6 @@ final class PostboxTransaction { self.updatedNoticeEntryKeys = updatedNoticeEntryKeys self.updatedCacheEntryKeys = updatedCacheEntryKeys self.updatedFailedMessagePeerIds = updatedFailedMessagePeerIds + self.updatedFailedMessageIds = updatedFailedMessageIds } } diff --git a/submodules/Postbox/Sources/ViewTracker.swift b/submodules/Postbox/Sources/ViewTracker.swift index 65b058a806..d85a3f8a50 100644 --- a/submodules/Postbox/Sources/ViewTracker.swift +++ b/submodules/Postbox/Sources/ViewTracker.swift @@ -55,6 +55,10 @@ final class ViewTracker { private var multiplePeersViews = Bag<(MutableMultiplePeersView, ValuePipe)>() private var itemCollectionsViews = Bag<(MutableItemCollectionsView, ValuePipe)>() + + private var failedMessageIdsViews = Bag<(MutableFailedMessageIdsView, ValuePipe)>() + + init(queue: Queue, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, getTotalUnreadState: @escaping () -> ChatListTotalUnreadState, getPeerReadState: @escaping (PeerId) -> CombinedPeerReadState?, operationLogGetOperations: @escaping (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], operationLogGetTailIndex: @escaping (PeerOperationLogTag) -> Int32?, getTimestampBasedMessageAttributesHead: @escaping (UInt16) -> TimestampBasedMessageAttributesEntry?, getPreferencesEntry: @escaping (ValueBoxKey) -> PreferencesEntry?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { self.queue = queue self.renderMessage = renderMessage @@ -438,6 +442,12 @@ final class ViewTracker { pipe.putNext(mutableView.immutableView()) } } + + for (view, pipe) in self.failedMessageIdsViews.copyItems() { + if view.replay(postbox: postbox, transaction: transaction) { + pipe.putNext(view.immutableView()) + } + } } private func updateTrackedChatListHoles() { @@ -584,4 +594,17 @@ final class ViewTracker { return disposable } } + + func addFailedMessageIdsView(_ view: MutableFailedMessageIdsView) -> (Bag<(MutableFailedMessageIdsView, ValuePipe)>.Index, Signal) { + let record = (view, ValuePipe()) + let index = self.failedMessageIdsViews.add(record) + + return (index, record.1.signal()) + } + + func removeFailedMessageIdsView(_ index: Bag<(MutableFailedMessageIdsView, ValuePipe)>.Index) { + self.failedMessageIdsViews.remove(index) + } + + } diff --git a/submodules/TelegramCore/Sources/GroupsInCommon.swift b/submodules/TelegramCore/Sources/GroupsInCommon.swift index a81c7c9213..4c4efd13c5 100644 --- a/submodules/TelegramCore/Sources/GroupsInCommon.swift +++ b/submodules/TelegramCore/Sources/GroupsInCommon.swift @@ -3,12 +3,12 @@ import Postbox import TelegramApi import SwiftSignalKit -public func groupsInCommon(account:Account, peerId:PeerId) -> Signal<[PeerId], NoError> { - return account.postbox.transaction { transaction -> Signal<[PeerId], NoError> in +public func groupsInCommon(account:Account, peerId:PeerId) -> Signal<[Peer], NoError> { + return account.postbox.transaction { transaction -> Signal<[Peer], NoError> in if let peer = transaction.getPeer(peerId), let inputUser = apiInputUser(peer) { return account.network.request(Api.functions.messages.getCommonChats(userId: inputUser, maxId: 0, limit: 100)) |> retryRequest - |> mapToSignal { result -> Signal<[PeerId], NoError> in + |> mapToSignal { result -> Signal<[Peer], NoError> in let chats: [Api.Chat] switch result { case let .chats(chats: apiChats): @@ -17,7 +17,7 @@ public func groupsInCommon(account:Account, peerId:PeerId) -> Signal<[PeerId], N chats = apiChats } - return account.postbox.transaction { transaction -> [PeerId] in + return account.postbox.transaction { transaction -> [Peer] in var peers:[Peer] = [] for chat in chats { if let peer = parseTelegramGroupOrChannel(chat: chat) { @@ -27,7 +27,7 @@ public func groupsInCommon(account:Account, peerId:PeerId) -> Signal<[PeerId], N updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer? in return updated }) - return peers.map {$0.id} + return peers } } } else {