diff options
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt')
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt | 116 |
1 files changed, 73 insertions, 43 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt index a46d1aa..6e1f939 100644 --- a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt @@ -123,11 +123,22 @@ fun ReaderScreen( val currentPage = pagerState.currentPage val hasPrevious = currentPage > 0 val hasNext = currentPage < safeIds.size - 1 + // Marking read happens HERE (moving to another article), not on open -- backing out + // to the list without paging past an article leaves it unread. Only Next/Previous + // count as "moved away from it"; exiting via the back arrow deliberately doesn't. fun goToPreviousArticle() { - scope.launch { pagerState.animateScrollToPage((pagerState.currentPage - 1).coerceAtLeast(0)) } + val leavingId = safeIds[pagerState.currentPage] + scope.launch { + db.articleDao().setRead(leavingId, true) + pagerState.animateScrollToPage((pagerState.currentPage - 1).coerceAtLeast(0)) + } } fun goToNextArticle() { - scope.launch { pagerState.animateScrollToPage((pagerState.currentPage + 1).coerceAtMost(safeIds.size - 1)) } + val leavingId = safeIds[pagerState.currentPage] + scope.launch { + db.articleDao().setRead(leavingId, true) + pagerState.animateScrollToPage((pagerState.currentPage + 1).coerceAtMost(safeIds.size - 1)) + } } HorizontalPager(state = pagerState, userScrollEnabled = false, modifier = Modifier.fillMaxSize()) { page -> @@ -175,7 +186,8 @@ private fun ReaderContent( LaunchedEffect(articleId) { val loaded = db.articleDao().getById(articleId) ?: return@LaunchedEffect article = loaded - db.articleDao().setRead(articleId, true) + // Deliberately NOT marking read here -- see ReaderScreen.goToNextArticle/ + // goToPreviousArticle, which is where "read" actually gets set now. val loadedFeed = loaded.feedId?.let { db.feedDao().getById(it) } feed = loadedFeed @@ -274,16 +286,10 @@ private fun ReaderContent( ) } } - IconButton(onClick = { - val a = currentArticle ?: return@IconButton - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(a.link)) - context.startActivity(browserIntent) - }) { - Icon(Icons.Default.OpenInBrowser, contentDescription = "Open in browser") - } // Purely local: just flips isSaved (shows up in the Saved list). Does NOT - // touch Stash -- that's a separate, explicit action below, since archiving - // to Stash is a heavier/networked action the user should trigger on purpose. + // touch Stash -- that's a separate, explicit action in the overflow menu, + // since archiving to Stash is a heavier/networked action the user should + // trigger on purpose. IconButton(onClick = { val a = currentArticle ?: return@IconButton val newSaved = !a.isSaved @@ -295,41 +301,65 @@ private fun ReaderContent( contentDescription = "Save locally" ) } - IconButton( - enabled = !isSaving, - onClick = { - val a = currentArticle ?: return@IconButton - isSaving = true - scope.launch { - val settings = Settings(context) - val client = StashClient(Net.client, settings.stashBaseUrlNow(), settings.stashApiKeyNow()) - when (val result = client.saveArticle(a.link, a.title)) { - is StashSaveResult.Saved -> { - db.articleDao().setStashArtifactId(a.id, result.artifact.id) - article = a.copy(stashArtifactId = result.artifact.id) - snackbarHostState.showSnackbar("Saved to Stash") - } - is StashSaveResult.AlreadySaved -> { - db.articleDao().setStashArtifactId(a.id, result.existingId) - article = a.copy(stashArtifactId = result.existingId) - snackbarHostState.showSnackbar("Already in Stash") - } - is StashSaveResult.Failed -> - snackbarHostState.showSnackbar("Save failed: ${result.message}") - } - isSaving = false - } - } - ) { - Icon( - if (currentArticle?.stashArtifactId != null) Icons.Default.CloudDone else Icons.Default.CloudUpload, - contentDescription = "Save to Stash" - ) - } IconButton(onClick = { moreMenuExpanded = true }) { Icon(Icons.Default.MoreVert, contentDescription = "More") } DropdownMenu(expanded = moreMenuExpanded, onDismissRequest = { moreMenuExpanded = false }) { + DropdownMenuItem( + leadingIcon = { Icon(Icons.Default.OpenInBrowser, contentDescription = null) }, + text = { Text("Open original") }, + onClick = { + moreMenuExpanded = false + val a = currentArticle ?: return@DropdownMenuItem + context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(a.link))) + } + ) + DropdownMenuItem( + enabled = !isSaving, + leadingIcon = { + Icon( + if (currentArticle?.stashArtifactId != null) Icons.Default.CloudDone else Icons.Default.CloudUpload, + contentDescription = null + ) + }, + text = { Text(if (currentArticle?.stashArtifactId != null) "Saved to Stash" else "Save to Stash") }, + onClick = { + moreMenuExpanded = false + val a = currentArticle ?: return@DropdownMenuItem + isSaving = true + scope.launch { + val settings = Settings(context) + val client = StashClient(Net.client, settings.stashBaseUrlNow(), settings.stashApiKeyNow()) + when (val result = client.saveArticle(a.link, a.title)) { + is StashSaveResult.Saved -> { + db.articleDao().setStashArtifactId(a.id, result.artifact.id) + article = a.copy(stashArtifactId = result.artifact.id) + snackbarHostState.showSnackbar("Saved to Stash") + } + is StashSaveResult.AlreadySaved -> { + db.articleDao().setStashArtifactId(a.id, result.existingId) + article = a.copy(stashArtifactId = result.existingId) + snackbarHostState.showSnackbar("Already in Stash") + } + is StashSaveResult.Failed -> + snackbarHostState.showSnackbar("Save failed: ${result.message}") + } + isSaving = false + } + } + ) + DropdownMenuItem( + text = { Text("Mark unread") }, + onClick = { + moreMenuExpanded = false + val a = currentArticle ?: return@DropdownMenuItem + article = a.copy(isRead = false) + scope.launch { + db.articleDao().setRead(a.id, false) + snackbarHostState.showSnackbar("Marked unread") + } + } + ) // A real (JS-on) browser, not the extraction pipeline -- search results // and login pages need actual interaction, not a Readability pass. DropdownMenuItem( |
