diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 64e1748704..1240f957d3 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -420,14 +420,14 @@ open class NavigationController: UINavigationController, ContainableController, self.modalContainers.append(modalContainer) if !modalContainer.isReady { modalContainer.isReadyUpdated = { [weak self, weak modalContainer] in - guard let strongSelf = self, let modalContainer = modalContainer else { + guard let strongSelf = self, let _ = modalContainer else { return } strongSelf.updateContainersNonReentrant(transition: .animated(duration: 0.5, curve: .spring)) } } modalContainer.updateDismissProgress = { [weak self, weak modalContainer] _, transition in - guard let strongSelf = self, let modalContainer = modalContainer else { + guard let strongSelf = self, let _ = modalContainer else { return } strongSelf.updateContainersNonReentrant(transition: transition) @@ -454,7 +454,7 @@ open class NavigationController: UINavigationController, ContainableController, container.view.endEditing(true) } - transition = container.dismiss(transition: transition, completion: { [weak self, weak container] in + transition = container.dismiss(transition: transition, completion: { [weak container] in container?.removeFromSupernode() }) } diff --git a/submodules/Display/Source/Navigation/NavigationModalContainer.swift b/submodules/Display/Source/Navigation/NavigationModalContainer.swift index 58062e6f8f..aa40c78ee3 100644 --- a/submodules/Display/Source/Navigation/NavigationModalContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationModalContainer.swift @@ -437,7 +437,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes return } for controller in strongSelf.container.controllers { - controller.viewWillDisappear(transition.isAnimated) + controller.viewDidDisappear(transition.isAnimated) } completion() }) diff --git a/submodules/Postbox/Sources/ChatListHolesView.swift b/submodules/Postbox/Sources/ChatListHolesView.swift index a2fd52f789..c1c1d103db 100644 --- a/submodules/Postbox/Sources/ChatListHolesView.swift +++ b/submodules/Postbox/Sources/ChatListHolesView.swift @@ -3,6 +3,11 @@ import Foundation public struct ChatListHolesEntry: Hashable { public let groupId: PeerGroupId public let hole: ChatListHole + + public init(groupId: PeerGroupId, hole: ChatListHole) { + self.groupId = groupId + self.hole = hole + } } final class MutableChatListHolesView { diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index 131ecf4f60..c9b32485c6 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -528,7 +528,7 @@ final class MutableChatListView { } - func firstHole() -> ChatListHole? { + func firstHole() -> (PeerGroupId, ChatListHole)? { return self.sampledState.hole } diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index a9634f8552..d8a65aa588 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -99,7 +99,7 @@ private final class ChatListViewSpaceState { self.filterPredicate = filterPredicate self.summaryComponents = summaryComponents self.halfLimit = halfLimit - self.orderedEntries = OrderedChatListViewEntries(lowerOrAtAnchor: [], higherThanAnchor: []) + self.orderedEntries = OrderedChatListViewEntries(anchorIndex: anchorIndex.index, lowerOrAtAnchor: [], higherThanAnchor: []) self.fillSpace(postbox: postbox) } @@ -142,7 +142,7 @@ private final class ChatListViewSpaceState { if let lastMessage = lowerOrAtAnchorMessages.min(by: { $0.entryIndex < $1.entryIndex }) { nextLowerIndex = lastMessage.entryIndex.predecessor } else { - nextLowerIndex = resolvedAnchorIndex + nextLowerIndex = min(resolvedAnchorIndex, self.anchorIndex) } var loadedLowerMessages = Array(loadedMessages.filter({ $0.entryIndex <= nextLowerIndex }).reversed()) let lowerLimit = self.halfLimit - lowerOrAtAnchorMessages.count @@ -156,9 +156,9 @@ private final class ChatListViewSpaceState { if let lastMessage = higherThanAnchorMessages.max(by: { $0.entryIndex < $1.entryIndex }) { nextHigherIndex = lastMessage.entryIndex.successor } else { - nextHigherIndex = resolvedAnchorIndex + nextHigherIndex = max(resolvedAnchorIndex, self.anchorIndex.successor) } - var loadedHigherMessages = loadedMessages.filter({ $0.entryIndex >= nextHigherIndex }) + var loadedHigherMessages = loadedMessages.filter({ $0.entryIndex > nextHigherIndex }) let higherLimit = self.halfLimit - higherThanAnchorMessages.count if loadedHigherMessages.count > higherLimit { loadedHigherMessages.removeLast(loadedHigherMessages.count - higherLimit) @@ -198,7 +198,7 @@ private final class ChatListViewSpaceState { assert(Set(allIndices).count == allIndices.count) assert(allIndices.sorted() == allIndices) - let entries = OrderedChatListViewEntries(lowerOrAtAnchor: lowerOrAtAnchorMessages, higherThanAnchor: higherThanAnchorMessages) + let entries = OrderedChatListViewEntries(anchorIndex: self.anchorIndex.index, lowerOrAtAnchor: lowerOrAtAnchorMessages, higherThanAnchor: higherThanAnchorMessages) self.orderedEntries = entries } } @@ -571,12 +571,18 @@ private extension MutableChatListEntry { } private struct OrderedChatListViewEntries { + private let anchorIndex: ChatListIndex + private(set) var lowerOrAtAnchor: [MutableChatListEntry] private(set) var higherThanAnchor: [MutableChatListEntry] private(set) var reverseIndices: [PeerId: [MutableChatListEntryIndex]] = [:] - fileprivate init(lowerOrAtAnchor: [MutableChatListEntry], higherThanAnchor: [MutableChatListEntry]) { + fileprivate init(anchorIndex: ChatListIndex, lowerOrAtAnchor: [MutableChatListEntry], higherThanAnchor: [MutableChatListEntry]) { + self.anchorIndex = anchorIndex + assert(!lowerOrAtAnchor.contains(where: { $0.index > anchorIndex })) + assert(!higherThanAnchor.contains(where: { $0.index <= anchorIndex })) + self.lowerOrAtAnchor = lowerOrAtAnchor self.higherThanAnchor = higherThanAnchor @@ -601,6 +607,8 @@ private struct OrderedChatListViewEntries { } mutating func setLowerOrAtAnchorAtArrayIndex(_ index: Int, to value: MutableChatListEntry) { + assert(value.index <= self.anchorIndex) + let previousIndex = self.lowerOrAtAnchor[index].entryIndex let updatedIndex = value.entryIndex let previousPeerId = self.lowerOrAtAnchor[index].messagePeerId @@ -626,6 +634,8 @@ private struct OrderedChatListViewEntries { } mutating func setHigherThanAnchorAtArrayIndex(_ index: Int, to value: MutableChatListEntry) { + assert(value.index > self.anchorIndex) + let previousIndex = self.higherThanAnchor[index].entryIndex let updatedIndex = value.entryIndex let previousPeerId = self.higherThanAnchor[index].messagePeerId @@ -651,6 +661,7 @@ private struct OrderedChatListViewEntries { } mutating func insertLowerOrAtAnchorAtArrayIndex(_ index: Int, value: MutableChatListEntry) { + assert(value.index <= self.anchorIndex) self.lowerOrAtAnchor.insert(value, at: index) if let peerId = value.messagePeerId { @@ -663,6 +674,7 @@ private struct OrderedChatListViewEntries { } mutating func insertHigherThanAnchorAtArrayIndex(_ index: Int, value: MutableChatListEntry) { + assert(value.index > self.anchorIndex) self.higherThanAnchor.insert(value, at: index) if let peerId = value.messagePeerId { @@ -736,11 +748,13 @@ private struct OrderedChatListViewEntries { mutating func update(index: MutableChatListEntryIndex, _ f: (MutableChatListEntry) -> MutableChatListEntry?) -> Bool { if let entryIndex = binarySearch(self.lowerOrAtAnchor, extract: { $0.entryIndex }, searchItem: index) { if let updated = f(self.lowerOrAtAnchor[entryIndex]) { + assert(updated.index == self.lowerOrAtAnchor[entryIndex].index) self.setLowerOrAtAnchorAtArrayIndex(entryIndex, to: updated) return true } } else if let entryIndex = binarySearch(self.higherThanAnchor, extract: { $0.entryIndex }, searchItem: index) { if let updated = f(self.higherThanAnchor[entryIndex]) { + assert(updated.index == self.lowerOrAtAnchor[entryIndex].index) self.setHigherThanAnchorAtArrayIndex(entryIndex, to: updated) return true } @@ -766,9 +780,9 @@ final class ChatListViewSample { let lower: MutableChatListEntry? let upper: MutableChatListEntry? let anchorIndex: ChatListIndex - let hole: ChatListHole? + let hole: (PeerGroupId, ChatListHole)? - fileprivate init(entries: [MutableChatListEntry], lower: MutableChatListEntry?, upper: MutableChatListEntry?, anchorIndex: ChatListIndex, hole: ChatListHole?) { + fileprivate init(entries: [MutableChatListEntry], lower: MutableChatListEntry?, upper: MutableChatListEntry?, anchorIndex: ChatListIndex, hole: (PeerGroupId, ChatListHole)?) { self.entries = entries self.lower = lower self.upper = upper @@ -814,7 +828,9 @@ struct ChatListViewState { } var backwardsResult: [(ChatListViewSpace, Int)] = [] + var backwardsResultIndices: [ChatListIndex] = [] var result: [(ChatListViewSpace, Int)] = [] + var resultIndices: [ChatListIndex] = [] while true { var minSpace: ChatListViewSpace? @@ -831,6 +847,7 @@ struct ChatListViewState { } if let minSpace = minSpace { backwardsResult.append((minSpace, previousAnchorIndices[minSpace]!)) + backwardsResultIndices.append(self.stateBySpace[minSpace]!.orderedEntries.lowerOrAtAnchor[previousAnchorIndices[minSpace]!].index) previousAnchorIndices[minSpace]! -= 1 if backwardsResult.count == self.halfLimit { break @@ -857,6 +874,7 @@ struct ChatListViewState { } if let maxSpace = maxSpace { result.append((maxSpace, nextAnchorIndices[maxSpace]!)) + resultIndices.append(self.stateBySpace[maxSpace]!.orderedEntries.higherThanAnchor[nextAnchorIndices[maxSpace]!].index) nextAnchorIndices[maxSpace]! += 1 if result.count == self.halfLimit { break @@ -867,17 +885,26 @@ struct ChatListViewState { break } } + + backwardsResultIndices.reverse() + assert(backwardsResultIndices.sorted() == backwardsResultIndices) + assert(resultIndices.sorted() == resultIndices) + let combinedIndices = (backwardsResultIndices + resultIndices) + assert(combinedIndices.sorted() == combinedIndices) + return (backwardsResult.reversed(), result) } func sample(postbox: Postbox) -> ChatListViewSample { let combinedSpacesAndIndicesByDirection = self.sampleIndices() - var result: [MutableChatListEntry] = [] + var result: [(ChatListViewSpace, MutableChatListEntry)] = [] var sampledHoleIndices: [Int] = [] var sampledAnchorBoundaryIndex: Int? + var sampledHoleChatListIndices = Set() + let directions = [combinedSpacesAndIndicesByDirection.lowerOrAtAnchor, combinedSpacesAndIndicesByDirection.higherThanAnchor] for directionIndex in 0 ..< directions.count { outer: for i in 0 ..< directions[directionIndex].count { @@ -934,20 +961,26 @@ struct ChatListViewState { } else { self.stateBySpace[space]!.orderedEntries.setHigherThanAnchorAtArrayIndex(listIndex, to: updatedEntry) } - result.append(updatedEntry) + result.append((space, updatedEntry)) case .MessageEntry: - result.append(entry) + result.append((space, entry)) case .HoleEntry: - sampledHoleIndices.append(result.count) - - result.append(entry) + if !sampledHoleChatListIndices.contains(entry.index) { + sampledHoleChatListIndices.insert(entry.index) + sampledHoleIndices.append(result.count) + + result.append((space, entry)) + } } } } - let allIndices = result.map { $0.entryIndex } + let allIndices = result.map { $0.1.entryIndex } + let allIndicesSorted = allIndices.sorted() + for i in 0 ..< allIndicesSorted.count { + assert(allIndicesSorted[i] == allIndices[i]) + } assert(Set(allIndices).count == allIndices.count) - assert(allIndices.sorted() == allIndices) var sampledHoleIndex: Int? if !sampledHoleIndices.isEmpty { @@ -968,10 +1001,11 @@ struct ChatListViewState { } } - var sampledHole: ChatListHole? + var sampledHole: (ChatListViewSpace, ChatListHole)? if let index = sampledHoleIndex { - if case let .HoleEntry(hole) = result[index] { - sampledHole = hole + let (space, entry) = result[index] + if case let .HoleEntry(hole) = entry { + sampledHole = (space, hole) } else { assertionFailure() } @@ -979,16 +1013,21 @@ struct ChatListViewState { var lower: MutableChatListEntry? if combinedSpacesAndIndicesByDirection.lowerOrAtAnchor.count >= self.halfLimit { - lower = result[0] + lower = result[0].1 result.removeFirst() } var upper: MutableChatListEntry? if combinedSpacesAndIndicesByDirection.higherThanAnchor.count >= self.halfLimit { - upper = result.last + upper = result.last?.1 result.removeLast() } - return ChatListViewSample(entries: result, lower: lower, upper: upper, anchorIndex: self.anchorIndex.index, hole: sampledHole) + return ChatListViewSample(entries: result.map { $0.1 }, lower: lower, upper: upper, anchorIndex: self.anchorIndex.index, hole: sampledHole.flatMap { space, hole in + switch space { + case let .group(groupId, _): + return (groupId, hole) + } + }) } } diff --git a/submodules/Postbox/Sources/ViewTracker.swift b/submodules/Postbox/Sources/ViewTracker.swift index d85a3f8a50..408fd8d842 100644 --- a/submodules/Postbox/Sources/ViewTracker.swift +++ b/submodules/Postbox/Sources/ViewTracker.swift @@ -455,7 +455,7 @@ final class ViewTracker { for (view, _) in self.chatListViews.copyItems() { if let hole = view.firstHole() { - firstHoles.insert(ChatListHolesEntry(groupId: view.groupId, hole: hole)) + firstHoles.insert(ChatListHolesEntry(groupId: hole.0, hole: hole.1)) } } diff --git a/submodules/TelegramCore/Sources/ManagedChatListHoles.swift b/submodules/TelegramCore/Sources/ManagedChatListHoles.swift index dca1df5fad..aaa8353268 100644 --- a/submodules/TelegramCore/Sources/ManagedChatListHoles.swift +++ b/submodules/TelegramCore/Sources/ManagedChatListHoles.swift @@ -79,11 +79,15 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee var additionalLatestArchiveHole: ChatListHole? if let preferencesView = combinedView.views[filtersKey] as? PreferencesView, let filtersState = preferencesView.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState, !filtersState.filters.isEmpty { - if let topRootHole = combinedView.views[topRootHoleKey] as? AllChatListHolesView { - additionalLatestHole = topRootHole.latestHole + if let topRootHole = combinedView.views[topRootHoleKey] as? AllChatListHolesView, let hole = topRootHole.latestHole { + if !view.entries.contains(ChatListHolesEntry(groupId: .root, hole: hole)) { + additionalLatestHole = hole + } } - if let topArchiveHole = combinedView.views[topArchiveHoleKey] as? AllChatListHolesView { - additionalLatestArchiveHole = topArchiveHole.latestHole + if let topArchiveHole = combinedView.views[topArchiveHoleKey] as? AllChatListHolesView, let hole = topArchiveHole.latestHole { + if !view.entries.contains(ChatListHolesEntry(groupId: Namespaces.PeerGroup.archive, hole: hole)) { + additionalLatestArchiveHole = hole + } } } @@ -104,7 +108,7 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee } if let (hole, disposable) = addedAdditionalLatestArchiveHole { - disposable.set(fetchChatListHole(postbox: postbox, network: network, accountPeerId: accountPeerId, groupId: .root, hole: hole).start()) + disposable.set(fetchChatListHole(postbox: postbox, network: network, accountPeerId: accountPeerId, groupId: Namespaces.PeerGroup.archive, hole: hole).start()) } })