From 5c7f9d0d306b66740642cf4111f2a9ee26897ac5 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sat, 22 Aug 2026 01:02:31 +0000 Subject: Fix reader rotation bug: replace-nav instead of a swipe-queue snapshot The reader used to snapshot the article-id order of its list ONCE at entry and page a HorizontalPager across that fixed queue. Two real bugs traced back to one root cause: the snapshot lived in plain remember state that did NOT survive an orientation change, while the pager's own page index did -- rotating mid-session left the index pointing into a freshly re-fetched (and by then shorter, since paging past an article marks it read and drops it from the Unread query) list, landing on a different article than the one actually being read. Back was confusing for the same reason: it always returned to wherever the ORIGINAL initialArticleId pointed, not wherever the pager had since scrolled to. Fixed by not having a separate queue/index at all: each Next/Previous now replaces this screen's own back-stack entry with a fresh navigation to the neighboring article's id (MainActivity wires onNavigateToArticle with the same popUpTo-replace pattern LinkReaderScreen already uses for its own link chain). "Which article is showing" is now the nav route argument itself -- Android's own Saved State mechanism, not custom state that can drift out of sync with it -- and the back stack is always exactly [list, one reader entry] no matter how many articles were paged through. --- .../main/java/org/terst/feedreader/ui/MainActivity.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'app/src/main/java/org/terst/feedreader/ui/MainActivity.kt') diff --git a/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt b/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt index f7ca043..9add10a 100644 --- a/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt +++ b/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt @@ -89,13 +89,23 @@ fun FeedReaderApp() { val feedId = decodeNavArg(backStackEntry.arguments?.getString("feedId") ?: UNREAD_FEED_ID) val articleId = decodeNavArg(backStackEntry.arguments?.getString("articleId") ?: "") // feedId here is the list the article was opened from (Unread/Saved/a specific - // feed) -- ReaderScreen uses it to build the swipe queue of sibling articles. + // feed) -- ReaderScreen uses it to look up the current article's neighbors. ReaderScreen( feedId = feedId, - initialArticleId = articleId, + articleId = articleId, onBack = { navController.popBackStack() }, onOpenLink = { url -> navController.navigate("linkReader/${encodeNavArg(url)}") }, - onOpenBrowser = { url -> navController.navigate("browser/${encodeNavArg(url)}") } + onOpenBrowser = { url -> navController.navigate("browser/${encodeNavArg(url)}") }, + // Replaces this screen's own back-stack entry rather than stacking a new + // one per article (same popUpTo-replace pattern linkReader's own link-chain + // navigation already uses below) -- so the stack is always exactly + // [list, this one reader entry], and back always returns to the list in a + // single press no matter how many articles were paged through via Next/Previous. + onNavigateToArticle = { nextArticleId -> + navController.navigate("reader/${encodeNavArg(feedId)}/${encodeNavArg(nextArticleId)}") { + popUpTo("reader/{feedId}/{articleId}") { inclusive = true } + } + } ) } composable( -- cgit v1.2.3