From a52334046d16faef974d2649bfb2616a3749dd9c Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 20 Mar 2020 19:59:38 +0400 Subject: [PATCH] Move folder preloading to filter resolution --- submodules/TelegramCore/Sources/Account.swift | 2 +- .../Sources/ChatListFiltering.swift | 189 +++++++++++++++++- .../Sources/ManagedChatListHoles.swift | 181 +---------------- 3 files changed, 186 insertions(+), 186 deletions(-) diff --git a/submodules/TelegramCore/Sources/Account.swift b/submodules/TelegramCore/Sources/Account.swift index be82e24425..a209643aca 100644 --- a/submodules/TelegramCore/Sources/Account.swift +++ b/submodules/TelegramCore/Sources/Account.swift @@ -1044,7 +1044,7 @@ public class Account { self.managedOperationsDisposable.add(managedApplyPendingMessageReactionsActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedSynchronizeEmojiKeywordsOperations(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedApplyPendingScheduledMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) - self.managedOperationsDisposable.add(managedChatListFilters(postbox: self.postbox, network: self.network).start()) + self.managedOperationsDisposable.add(managedChatListFilters(postbox: self.postbox, network: self.network, accountPeerId: self.peerId).start()) let importantBackgroundOperations: [Signal] = [ managedSynchronizeChatInputStateOperations(postbox: self.postbox, network: self.network) |> map { $0 ? AccountRunningImportantTasks.other : [] }, diff --git a/submodules/TelegramCore/Sources/ChatListFiltering.swift b/submodules/TelegramCore/Sources/ChatListFiltering.swift index fb4a93f75d..22652ae25c 100644 --- a/submodules/TelegramCore/Sources/ChatListFiltering.swift +++ b/submodules/TelegramCore/Sources/ChatListFiltering.swift @@ -351,16 +351,18 @@ private enum RequestChatListFiltersError { case generic } -private func requestChatListFilters(postbox: Postbox, network: Network) -> Signal<[ChatListFilter], RequestChatListFiltersError> { +private func requestChatListFilters(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal<[ChatListFilter], RequestChatListFiltersError> { return network.request(Api.functions.messages.getDialogFilters()) |> mapError { _ -> RequestChatListFiltersError in return .generic } |> mapToSignal { result -> Signal<[ChatListFilter], RequestChatListFiltersError> in - return postbox.transaction { transaction -> ([ChatListFilter], [Api.InputPeer]) in + return postbox.transaction { transaction -> ([ChatListFilter], [Api.InputPeer], [Api.InputPeer]) in var filters: [ChatListFilter] = [] var missingPeers: [Api.InputPeer] = [] + var missingChats: [Api.InputPeer] = [] var missingPeerIds = Set() + var missingChatIds = Set() for apiFilter in result { let filter = ChatListFilter(apiFilter: apiFilter) filters.append(filter) @@ -385,13 +387,33 @@ private func requestChatListFilters(postbox: Postbox, network: Network) -> Signa } } } + + for peer in pinnedPeers { + var peerId: PeerId? + switch peer { + case let .inputPeerUser(userId, _): + peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + case let .inputPeerChat(chatId): + peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) + case let .inputPeerChannel(channelId, _): + peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) + default: + break + } + if let peerId = peerId, !missingChatIds.contains(peerId) { + if transaction.getPeerChatListIndex(peerId) == nil { + missingChatIds.insert(peerId) + missingChats.append(peer) + } + } + } } } - return (filters, missingPeers) + return (filters, missingPeers, missingChats) } |> castError(RequestChatListFiltersError.self) |> mapToSignal { filtersAndMissingPeers -> Signal<[ChatListFilter], RequestChatListFiltersError> in - let (filters, missingPeers) = filtersAndMissingPeers + let (filters, missingPeers, missingChats) = filtersAndMissingPeers var missingUsers: [Api.InputUser] = [] var missingChannels: [Api.InputChannel] = [] @@ -493,6 +515,13 @@ private func requestChatListFilters(postbox: Postbox, network: Network) -> Signa resolveMissingGroups = .complete() } + let loadMissingChats: Signal + if !missingChats.isEmpty { + loadMissingChats = loadAndStorePeerChatInfos(accountPeerId: accountPeerId, postbox: postbox, network: network, peers: missingChats) + } else { + loadMissingChats = .complete() + } + return ( resolveMissingUsers ) @@ -502,6 +531,9 @@ private func requestChatListFilters(postbox: Postbox, network: Network) -> Signa |> then( resolveMissingGroups ) + |> then( + loadMissingChats + ) |> castError(RequestChatListFiltersError.self) |> mapToSignal { _ -> Signal<[ChatListFilter], RequestChatListFiltersError> in #if swift(<5.1) @@ -515,6 +547,147 @@ private func requestChatListFilters(postbox: Postbox, network: Network) -> Signa } } +private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, network: Network, peers: [Api.InputPeer]) -> Signal { + let signal = network.request(Api.functions.messages.getPeerDialogs(peers: peers.map(Api.InputDialogPeer.inputDialogPeer(peer:)))) + |> map(Optional.init) + + return signal + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result = result else { + return .complete() + } + + return postbox.transaction { transaction -> Void in + var peers: [Peer] = [] + var peerPresences: [PeerId: PeerPresence] = [:] + var notificationSettings: [PeerId: PeerNotificationSettings] = [:] + var channelStates: [PeerId: ChannelState] = [:] + + switch result { + case let .peerDialogs(dialogs, messages, chats, users, _): + for chat in chats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + peers.append(groupOrChannel) + } + } + for user in users { + let telegramUser = TelegramUser(user: user) + peers.append(telegramUser) + if let presence = TelegramUserPresence(apiUser: user) { + peerPresences[telegramUser.id] = presence + } + } + + var topMessageIds = Set() + + for dialog in dialogs { + switch dialog { + case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, notifySettings, pts, _, folderId): + let peerId = peer.peerId + + if topMessage != 0 { + topMessageIds.insert(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: topMessage)) + } + + var isExcludedFromChatList = false + for chat in chats { + if chat.peerId == peerId { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + if let group = groupOrChannel as? TelegramGroup { + if group.flags.contains(.deactivated) { + isExcludedFromChatList = true + } else { + switch group.membership { + case .Member: + break + default: + isExcludedFromChatList = true + } + } + } else if let channel = groupOrChannel as? TelegramChannel { + switch channel.participationStatus { + case .member: + break + default: + isExcludedFromChatList = true + } + } + } + break + } + } + + if !isExcludedFromChatList { + let groupId = PeerGroupId(rawValue: folderId ?? 0) + let currentInclusion = transaction.getPeerChatListInclusion(peerId) + var currentPinningIndex: UInt16? + var currentMinTimestamp: Int32? + switch currentInclusion { + case let .ifHasMessagesOrOneOf(currentGroupId, pinningIndex, minTimestamp): + if currentGroupId == groupId { + currentPinningIndex = pinningIndex + } + currentMinTimestamp = minTimestamp + default: + break + } + transaction.updatePeerChatListInclusion(peerId, inclusion: .ifHasMessagesOrOneOf(groupId: groupId, pinningIndex: currentPinningIndex, minTimestamp: currentMinTimestamp)) + } + + notificationSettings[peer.peerId] = TelegramPeerNotificationSettings(apiSettings: notifySettings) + + transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: .idBased(maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: false)]]) + + transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage) + + if let pts = pts { + let channelState = ChannelState(pts: pts, invalidatedPts: pts) + transaction.setPeerChatState(peerId, state: channelState) + channelStates[peer.peerId] = channelState + } + case .dialogFolder: + assertionFailure() + break + } + } + + var storeMessages: [StoreMessage] = [] + for message in messages { + if let storeMessage = StoreMessage(apiMessage: message) { + var updatedStoreMessage = storeMessage + if case let .Id(id) = storeMessage.id { + if let channelState = channelStates[id.peerId] { + var updatedAttributes = storeMessage.attributes + updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts)) + updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes) + } + } + storeMessages.append(updatedStoreMessage) + } + } + + for message in storeMessages { + if case let .Id(id) = message.id { + let _ = transaction.addMessages([message], location: topMessageIds.contains(id) ? .UpperHistoryBlock : .Random) + } + } + } + + updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in + return updated + }) + + updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) + + transaction.updateCurrentPeerNotificationSettings(notificationSettings) + } + |> ignoreValues + } +} + struct ChatListFiltersState: PreferencesEntry, Equatable { var filters: [ChatListFilter] var remoteFilters: [ChatListFilter]? @@ -877,7 +1050,7 @@ func requestChatListFiltersSync(transaction: Transaction) { transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeChatListFiltersOperation(content: .sync)) } -func managedChatListFilters(postbox: Postbox, network: Network) -> Signal { +func managedChatListFilters(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal { return Signal { _ in let updateFeaturedDisposable = updateChatListFeaturedFilters(postbox: postbox, network: network).start() let _ = postbox.transaction({ transaction in @@ -901,7 +1074,7 @@ func managedChatListFilters(postbox: Postbox, network: Network) -> Signal Signal in if let entry = entry { if let operation = entry.contents as? SynchronizeChatListFiltersOperation { - return synchronizeChatListFilters(transaction: transaction, postbox: postbox, network: network, operation: operation) + return synchronizeChatListFilters(transaction: transaction, accountPeerId: accountPeerId, postbox: postbox, network: network, operation: operation) } else { assertionFailure() } @@ -933,14 +1106,14 @@ func managedChatListFilters(postbox: Postbox, network: Network) -> Signal Signal { +private func synchronizeChatListFilters(transaction: Transaction, accountPeerId: PeerId, postbox: Postbox, network: Network, operation: SynchronizeChatListFiltersOperation) -> Signal { switch operation.content { case .sync: let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default let localFilters = settings.filters let locallyKnownRemoteFilters = settings.remoteFilters ?? [] - return requestChatListFilters(postbox: postbox, network: network) + return requestChatListFilters(accountPeerId: accountPeerId, postbox: postbox, network: network) |> `catch` { _ -> Signal<[ChatListFilter], NoError> in return .complete() } diff --git a/submodules/TelegramCore/Sources/ManagedChatListHoles.swift b/submodules/TelegramCore/Sources/ManagedChatListHoles.swift index bebb9ebefe..e943f93d8a 100644 --- a/submodules/TelegramCore/Sources/ManagedChatListHoles.swift +++ b/submodules/TelegramCore/Sources/ManagedChatListHoles.swift @@ -6,8 +6,6 @@ import TelegramApi private final class ManagedChatListHolesState { private var currentHole: (ChatListHolesEntry, Disposable)? - private var currentPinnedIds: (Set, Disposable)? - private var processedPinnedIds: Set? func clearDisposables() -> [Disposable] { if let (_, disposable) = self.currentHole { @@ -18,17 +16,9 @@ private final class ManagedChatListHolesState { } } - func update(entries: [ChatListHolesEntry], pinnedIds: Set) -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable], addedPinnedIds: (Set, MetaDisposable)?) { + func update(entries: [ChatListHolesEntry]) -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable]) { var removed: [Disposable] = [] var added: [ChatListHolesEntry: MetaDisposable] = [:] - var addedPinnedIds: (Set, MetaDisposable)? - - if self.processedPinnedIds == nil && !pinnedIds.isEmpty { - self.processedPinnedIds = pinnedIds - let disposable = MetaDisposable() - self.currentPinnedIds = (pinnedIds, disposable) - addedPinnedIds = (pinnedIds, disposable) - } if let (entry, disposable) = self.currentHole { if !entries.contains(entry) { @@ -43,7 +33,7 @@ private final class ManagedChatListHolesState { added[entry] = disposable } - return (removed, added, addedPinnedIds) + return (removed, added) } } @@ -61,13 +51,7 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee return lhs.hole.index > rhs.hole.index }) - var pinnedIds = Set() - if let preferencesView = combinedView.views[filtersKey] as? PreferencesView, let filtersState = preferencesView.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState, !filtersState.filters.isEmpty { - for filter in filtersState.filters { - pinnedIds.formUnion(filter.data.includePeers.pinnedPeers) - } - if let topRootHole = combinedView.views[topRootHoleKey] as? AllChatListHolesView, let hole = topRootHole.latestHole { let entry = ChatListHolesEntry(groupId: .root, hole: hole) if !entries.contains(entry) { @@ -84,8 +68,8 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee } } - let (removed, added, addedPinnedIds) = state.with { state in - return state.update(entries: entries, pinnedIds: pinnedIds) + let (removed, added) = state.with { state in + return state.update(entries: entries) } for disposable in removed { @@ -95,22 +79,6 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee for (entry, disposable) in added { disposable.set(fetchChatListHole(postbox: postbox, network: network, accountPeerId: accountPeerId, groupId: entry.groupId, hole: entry.hole).start()) } - - if let (ids, disposable) = addedPinnedIds { - let signal = postbox.transaction { transaction -> [Api.InputPeer] in - var peers: [Api.InputPeer] = [] - for id in ids { - if let inputPeer = transaction.getPeer(id).flatMap(apiInputPeer) { - peers.append(inputPeer) - } - } - return peers - } - |> mapToSignal { inputPeers -> Signal in - return loadAndStorePeerChatInfos(accountPeerId: accountPeerId, postbox: postbox, network: network, peers: inputPeers) - } - disposable.set(signal.start()) - } }) return ActionDisposable { @@ -123,144 +91,3 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee } } } - -private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, network: Network, peers: [Api.InputPeer]) -> Signal { - let signal = network.request(Api.functions.messages.getPeerDialogs(peers: peers.map(Api.InputDialogPeer.inputDialogPeer(peer:)))) - |> map(Optional.init) - - return signal - |> `catch` { _ -> Signal in - return .single(nil) - } - |> mapToSignal { result -> Signal in - guard let result = result else { - return .complete() - } - - return postbox.transaction { transaction -> Void in - var peers: [Peer] = [] - var peerPresences: [PeerId: PeerPresence] = [:] - var notificationSettings: [PeerId: PeerNotificationSettings] = [:] - var channelStates: [PeerId: ChannelState] = [:] - - switch result { - case let .peerDialogs(dialogs, messages, chats, users, _): - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - if let presence = TelegramUserPresence(apiUser: user) { - peerPresences[telegramUser.id] = presence - } - } - - var topMessageIds = Set() - - for dialog in dialogs { - switch dialog { - case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, notifySettings, pts, _, folderId): - let peerId = peer.peerId - - if topMessage != 0 { - topMessageIds.insert(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: topMessage)) - } - - var isExcludedFromChatList = false - for chat in chats { - if chat.peerId == peerId { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - if let group = groupOrChannel as? TelegramGroup { - if group.flags.contains(.deactivated) { - isExcludedFromChatList = true - } else { - switch group.membership { - case .Member: - break - default: - isExcludedFromChatList = true - } - } - } else if let channel = groupOrChannel as? TelegramChannel { - switch channel.participationStatus { - case .member: - break - default: - isExcludedFromChatList = true - } - } - } - break - } - } - - if !isExcludedFromChatList { - let groupId = PeerGroupId(rawValue: folderId ?? 0) - let currentInclusion = transaction.getPeerChatListInclusion(peerId) - var currentPinningIndex: UInt16? - var currentMinTimestamp: Int32? - switch currentInclusion { - case let .ifHasMessagesOrOneOf(currentGroupId, pinningIndex, minTimestamp): - if currentGroupId == groupId { - currentPinningIndex = pinningIndex - } - currentMinTimestamp = minTimestamp - default: - break - } - transaction.updatePeerChatListInclusion(peerId, inclusion: .ifHasMessagesOrOneOf(groupId: groupId, pinningIndex: currentPinningIndex, minTimestamp: currentMinTimestamp)) - } - - notificationSettings[peer.peerId] = TelegramPeerNotificationSettings(apiSettings: notifySettings) - - transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: .idBased(maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: false)]]) - - transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage) - - if let pts = pts { - let channelState = ChannelState(pts: pts, invalidatedPts: pts) - transaction.setPeerChatState(peerId, state: channelState) - channelStates[peer.peerId] = channelState - } - case .dialogFolder: - assertionFailure() - break - } - } - - var storeMessages: [StoreMessage] = [] - for message in messages { - if let storeMessage = StoreMessage(apiMessage: message) { - var updatedStoreMessage = storeMessage - if case let .Id(id) = storeMessage.id { - if let channelState = channelStates[id.peerId] { - var updatedAttributes = storeMessage.attributes - updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts)) - updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes) - } - } - storeMessages.append(updatedStoreMessage) - } - } - - for message in storeMessages { - if case let .Id(id) = message.id { - let _ = transaction.addMessages([message], location: topMessageIds.contains(id) ? .UpperHistoryBlock : .Random) - } - } - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - - updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) - - transaction.updateCurrentPeerNotificationSettings(notificationSettings) - } - |> ignoreValues - } -}