summaryrefslogtreecommitdiff
path: root/app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-08-22 01:01:05 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-08-22 01:01:05 +0000
commita3296f5c14a5fb95feaa3751044a1995935e6f3e (patch)
tree6e1c1399c1a04ef4c7f24b573e48fd7de4f6d547 /app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt
parentfd49bacfb92ef8d29cbad56419acd016ff40f357 (diff)
Feed writes: bulk OPML upsert, targeted fetch-result update, shared toggle
- AddFeedScreen: OPML import no longer crashes the app on a non-XML/corrupt file (try/catch around the whole import). Bulk upsertAll() instead of a per-feed getById()+upsert() loop -- 2N sequential DB round-trips for N feeds otherwise, real cost at 100+ feed subscription lists. - FeedDao.updateFetchResult only writes the columns a fetch actually produces, instead of a full-row @Update built from a feed snapshot read before a (possibly slow) network round-trip -- the old path could silently revert a readerModeEnabled/folder edit made via FeedListScreen while that feed's fetch was still in flight. - setReaderModeEnabled(feed, enabled) persists the toggle and returns the exact confirmation text, shared by FeedListScreen's per-row toggle and ReaderScreen's toggleFeedReaderMode -- previously two independently-typed copies of the same snackbar message.
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt')
-rw-r--r--app/src/main/java/org/terst/feedreader/ui/AddFeedScreen.kt58
1 files changed, 35 insertions, 23 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
}
}