Move folder preloading to filter resolution

This commit is contained in:
Ali 2020-03-20 19:59:38 +04:00
parent 957726219b
commit a52334046d
3 changed files with 186 additions and 186 deletions

View File

@ -1044,7 +1044,7 @@ public class Account {
self.managedOperationsDisposable.add(managedApplyPendingMessageReactionsActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) 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(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(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<AccountRunningImportantTasks, NoError>] = [ let importantBackgroundOperations: [Signal<AccountRunningImportantTasks, NoError>] = [
managedSynchronizeChatInputStateOperations(postbox: self.postbox, network: self.network) |> map { $0 ? AccountRunningImportantTasks.other : [] }, managedSynchronizeChatInputStateOperations(postbox: self.postbox, network: self.network) |> map { $0 ? AccountRunningImportantTasks.other : [] },

View File

@ -351,16 +351,18 @@ private enum RequestChatListFiltersError {
case generic 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()) return network.request(Api.functions.messages.getDialogFilters())
|> mapError { _ -> RequestChatListFiltersError in |> mapError { _ -> RequestChatListFiltersError in
return .generic return .generic
} }
|> mapToSignal { result -> Signal<[ChatListFilter], RequestChatListFiltersError> in |> 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 filters: [ChatListFilter] = []
var missingPeers: [Api.InputPeer] = [] var missingPeers: [Api.InputPeer] = []
var missingChats: [Api.InputPeer] = []
var missingPeerIds = Set<PeerId>() var missingPeerIds = Set<PeerId>()
var missingChatIds = Set<PeerId>()
for apiFilter in result { for apiFilter in result {
let filter = ChatListFilter(apiFilter: apiFilter) let filter = ChatListFilter(apiFilter: apiFilter)
filters.append(filter) 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) |> castError(RequestChatListFiltersError.self)
|> mapToSignal { filtersAndMissingPeers -> Signal<[ChatListFilter], RequestChatListFiltersError> in |> mapToSignal { filtersAndMissingPeers -> Signal<[ChatListFilter], RequestChatListFiltersError> in
let (filters, missingPeers) = filtersAndMissingPeers let (filters, missingPeers, missingChats) = filtersAndMissingPeers
var missingUsers: [Api.InputUser] = [] var missingUsers: [Api.InputUser] = []
var missingChannels: [Api.InputChannel] = [] var missingChannels: [Api.InputChannel] = []
@ -493,6 +515,13 @@ private func requestChatListFilters(postbox: Postbox, network: Network) -> Signa
resolveMissingGroups = .complete() resolveMissingGroups = .complete()
} }
let loadMissingChats: Signal<Never, NoError>
if !missingChats.isEmpty {
loadMissingChats = loadAndStorePeerChatInfos(accountPeerId: accountPeerId, postbox: postbox, network: network, peers: missingChats)
} else {
loadMissingChats = .complete()
}
return ( return (
resolveMissingUsers resolveMissingUsers
) )
@ -502,6 +531,9 @@ private func requestChatListFilters(postbox: Postbox, network: Network) -> Signa
|> then( |> then(
resolveMissingGroups resolveMissingGroups
) )
|> then(
loadMissingChats
)
|> castError(RequestChatListFiltersError.self) |> castError(RequestChatListFiltersError.self)
|> mapToSignal { _ -> Signal<[ChatListFilter], RequestChatListFiltersError> in |> mapToSignal { _ -> Signal<[ChatListFilter], RequestChatListFiltersError> in
#if swift(<5.1) #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<Never, NoError> {
let signal = network.request(Api.functions.messages.getPeerDialogs(peers: peers.map(Api.InputDialogPeer.inputDialogPeer(peer:))))
|> map(Optional.init)
return signal
|> `catch` { _ -> Signal<Api.messages.PeerDialogs?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Never, NoError> 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<MessageId>()
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 { struct ChatListFiltersState: PreferencesEntry, Equatable {
var filters: [ChatListFilter] var filters: [ChatListFilter]
var remoteFilters: [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)) transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeChatListFiltersOperation(content: .sync))
} }
func managedChatListFilters(postbox: Postbox, network: Network) -> Signal<Void, NoError> { func managedChatListFilters(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<Void, NoError> {
return Signal { _ in return Signal { _ in
let updateFeaturedDisposable = updateChatListFeaturedFilters(postbox: postbox, network: network).start() let updateFeaturedDisposable = updateChatListFeaturedFilters(postbox: postbox, network: network).start()
let _ = postbox.transaction({ transaction in let _ = postbox.transaction({ transaction in
@ -901,7 +1074,7 @@ func managedChatListFilters(postbox: Postbox, network: Network) -> Signal<Void,
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Never, NoError> in let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Never, NoError> in
if let entry = entry { if let entry = entry {
if let operation = entry.contents as? SynchronizeChatListFiltersOperation { 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 { } else {
assertionFailure() assertionFailure()
} }
@ -933,14 +1106,14 @@ func managedChatListFilters(postbox: Postbox, network: Network) -> Signal<Void,
} }
} }
private func synchronizeChatListFilters(transaction: Transaction, postbox: Postbox, network: Network, operation: SynchronizeChatListFiltersOperation) -> Signal<Never, NoError> { private func synchronizeChatListFilters(transaction: Transaction, accountPeerId: PeerId, postbox: Postbox, network: Network, operation: SynchronizeChatListFiltersOperation) -> Signal<Never, NoError> {
switch operation.content { switch operation.content {
case .sync: case .sync:
let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default
let localFilters = settings.filters let localFilters = settings.filters
let locallyKnownRemoteFilters = settings.remoteFilters ?? [] let locallyKnownRemoteFilters = settings.remoteFilters ?? []
return requestChatListFilters(postbox: postbox, network: network) return requestChatListFilters(accountPeerId: accountPeerId, postbox: postbox, network: network)
|> `catch` { _ -> Signal<[ChatListFilter], NoError> in |> `catch` { _ -> Signal<[ChatListFilter], NoError> in
return .complete() return .complete()
} }

View File

@ -6,8 +6,6 @@ import TelegramApi
private final class ManagedChatListHolesState { private final class ManagedChatListHolesState {
private var currentHole: (ChatListHolesEntry, Disposable)? private var currentHole: (ChatListHolesEntry, Disposable)?
private var currentPinnedIds: (Set<PeerId>, Disposable)?
private var processedPinnedIds: Set<PeerId>?
func clearDisposables() -> [Disposable] { func clearDisposables() -> [Disposable] {
if let (_, disposable) = self.currentHole { if let (_, disposable) = self.currentHole {
@ -18,17 +16,9 @@ private final class ManagedChatListHolesState {
} }
} }
func update(entries: [ChatListHolesEntry], pinnedIds: Set<PeerId>) -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable], addedPinnedIds: (Set<PeerId>, MetaDisposable)?) { func update(entries: [ChatListHolesEntry]) -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable]) {
var removed: [Disposable] = [] var removed: [Disposable] = []
var added: [ChatListHolesEntry: MetaDisposable] = [:] var added: [ChatListHolesEntry: MetaDisposable] = [:]
var addedPinnedIds: (Set<PeerId>, 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 let (entry, disposable) = self.currentHole {
if !entries.contains(entry) { if !entries.contains(entry) {
@ -43,7 +33,7 @@ private final class ManagedChatListHolesState {
added[entry] = disposable 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 return lhs.hole.index > rhs.hole.index
}) })
var pinnedIds = Set<PeerId>()
if let preferencesView = combinedView.views[filtersKey] as? PreferencesView, let filtersState = preferencesView.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState, !filtersState.filters.isEmpty { 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 { if let topRootHole = combinedView.views[topRootHoleKey] as? AllChatListHolesView, let hole = topRootHole.latestHole {
let entry = ChatListHolesEntry(groupId: .root, hole: hole) let entry = ChatListHolesEntry(groupId: .root, hole: hole)
if !entries.contains(entry) { if !entries.contains(entry) {
@ -84,8 +68,8 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee
} }
} }
let (removed, added, addedPinnedIds) = state.with { state in let (removed, added) = state.with { state in
return state.update(entries: entries, pinnedIds: pinnedIds) return state.update(entries: entries)
} }
for disposable in removed { for disposable in removed {
@ -95,22 +79,6 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee
for (entry, disposable) in added { for (entry, disposable) in added {
disposable.set(fetchChatListHole(postbox: postbox, network: network, accountPeerId: accountPeerId, groupId: entry.groupId, hole: entry.hole).start()) 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<Never, NoError> in
return loadAndStorePeerChatInfos(accountPeerId: accountPeerId, postbox: postbox, network: network, peers: inputPeers)
}
disposable.set(signal.start())
}
}) })
return ActionDisposable { 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<Never, NoError> {
let signal = network.request(Api.functions.messages.getPeerDialogs(peers: peers.map(Api.InputDialogPeer.inputDialogPeer(peer:))))
|> map(Optional.init)
return signal
|> `catch` { _ -> Signal<Api.messages.PeerDialogs?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Never, NoError> 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<MessageId>()
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
}
}