From 02fd0780d5653f892ac6db7625783311ff52223f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 28 May 2026 23:01:49 +0000 Subject: [PATCH] fix(feeds): don't snap to top when returning from a post screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit StickToTopOnPrepend hardcoded `wasAtTop = true` on remember, so when the user scrolled down, navigated to a post and came back, rememberForeverLazyListState restored a non-zero offset but the helper still believed the user was at the top. As soon as the head-of-feed key emitted (first observation after recomposition), the LaunchedEffect scrolled them back to 0. Seed wasAtTop from the actual restored scroll position via an initialAtTop lambda. The sampler's gesture-guarded false→true protection against keyed-item shifts is unchanged. Regression from #3088. --- .../amethyst/ui/feeds/WatchScrollToTop.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/WatchScrollToTop.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/WatchScrollToTop.kt index 8f27178ddf..ddccd7e96d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/WatchScrollToTop.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/WatchScrollToTop.kt @@ -137,6 +137,9 @@ fun StickToTopOnPrepend( stickToTopOnPrepend( stateKey = listState, firstItemKey = firstItemKey, + initialAtTop = { + listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset == 0 + }, sampler = { snapshotFlow { listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset == 0 @@ -156,6 +159,9 @@ fun StickToTopOnPrepend( stickToTopOnPrepend( stateKey = gridState, firstItemKey = firstItemKey, + initialAtTop = { + gridState.firstVisibleItemIndex == 0 && gridState.firstVisibleItemScrollOffset == 0 + }, sampler = { snapshotFlow { gridState.firstVisibleItemIndex == 0 && gridState.firstVisibleItemScrollOffset == 0 @@ -209,6 +215,7 @@ private fun rememberFirstItemIdHex(feedContentState: FeedContentState): String? private fun stickToTopOnPrepend( stateKey: Any, firstItemKey: Any?, + initialAtTop: () -> Boolean, sampler: () -> Flow, isScrollInProgress: () -> Boolean, firstVisibleItemIndex: () -> Int, @@ -216,7 +223,10 @@ private fun stickToTopOnPrepend( ) { // Plain holder instead of mutableStateOf — we only read this inside // effects, never in composition, so we don't need snapshot tracking. - val wasAtTop = remember { booleanArrayOf(true) } + // Seed from the actual restored scroll position: when the user returns + // to a feed via rememberForeverLazyListState, the saved offset is + // already in place, and a hardcoded `true` would mis-snap them to 0. + val wasAtTop = remember(stateKey) { booleanArrayOf(initialAtTop()) } LaunchedEffect(stateKey) { sampler().collect { atTop ->