Skip to content

Commit

Permalink
Fix article location after app is closed (#841)
Browse files Browse the repository at this point in the history
* Try index lookup

- Fix ArticleHandler. This was resetting articles too much on launch
- Fix pagerState. This needs to wait for async initialization for when the
  findIndex is called on the current article
- Remove extra IndexedArticle handling. This caused articles to be
  out-of-date

* Fix offset bug when page refreshes

Use a coroutine job to cancel and restart finding the
current index when the article count changes.
  • Loading branch information
jocmp authored Feb 9, 2025
1 parent 9edeaf2 commit 6f90eb6
Show file tree
Hide file tree
Showing 26 changed files with 1,063 additions and 514 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ package com.capyreader.app.ui.articles
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import com.capyreader.app.common.AppPreferences
import com.jocmp.capy.Article
import org.koin.compose.koinInject

@Composable
fun ArticleHandler(
article: Article?,
appPreferences: AppPreferences = koinInject(),
onRequestArticle: (articleID: String) -> Unit,
) {
LaunchedEffect(Unit) {
LaunchedEffect(article?.id) {
if (article != null) {
return@LaunchedEffect
}

val articleID = appPreferences.articleID.get()

if (articleID.isNotBlank()) {
Expand Down
55 changes: 28 additions & 27 deletions app/src/main/java/com/capyreader/app/ui/articles/ArticleLayout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ fun ArticleLayout(
) {
val skipInitialRefresh = refreshInterval == RefreshInterval.MANUALLY_ONLY

val (isInitialized, setInitialized) = rememberSaveable {
val (isRefreshInitialized, setRefreshInitialized) = rememberSaveable {
mutableStateOf(skipInitialRefresh)
}
val (isUpdatePasswordDialogOpen, setUpdatePasswordDialogOpen) = rememberSaveable {
Expand All @@ -116,6 +116,7 @@ fun ArticleLayout(
val hasMultipleColumns = scaffoldNavigator.scaffoldDirective.maxHorizontalPartitions > 1
var isRefreshing by remember { mutableStateOf(false) }
val listState = rememberLazyListState()

val snackbarHost = remember { SnackbarHostState() }
val addFeedSuccessMessage = stringResource(R.string.add_feed_success)
val currentFeed = findCurrentFeed(filter, allFeeds)
Expand All @@ -132,6 +133,7 @@ fun ArticleLayout(
scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
}


fun scrollToArticle(index: Int) {
coroutineScope.launch {
if (index > -1) {
Expand Down Expand Up @@ -197,8 +199,8 @@ fun ArticleLayout(
onInitialized {
isRefreshing = false
refreshPagination()
if (!isInitialized) {
setInitialized(true)
if (!isRefreshInitialized) {
setRefreshInitialized(true)
}
}
}
Expand All @@ -218,6 +220,13 @@ fun ArticleLayout(
}
}

fun clearArticle() {
coroutineScope.launchUI {
scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.List)
}
onRequestClearArticle()
}

val toggleDrawer = {
coroutineScope.launch {
if (drawerState.isOpen) {
Expand Down Expand Up @@ -308,8 +317,8 @@ fun ArticleLayout(
}
}

ArticleHandler {
selectArticle(it)
ArticleHandler(article) { articleID ->
selectArticle(articleID)
}

ArticleScaffold(
Expand Down Expand Up @@ -378,7 +387,7 @@ fun ArticleLayout(
) { innerPadding ->
ArticleListScaffold(
padding = innerPadding,
showOnboarding = isInitialized && !isRefreshing && allFeeds.isEmpty(),
showOnboarding = isRefreshInitialized && allFeeds.isEmpty(),
onboarding = {
EmptyOnboardingView {
AddFeedButton(
Expand Down Expand Up @@ -417,7 +426,9 @@ fun ArticleLayout(
onMarkAllRead = { range ->
onMarkAllRead(range)
},
onSelect = ::selectArticle,
onSelect = { articleID ->
selectArticle(articleID)
},
)
}
}
Expand All @@ -434,35 +445,25 @@ fun ArticleLayout(
) {
CapyPlaceholder()
}
} else if (article != null) {
val indexedArticles =
rememberIndexedArticles(article = article, articles = articles)

} else if (article != null && articles.itemCount > 0) {
ArticleView(
article = article,
onNavigateToMedia = { media = it },
articles = indexedArticles,
articles = articles,
onBackPressed = {
coroutineScope.launchUI {
scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.List)
}
onRequestClearArticle()
clearArticle()
},
onToggleRead = onToggleArticleRead,
onToggleStar = onToggleArticleStar,
enableBackHandler = media == null,
onRequestArticle = { id ->
coroutineScope.launchUI {
onSelectArticle(id)
}
onRequestArticle = { index, articleID ->
selectArticle(articleID)
scrollToArticle(index)
},
)

LaunchedEffect(article.id, indexedArticles.index) {
if (hasMultipleColumns) {
scrollToArticle(indexedArticles.index)
onScrollToArticle = { index ->
scrollToArticle(index)
}
}
)
}
}
)
Expand Down Expand Up @@ -500,7 +501,7 @@ fun ArticleLayout(
}

LaunchedEffect(Unit) {
if (!isInitialized) {
if (!isRefreshInitialized) {
initialize()
}
}
Expand Down
11 changes: 3 additions & 8 deletions app/src/main/java/com/capyreader/app/ui/articles/ArticleList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import com.jocmp.capy.MarkRead
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import org.koin.compose.koinInject
import java.time.LocalDateTime

Expand All @@ -52,12 +51,6 @@ fun ArticleList(
val localDensity = LocalDensity.current
var listHeight by remember { mutableStateOf(0.dp) }

val selectArticle = { articleID: String ->
composableScope.launch {
onSelect(articleID)
}
}

LazyScrollbar(state = listState) {
LazyColumn(
state = listState,
Expand All @@ -77,7 +70,9 @@ fun ArticleList(
ArticleRow(
article = item,
selected = selectedArticleKey == item.id,
onSelect = { selectArticle(it) },
onSelect = {
onSelect(it)
},
onMarkAllRead = onMarkAllRead,
currentTime = currentTime,
options = articleOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ fun ArticleScreen(
CompositionLocalProvider(
LocalFullContent provides fullContent,
LocalArticleActions provides articleActions,
LocalConnectivity provides connectivity
LocalConnectivity provides connectivity,
LocalArticleLookup provides ArticleLookup(
findIndex = {
viewModel.findArticleIndex(it)
},
),
) {
ArticleLayout(
filter = filter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.jocmp.capy.common.UnauthorizedError
import com.jocmp.capy.common.launchIO
import com.jocmp.capy.common.launchUI
import com.jocmp.capy.countAll
import com.jocmp.capy.findArticleIndex
import com.jocmp.capy.logging.CapyLog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand Down Expand Up @@ -572,6 +573,16 @@ class ArticleScreenViewModel(
}
}

fun findArticleIndex(articleID: String): Int {
return account.findArticleIndex(
articleID = articleID,
filter = latestFilter,
query = _searchQuery.value,
unreadSort = unreadSort.value,
since = articlesSince.value
).toInt()
}

private val latestFilter: ArticleFilter
get() = filter.value

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.capyreader.app.ui.articles

import androidx.compose.runtime.compositionLocalOf

val LocalArticleLookup = compositionLocalOf { ArticleLookup() }

data class ArticleLookup(
val findIndex: (articleID: String) -> Int = { -1 }
)
Loading

0 comments on commit 6f90eb6

Please sign in to comment.