diff options
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui')
3 files changed, 41 insertions, 32 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt b/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt index 38b0f32..1e8814c 100644 --- a/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt @@ -54,30 +54,42 @@ fun AddFeedScreen(onDone: () -> Unit) { if (uri == null) return@rememberLauncherForActivityResult isBusy = true scope.launch { - val imported = withContext(Dispatchers.IO) { - context.contentResolver.openInputStream(uri)?.use { stream -> Opml.parse(stream) } ?: emptyList() - } - // A feed cross-listed under multiple OPML category folders (common when - // exported from a reader with folders/labels) appears once per folder here -- - // dedupe by feed URL before reporting a count, or "108 feeds" would be reported - // as some larger number even though upsert already collapses them to one row each. - val uniqueFeeds = imported.map { it.copy(xmlUrl = preferHttps(it.xmlUrl)) }.distinctBy { it.xmlUrl } - for (feed in uniqueFeeds) { - // upsert() REPLACEs the whole row -- for a feed that's already subscribed, - // building a bare FeedEntity here would silently wipe readerModeDisabled - // (and etag/lastModified/lastError) back to defaults on every re-import. - // Preserve the existing row's other fields; only title/siteUrl come from OPML. - val existing = db.feedDao().getById(feed.xmlUrl) - val entity = existing?.copy( - title = feed.title, - siteUrl = feed.htmlUrl ?: existing.siteUrl, - folder = feed.folder ?: existing.folder - ) ?: FeedEntity(id = feed.xmlUrl, title = feed.title, siteUrl = feed.htmlUrl, folder = feed.folder) - db.feedDao().upsert(entity) + try { + val imported = withContext(Dispatchers.IO) { + context.contentResolver.openInputStream(uri)?.use { stream -> Opml.parse(stream) } ?: emptyList() + } + // A feed cross-listed under multiple OPML category folders (common when + // exported from a reader with folders/labels) appears once per folder here -- + // dedupe by feed URL before reporting a count, or "108 feeds" would be reported + // as some larger number even though upsert already collapses them to one row each. + val uniqueFeeds = imported.map { it.copy(xmlUrl = preferHttps(it.xmlUrl)) }.distinctBy { it.xmlUrl } + withContext(Dispatchers.IO) { + // Loaded once, not one getById() per feed: upsert() REPLACEs the whole + // row, and for a feed that's already subscribed, building a bare + // FeedEntity here would silently wipe readerModeDisabled (and + // etag/lastModified/lastError) back to defaults on every re-import -- + // preserve the existing row's other fields, only title/siteUrl come + // from OPML. + val existingById = db.feedDao().getAll().associateBy { it.id } + val entities = uniqueFeeds.map { feed -> + val existing = existingById[feed.xmlUrl] + existing?.copy( + title = feed.title, + siteUrl = feed.htmlUrl ?: existing.siteUrl, + folder = feed.folder ?: existing.folder + ) ?: FeedEntity(id = feed.xmlUrl, title = feed.title, siteUrl = feed.htmlUrl, folder = feed.folder) + } + db.feedDao().upsertAll(entities) + } + FeedFetchWorker.fetchNow(context) + status = "Imported ${uniqueFeeds.size} feed(s)" + } catch (e: Exception) { + // A non-XML or corrupt/truncated file throws from Opml.parse -- without this, + // that crashed the whole app instead of just failing this one import. + status = "Couldn't import: ${e.message}" + } finally { + isBusy = false } - FeedFetchWorker.fetchNow(context) - status = "Imported ${uniqueFeeds.size} feed(s)" - isBusy = false } } 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 90bfd7e..3f9a135 100644 --- a/app/src/main/java/org/terst/feedreader/ui/FeedListScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/FeedListScreen.kt @@ -48,6 +48,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import org.terst.feedreader.data.AppDatabase import org.terst.feedreader.data.FeedEntity +import org.terst.feedreader.data.setReaderModeEnabled import org.terst.feedreader.work.FeedFetchWorker @OptIn(ExperimentalMaterial3Api::class) @@ -170,11 +171,8 @@ fun FeedListScreen( onToggleReaderMode = { 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" - ) + val message = db.feedDao().setReaderModeEnabled(feed, newEnabled) + snackbarHostState.showSnackbar(message) } } ) 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 b586fbc..ae63258 100644 --- a/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt +++ b/app/src/main/java/org/terst/feedreader/ui/ReaderScreen.kt @@ -62,6 +62,7 @@ import org.terst.feedreader.data.ArticleEntity import org.terst.feedreader.data.FeedEntity import org.terst.feedreader.data.ensureStandaloneArticle import org.terst.feedreader.data.saveArticleToStash +import org.terst.feedreader.data.setReaderModeEnabled import org.terst.feedreader.readability.ReaderExtractor import java.net.URLEncoder @@ -229,8 +230,7 @@ private fun ReaderContent( val newEnabled = !f.readerModeEnabled feed = f.copy(readerModeEnabled = newEnabled) scope.launch { - db.feedDao().update(f.copy(readerModeEnabled = newEnabled)) - val feedLabel = f.title.ifBlank { "this feed" } + val message = db.feedDao().setReaderModeEnabled(f, newEnabled) if (newEnabled) { if (readerContent == null) { isExtracting = true @@ -243,13 +243,12 @@ private fun ReaderContent( } } isReaderMode = true - snackbarHostState.showSnackbar("Reader mode enabled for $feedLabel") } else { isReaderMode = false readerContent = null db.articleDao().clearExtractedContent(articleId) - snackbarHostState.showSnackbar("Reader mode disabled for $feedLabel") } + snackbarHostState.showSnackbar(message) } } |
