Skip to content

Commit 30bc51f

Browse files
committed
Fix chat folder duplication regression on tab switch
Root cause: isActiveForFolderPagination defaulted back to true (e2ed943), so adjacent preloaded folder tabs paginated in background and corrupted list state when switched to. - Default pagination gate to false; opt in for standalone lists - Explicitly gate adjacent itemNodes in ChatListContainerNode.update - deactivateFolderPagination() resets navigation on tab switch-out - Apply-time locationGeneration guard in dequeueTransition - Honor animated:false on switchToFilter (tab taps use immediate transition) Co-authored-by: D3C0Y <decoder-dev@users.noreply.github.com>
1 parent 4608fc7 commit 30bc51f

5 files changed

Lines changed: 28 additions & 15 deletions

File tree

‎submodules/ChatListUI/Sources/ChatListControllerNode.swift‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
177177
previousItemNode.listNode.addedVisibleChatsWithPeerIds = nil
178178
previousItemNode.listNode.didBeginSelectingChats = nil
179179
previousItemNode.listNode.canExpandHiddenItems = nil
180-
previousItemNode.listNode.isActiveForFolderPagination = false
180+
previousItemNode.listNode.deactivateFolderPagination()
181181

182182
previousItemNode.accessibilityElementsHidden = true
183183
}
@@ -906,7 +906,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
906906

907907
self.selectedId = id
908908
self.applyItemNodeAsCurrent(id: id, itemNode: itemNode)
909-
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .spring)
909+
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.35, curve: .spring) : .immediate
910910
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: transition)
911911
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
912912
self.pinnedHeaderDisplayFractionUpdated?(transition)
@@ -922,6 +922,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
922922
}, openArchiveSettings: { [weak self] in
923923
self?.openArchiveSettings()
924924
}, autoSetReady: !animated, isMainTab: index == 0)
925+
itemNode.listNode.isActiveForFolderPagination = false
925926
self.pendingItemNode?.2.dispose()
926927
let disposable = MetaDisposable()
927928
self.pendingItemNode = (id, itemNode, disposable)
@@ -1077,6 +1078,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
10771078
self?.openArchiveSettings()
10781079
}, autoSetReady: false, isMainTab: i == 0)
10791080
itemNode.listNode.tempTopInset = self.tempTopInset
1081+
itemNode.listNode.isActiveForFolderPagination = false
10801082
self.itemNodes[id] = itemNode
10811083
}
10821084
}
@@ -1115,6 +1117,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
11151117
}
11161118

11171119
itemNode.listNode.isMainTab.set(self.availableFilters.firstIndex(where: { $0.id == id }) == 0)
1120+
itemNode.listNode.isActiveForFolderPagination = (id == self.selectedId)
11181121
itemNode.updateLayout(size: layout.size, insets: insets, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: itemInlineNavigationTransitionFraction, storiesInset: storiesInset, transition: nodeTransition)
11191122
if let scrollingOffset = self.scrollingOffset {
11201123
itemNode.updateScrollingOffset(navigationHeight: scrollingOffset.navigationHeight, offset: scrollingOffset.offset, transition: nodeTransition)

‎submodules/ChatListUI/Sources/Node/ChatListNode.swift‎

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct ChatListNodeListViewTransition {
4848
let stationaryItemRange: (Int, Int)?
4949
let adjustScrollToFirstItem: Bool
5050
let animateCrossfade: Bool
51+
let locationGeneration: Int
5152
}
5253

5354
final class ChatListHighlightedLocation: Equatable {
@@ -1171,9 +1172,10 @@ private func mappedChatListNodeViewListTransition(
11711172
mode: ChatListNodeMode,
11721173
isPeerEnabled: ((EnginePeer) -> Bool)?,
11731174
presentationData: ChatListPresentationData,
1174-
transition: ChatListNodeViewTransition
1175+
transition: ChatListNodeViewTransition,
1176+
locationGeneration: Int
11751177
) -> ChatListNodeListViewTransition {
1176-
return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: isPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, entries: transition.insertEntries, presentationData: presentationData), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: isPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, entries: transition.updateEntries, presentationData: presentationData), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, adjustScrollToFirstItem: transition.adjustScrollToFirstItem, animateCrossfade: transition.animateCrossfade)
1178+
return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: isPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, entries: transition.insertEntries, presentationData: presentationData), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: isPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, entries: transition.updateEntries, presentationData: presentationData), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, adjustScrollToFirstItem: transition.adjustScrollToFirstItem, animateCrossfade: transition.animateCrossfade, locationGeneration: locationGeneration)
11771179
}
11781180

11791181
private final class ChatListOpaqueTransactionState {
@@ -1300,16 +1302,9 @@ public final class ChatListNode: ListViewImpl {
13001302

13011303
private var currentLocation: ChatListNodeLocation?
13021304
private var chatListLocationGeneration: Int = 0
1303-
/// Gates `displayedItemRangeChanged` — pagination, history preload and story-peer refresh.
1304-
///
1305-
/// Defaults to true. `ChatListContainerNode` turns it off on the tab being switched away from
1306-
/// and back on for the one becoming current, which is the folder-duplication fix; but not every
1307-
/// `ChatListNode` lives in that container. `PeerSelectionControllerNode` (Forward to…) and
1308-
/// `ContactMultiselectionControllerNode` (add members) build one directly and never call
1309-
/// `reconcileLocationOnTabActivation`, so defaulting to false left those lists stuck on their
1310-
/// first window — scrolling down simply ran out of chats. True is also what every list did
1311-
/// before the gate existed, so this only ever narrows behaviour relative to that.
1312-
var isActiveForFolderPagination: Bool = true
1305+
/// Defaults to false. Only the visible folder tab (or standalone lists that opt in) may paginate;
1306+
/// adjacent preloaded tabs must stay false or background pagination corrupts their item state.
1307+
var isActiveForFolderPagination: Bool = false
13131308
public private(set) var chatListFilter: ChatListFilter? {
13141309
didSet {
13151310
self.chatListFilterValue.set(.single(self.chatListFilter))
@@ -2735,7 +2730,7 @@ public final class ChatListNode: ListViewImpl {
27352730
let presentationData = state.presentationData
27362731

27372732
return preparedChatListNodeViewTransition(from: previousView, to: processedView, reason: reason, previewing: previewing, disableAnimations: disableAnimations, scrollPosition: updatedScrollPosition, searchMode: searchMode, forceAllUpdated: forceAllUpdated)
2738-
|> map({ mappedChatListNodeViewListTransition(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: accountIsPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, presentationData: presentationData, transition: $0) })
2733+
|> map({ mappedChatListNodeViewListTransition(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: accountIsPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, presentationData: presentationData, transition: $0, locationGeneration: locationGeneration) })
27392734
|> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue)
27402735
}
27412736

@@ -3427,6 +3422,11 @@ public final class ChatListNode: ListViewImpl {
34273422
if let (transition, completion) = self.enqueuedTransition {
34283423
self.enqueuedTransition = nil
34293424

3425+
guard transition.locationGeneration == self.chatListLocationGeneration, self.isActiveForFolderPagination else {
3426+
completion()
3427+
return
3428+
}
3429+
34303430
let completion: (ListViewDisplayedItemRange) -> Void = { [weak self] visibleRange in
34313431
if let strongSelf = self {
34323432
strongSelf.chatListView = transition.chatListView
@@ -3825,6 +3825,13 @@ public final class ChatListNode: ListViewImpl {
38253825
}
38263826
}
38273827

3828+
func deactivateFolderPagination() {
3829+
self.isActiveForFolderPagination = false
3830+
if case .navigation = self.currentLocation {
3831+
self.setChatListLocation(.initial(count: 50, filter: self.chatListFilter))
3832+
}
3833+
}
3834+
38283835
func reconcileLocationOnTabActivation() {
38293836
self.isActiveForFolderPagination = true
38303837
if case .navigation = self.currentLocation {

‎submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, AS
172172
autoSetReady: false,
173173
isMainTab: nil
174174
)
175+
self.chatListNode.isActiveForFolderPagination = true
175176
self.chatListNode.synchronousDrawingWhenNotAnimated = true
176177

177178
super.init()

‎submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
247247
} else {
248248
self.mainContainerNode = nil
249249
self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: chatListMode, theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false, autoSetReady: true, isMainTab: false)
250+
self.chatListNode?.isActiveForFolderPagination = true
250251
if let multipleSelectionLimit = controller.multipleSelectionLimit {
251252
self.chatListNode?.selectionLimit = multipleSelectionLimit
252253
}

‎submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
189189

190190
placeholder = placeholderValue
191191
let chatListNode = ChatListNode(context: context, location: .chatList(groupId: .root), chatListFilter: chatListFilter, previewing: false, fillPreloadItems: false, mode: .peers(filter: [.excludeSecretChats], isSelecting: true, additionalCategories: additionalCategories?.categories ?? [], topPeers: [], chatListFilters: chatListFilters, displayAutoremoveTimeout: chatSelection.displayAutoremoveTimeout, displayPresence: chatSelection.displayPresence), isPeerEnabled: isPeerEnabled, theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false, autoSetReady: true, isMainTab: false)
192+
chatListNode.isActiveForFolderPagination = true
192193
chatListNode.passthroughPeerSelection = true
193194
chatListNode.disabledPeerSelected = { peer, _, reason in
194195
attemptDisabledItemSelection?(peer, reason)

0 commit comments

Comments
 (0)