summaryrefslogtreecommitdiff
path: root/app/src/main/java/org/terst/feedreader/data
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/data')
-rw-r--r--app/src/main/java/org/terst/feedreader/data/AppDatabase.kt17
-rw-r--r--app/src/main/java/org/terst/feedreader/data/FeedParser.kt10
2 files changed, 23 insertions, 4 deletions
diff --git a/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt b/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt
index 441556b..66722fd 100644
--- a/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt
+++ b/app/src/main/java/org/terst/feedreader/data/AppDatabase.kt
@@ -9,7 +9,7 @@ import androidx.room.migration.Migration
@Database(
entities = [FeedEntity::class, ArticleEntity::class, FilterRuleEntity::class],
- version = 4,
+ version = 5,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
@@ -77,12 +77,25 @@ abstract class AppDatabase : RoomDatabase() {
}
}
+ // v5: a feed with a literal empty <guid>/<id> element (confirmed live: a Mother
+ // Jones entry) produced an ArticleEntity with id = "" -- FeedParser fell back to
+ // itemLink only when the guid was null, not when it was blank. An empty-string
+ // primary key broke Navigation Compose's route matching and crashed on tap. The
+ // parser is fixed for future fetches; this cleans out any row already on-device
+ // with the bad id so it doesn't keep crashing. It'll come back with a real,
+ // hash-based id on the next fetch of that feed.
+ private val MIGRATION_4_5 = object : Migration(4, 5) {
+ override fun migrate(db: SupportSQLiteDatabase) {
+ db.execSQL("DELETE FROM articles WHERE trim(id) = ''")
+ }
+ }
+
fun get(context: Context): AppDatabase = instance ?: synchronized(this) {
instance ?: Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"feedreader.db"
- ).addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4).build().also { instance = it }
+ ).addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5).build().also { instance = it }
}
}
}
diff --git a/app/src/main/java/org/terst/feedreader/data/FeedParser.kt b/app/src/main/java/org/terst/feedreader/data/FeedParser.kt
index 629afe6..8b37e55 100644
--- a/app/src/main/java/org/terst/feedreader/data/FeedParser.kt
+++ b/app/src/main/java/org/terst/feedreader/data/FeedParser.kt
@@ -120,7 +120,12 @@ object FeedParser {
inItem = false
val content = itemContent ?: itemDescription
items += ParsedItem(
- guid = itemGuid ?: itemLink.ifBlank { null },
+ // .ifBlank, not just ?: -- a feed with a literal empty <guid></guid>
+ // (confirmed live: a Mother Jones entry) gives itemGuid = "" here,
+ // which is non-null and so passed straight through a plain `?:`,
+ // landing as the article's primary key. An empty-string id broke
+ // Navigation Compose's route matching and crashed on tap.
+ guid = itemGuid?.ifBlank { null } ?: itemLink.ifBlank { null },
title = itemTitle,
link = itemLink,
author = itemAuthor,
@@ -195,7 +200,8 @@ object FeedParser {
inEntry = false
val content = entryContent ?: entrySummary
items += ParsedItem(
- guid = entryId ?: entryLink.ifBlank { null },
+ // Same blank-vs-null fix as the RSS path above.
+ guid = entryId?.ifBlank { null } ?: entryLink.ifBlank { null },
title = entryTitle,
link = entryLink,
author = entryAuthor,