diff options
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui')
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/ui/FeedListScreen.kt | 23 | ||||
| -rw-r--r-- | app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt | 87 |
2 files changed, 64 insertions, 46 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/FeedListScreen.kt b/app/src/main/java/org/terst/feedreader/ui/FeedListScreen.kt index f8ea504..47f51fc 100644 --- a/app/src/main/java/org/terst/feedreader/ui/FeedListScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/FeedListScreen.kt @@ -27,6 +27,8 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar @@ -73,6 +75,7 @@ fun FeedListScreen( val olderUnreadCount by remember { db.articleDao().observeOlderUnreadCount(cutoff) }.collectAsState(initial = 0) var menuExpanded by remember { mutableStateOf(false) } var pendingDelete by remember { mutableStateOf<FeedEntity?>(null) } + val snackbarHostState = remember { SnackbarHostState() } pendingDelete?.let { feed -> AlertDialog( @@ -121,7 +124,8 @@ fun FeedListScreen( FloatingActionButton(onClick = onAddFeed) { Icon(Icons.Default.Add, contentDescription = "Add feed") } - } + }, + snackbarHost = { SnackbarHost(snackbarHostState) } ) { padding -> LazyColumn(modifier = Modifier.fillMaxSize().padding(padding)) { item { @@ -152,7 +156,14 @@ fun FeedListScreen( onClick = { onOpenFeed(feed.id) }, onDelete = { pendingDelete = feed }, onToggleReaderMode = { - scope.launch { db.feedDao().update(feed.copy(readerModeDisabled = !feed.readerModeDisabled)) } + val newEnabled = !feed.readerModeEnabled + scope.launch { + db.feedDao().update(feed.copy(readerModeEnabled = newEnabled)) + val label = feed.title.ifBlank { "this feed" } + snackbarHostState.showSnackbar( + if (newEnabled) "Reader mode enabled for $label" else "Reader mode disabled for $label" + ) + } } ) HorizontalDivider() @@ -184,11 +195,11 @@ private fun FeedRow( } IconButton(onClick = onToggleReaderMode) { Icon( - if (feed.readerModeDisabled) Icons.Outlined.Article else Icons.Filled.Article, - contentDescription = if (feed.readerModeDisabled) - "Reader mode disabled for this feed -- tap to re-enable" - else + if (feed.readerModeEnabled) Icons.Filled.Article else Icons.Outlined.Article, + contentDescription = if (feed.readerModeEnabled) "Reader mode enabled for this feed -- tap to disable" + else + "Reader mode disabled for this feed -- tap to enable" ) } IconButton(onClick = onDelete) { 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 4becea6..806926c 100644 --- a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt @@ -59,10 +59,6 @@ import org.terst.feedreader.stash.StashClient import org.terst.feedreader.stash.StashSaveResult import java.net.URLEncoder -// Below this length (of the plain feed-provided HTML), assume it's a teaser/summary, -// not the real article body, and auto-fall-back to on-device Readability extraction. -private const val THIN_CONTENT_THRESHOLD = 500 - /** * Swipeable wrapper: snapshots the article-id order of the list this reader * was opened from (Unread/Saved/a specific feed) ONCE at entry, then pages @@ -191,44 +187,53 @@ private fun ReaderContent( feedContent = loaded.feedContentHtml readerContent = loaded.extractedContent - val plainLength = feedContent?.replace(Regex("<[^>]*>"), "")?.trim()?.length ?: 0 - if (loadedFeed?.readerModeDisabled != true && readerContent == null && plainLength < THIN_CONTENT_THRESHOLD) { - // Feed content is too thin to be the real article -- auto-extract, since in this - // case reader mode isn't really an alternative view, it's the only real content. - isExtracting = true - val extracted = ReaderExtractor.extract(context, loaded.link) - isExtracting = false - extracted?.content?.let { - readerContent = it - db.articleDao().setExtractedContent(articleId, it) - } + // Reader mode is NEVER heuristic/automatic (no more "content looks thin, switch + // for you") -- it strictly follows the feed's persisted, explicitly-set opt-in. + // Once a feed has it on, articles from it default to the extracted view and + // auto-extract on open (that's the point of persisting it per-feed: no re-tapping + // per article), but nothing here ever turns it on by itself. + if (loadedFeed?.readerModeEnabled == true) { isReaderMode = true + if (readerContent == null) { + isExtracting = true + val extracted = ReaderExtractor.extract(context, loaded.link) + isExtracting = false + extracted?.content?.let { + readerContent = it + db.articleDao().setExtractedContent(articleId, it) + } + } } } - fun toggleReaderMode() { + // The toggle now sets reader mode for the ARTICLE'S FEED, persisted immediately, with + // a confirmation snackbar -- not a per-article, this-session-only view flip. Tapping it + // is the one and only way reader mode ever turns on. + fun toggleFeedReaderMode() { + val f = feed ?: return val a = article ?: return - if (feed?.readerModeDisabled == true) return - if (isReaderMode) { - isReaderMode = false - return - } - if (readerContent != null) { - isReaderMode = true - return - } + val newEnabled = !f.readerModeEnabled + feed = f.copy(readerModeEnabled = newEnabled) scope.launch { - isExtracting = true - val extracted = ReaderExtractor.extract(context, a.link) - isExtracting = false - val content = extracted?.content - if (content != null) { - readerContent = content - db.articleDao().setExtractedContent(articleId, content) + db.feedDao().update(f.copy(readerModeEnabled = newEnabled)) + val feedLabel = f.title.ifBlank { "this feed" } + if (newEnabled) { + if (readerContent == null) { + isExtracting = true + val extracted = ReaderExtractor.extract(context, a.link) + isExtracting = false + val content = extracted?.content + if (content != null) { + readerContent = content + db.articleDao().setExtractedContent(articleId, content) + } + } isReaderMode = true + snackbarHostState.showSnackbar("Reader mode enabled for $feedLabel") } else { - snackbarHostState.showSnackbar("Couldn't extract a reader view for this article") + isReaderMode = false + snackbarHostState.showSnackbar("Reader mode disabled for $feedLabel") } } } @@ -257,14 +262,16 @@ private fun ReaderContent( }, actions = { val currentArticle = article - // Only offer the toggle when there's an actual feed-provided alternative to - // switch back to (if we already had to auto-extract for thin feed content, - // reader mode IS the only content) and the feed hasn't opted out of it. - if (feedContent != null && feed?.readerModeDisabled != true) { - IconButton(enabled = !isExtracting, onClick = { toggleReaderMode() }) { + // Sets reader mode for the whole feed (persisted), not just this article -- + // shown whenever the article belongs to an actual subscribed feed. + if (feed != null) { + IconButton(enabled = !isExtracting, onClick = { toggleFeedReaderMode() }) { Icon( - if (isReaderMode) Icons.Filled.Article else Icons.Outlined.Article, - contentDescription = "Toggle reader view" + if (feed?.readerModeEnabled == true) Icons.Filled.Article else Icons.Outlined.Article, + contentDescription = if (feed?.readerModeEnabled == true) + "Reader mode enabled for this feed -- tap to disable" + else + "Reader mode disabled for this feed -- tap to enable" ) } } |
