diff --git a/Tusker/Screens/Notifications/NotificationsCollectionViewController.swift b/Tusker/Screens/Notifications/NotificationsCollectionViewController.swift index 7a5cd270..051eafb6 100644 --- a/Tusker/Screens/Notifications/NotificationsCollectionViewController.swift +++ b/Tusker/Screens/Notifications/NotificationsCollectionViewController.swift @@ -355,8 +355,24 @@ extension NotificationsCollectionViewController { } func handlePrependItems(_ timelineItems: [NotificationGroup]) async { + let topItem = dataSource.snapshot().itemIdentifiers(inSection: .notifications).first + // we always replace all, because new items are merged with existing ones await handleReplaceAllItems(timelineItems) + + // preserve the scroll position + // todo: this won't work for cmd+r when not at top + if let topID = topItem?.group?.notifications.first?.id { + // the exact item may have changed, due to merging + let newTopGroup = timelineItems.first { + $0.notifications.contains { + $0.id == topID + } + }! + if let newTopIndexPath = dataSource.indexPath(for: .group(newTopGroup)) { + collectionView.scrollToItem(at: newTopIndexPath, at: .top, animated: false) + } + } } func loadOlder() async throws -> [NotificationGroup] { diff --git a/Tusker/Screens/Utilities/TimelineLikeCollectionViewController.swift b/Tusker/Screens/Utilities/TimelineLikeCollectionViewController.swift index 36f1536e..f7754539 100644 --- a/Tusker/Screens/Utilities/TimelineLikeCollectionViewController.swift +++ b/Tusker/Screens/Utilities/TimelineLikeCollectionViewController.swift @@ -153,6 +153,7 @@ extension TimelineLikeCollectionViewController { } await apply(snapshot, animatingDifferences: false) + // todo: this won't work for cmd+r when not at top if let first, let indexPath = dataSource.indexPath(for: first) { // TODO: i can't tell if this actually works or not