summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/src/main/java/org/terst/feedreader/ui/ArticleListScreen.kt26
1 files changed, 25 insertions, 1 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/ArticleListScreen.kt b/app/src/main/java/org/terst/feedreader/ui/ArticleListScreen.kt
index 7a99050..d87620d 100644
--- a/app/src/main/java/org/terst/feedreader/ui/ArticleListScreen.kt
+++ b/app/src/main/java/org/terst/feedreader/ui/ArticleListScreen.kt
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
@@ -98,6 +99,29 @@ fun ArticleListScreen(
}
}.collectAsState(initial = emptyList())
+ // Hoisted (not LazyColumn's own default state) so the effect below can read/adjust it.
+ val listState = rememberLazyListState()
+ var previousArticleIds by remember(feedId) { mutableStateOf<List<String>>(emptyList()) }
+
+ // A background fetch (the 5am alarm, or the explicit refresh button) can insert new
+ // articles above whatever's currently on screen. LazyColumn doesn't move
+ // firstVisibleItemIndex just because the backing list changed -- that index is only
+ // ever moved by an actual scroll or an explicit scrollToItem -- so after an insert, the
+ // SAME index now points at a DIFFERENT (newer) article, and the visible content jumps
+ // out from under you instead of staying where you were reading. Re-anchor to the same
+ // article, by id, at whatever index it now sits at, preserving the on-screen offset too
+ // so this doesn't even show as a snap -- just an uninterrupted read.
+ LaunchedEffect(visibleArticles) {
+ val anchorId = previousArticleIds.getOrNull(listState.firstVisibleItemIndex)
+ if (anchorId != null) {
+ val newIndex = visibleArticles.indexOfFirst { it.first.id == anchorId }
+ if (newIndex >= 0 && newIndex != listState.firstVisibleItemIndex) {
+ listState.scrollToItem(newIndex, listState.firstVisibleItemScrollOffset)
+ }
+ }
+ previousArticleIds = visibleArticles.map { it.first.id }
+ }
+
// Unread/Older/Saved mix articles from every feed, so each row names its source; a
// single feed's own list already says so in the top bar, so rows there don't repeat it.
val showFeedName = feedId == UNREAD_FEED_ID || feedId == OLDER_FEED_ID || feedId == SAVED_FEED_ID
@@ -243,7 +267,7 @@ fun ArticleListScreen(
},
snackbarHost = { SnackbarHost(snackbarHostState) }
) { padding ->
- LazyColumn(modifier = Modifier.fillMaxSize().padding(padding)) {
+ LazyColumn(modifier = Modifier.fillMaxSize().padding(padding), state = listState) {
items(visibleArticles, key = { it.first.id }) { (article, result) ->
ArticleRow(
article = article,