diff --git a/android/app/src/main/java/com/fabledsword/minstrel/cache/db/AppDatabase.kt b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/AppDatabase.kt index f78325a9..a55f590f 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/cache/db/AppDatabase.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/AppDatabase.kt @@ -6,6 +6,7 @@ import androidx.room.TypeConverters import com.fabledsword.minstrel.cache.db.dao.AudioCacheIndexDao import com.fabledsword.minstrel.cache.db.dao.CachedAlbumDao import com.fabledsword.minstrel.cache.db.dao.CachedArtistDao +import com.fabledsword.minstrel.cache.db.dao.CachedHomeIndexDao import com.fabledsword.minstrel.cache.db.dao.CachedLikeDao import com.fabledsword.minstrel.cache.db.dao.CachedMutationDao import com.fabledsword.minstrel.cache.db.dao.CachedPlaylistDao @@ -17,6 +18,7 @@ import com.fabledsword.minstrel.cache.db.dao.SyncMetadataDao import com.fabledsword.minstrel.cache.db.entities.AudioCacheIndexEntity import com.fabledsword.minstrel.cache.db.entities.CachedAlbumEntity import com.fabledsword.minstrel.cache.db.entities.CachedArtistEntity +import com.fabledsword.minstrel.cache.db.entities.CachedHomeIndexEntity import com.fabledsword.minstrel.cache.db.entities.CachedLikeEntity import com.fabledsword.minstrel.cache.db.entities.CachedMutationEntity import com.fabledsword.minstrel.cache.db.entities.CachedPlaylistEntity @@ -52,6 +54,7 @@ import com.fabledsword.minstrel.cache.db.entities.SyncMetadataEntity AudioCacheIndexEntity::class, CachedMutationEntity::class, CachedResumeStateEntity::class, + CachedHomeIndexEntity::class, ], version = 1, exportSchema = true, @@ -69,4 +72,5 @@ abstract class AppDatabase : RoomDatabase() { abstract fun audioCacheIndexDao(): AudioCacheIndexDao abstract fun cachedMutationDao(): CachedMutationDao abstract fun cachedResumeStateDao(): CachedResumeStateDao + abstract fun cachedHomeIndexDao(): CachedHomeIndexDao } diff --git a/android/app/src/main/java/com/fabledsword/minstrel/cache/db/dao/CachedHomeIndexDao.kt b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/dao/CachedHomeIndexDao.kt new file mode 100644 index 00000000..aea08ff3 --- /dev/null +++ b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/dao/CachedHomeIndexDao.kt @@ -0,0 +1,33 @@ +package com.fabledsword.minstrel.cache.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.fabledsword.minstrel.cache.db.entities.CachedHomeIndexEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface CachedHomeIndexDao { + @Query( + "SELECT * FROM cached_home_index " + + "WHERE section = :section ORDER BY position ASC", + ) + fun observeBySection(section: String): Flow> + + @Query( + "SELECT * FROM cached_home_index " + + "WHERE section = :section ORDER BY position ASC", + ) + suspend fun getBySection(section: String): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun upsertAll(rows: List) + + /** Replace-all pattern; sync wipes a section then re-inserts. */ + @Query("DELETE FROM cached_home_index WHERE section = :section") + suspend fun deleteBySection(section: String) + + @Query("DELETE FROM cached_home_index") + suspend fun clear() +} diff --git a/android/app/src/main/java/com/fabledsword/minstrel/cache/db/entities/CachedHomeIndexEntity.kt b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/entities/CachedHomeIndexEntity.kt new file mode 100644 index 00000000..70d5e11c --- /dev/null +++ b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/entities/CachedHomeIndexEntity.kt @@ -0,0 +1,34 @@ +package com.fabledsword.minstrel.cache.db.entities + +import androidx.room.Entity +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant + +/** + * Per-item row driving the Home screen sections. Mirrors + * `flutter_client/lib/cache/db.dart`'s `CachedHomeIndex` Drift table. + * + * `section` is one of (matching /api/home keys): + * - "recently_added_albums" + * - "rediscover_albums" + * - "rediscover_artists" + * - "most_played_tracks" + * - "last_played_artists" + * + * `entityType` is "album" | "artist" | "track" — dispatches hydration + * to the right per-entity endpoint when a tile is rendered. + * + * The composite PK (section, position) means there's exactly one row + * per slot per section; sync replaces in-place by upsert. + */ +@Entity( + tableName = "cached_home_index", + primaryKeys = ["section", "position"], +) +data class CachedHomeIndexEntity( + val section: String, + val position: Int, + val entityType: String, + val entityId: String, + val fetchedAt: Instant = Clock.System.now(), +)