diff options
Diffstat (limited to 'app/src/main/java/org/terst')
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/ui/ArticleListScreen.kt | 26 |
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, |
