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 d56a4442..eb2d7ba0 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 @@ -8,6 +8,7 @@ import com.fabledsword.minstrel.cache.db.dao.CachedArtistDao import com.fabledsword.minstrel.cache.db.dao.CachedLikeDao import com.fabledsword.minstrel.cache.db.dao.CachedPlaylistDao import com.fabledsword.minstrel.cache.db.dao.CachedPlaylistTrackDao +import com.fabledsword.minstrel.cache.db.dao.CachedQuarantineDao import com.fabledsword.minstrel.cache.db.dao.CachedTrackDao import com.fabledsword.minstrel.cache.db.dao.SyncMetadataDao import com.fabledsword.minstrel.cache.db.entities.CachedAlbumEntity @@ -15,6 +16,7 @@ import com.fabledsword.minstrel.cache.db.entities.CachedArtistEntity import com.fabledsword.minstrel.cache.db.entities.CachedLikeEntity import com.fabledsword.minstrel.cache.db.entities.CachedPlaylistEntity import com.fabledsword.minstrel.cache.db.entities.CachedPlaylistTrackEntity +import com.fabledsword.minstrel.cache.db.entities.CachedQuarantineEntity import com.fabledsword.minstrel.cache.db.entities.CachedTrackEntity import com.fabledsword.minstrel.cache.db.entities.SyncMetadataEntity @@ -40,6 +42,7 @@ import com.fabledsword.minstrel.cache.db.entities.SyncMetadataEntity CachedLikeEntity::class, CachedPlaylistEntity::class, CachedPlaylistTrackEntity::class, + CachedQuarantineEntity::class, ], version = 1, exportSchema = true, @@ -53,4 +56,5 @@ abstract class AppDatabase : RoomDatabase() { abstract fun cachedLikeDao(): CachedLikeDao abstract fun cachedPlaylistDao(): CachedPlaylistDao abstract fun cachedPlaylistTrackDao(): CachedPlaylistTrackDao + abstract fun cachedQuarantineDao(): CachedQuarantineDao } diff --git a/android/app/src/main/java/com/fabledsword/minstrel/cache/db/dao/CachedQuarantineDao.kt b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/dao/CachedQuarantineDao.kt new file mode 100644 index 00000000..9cc73e7f --- /dev/null +++ b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/dao/CachedQuarantineDao.kt @@ -0,0 +1,36 @@ +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.CachedQuarantineEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface CachedQuarantineDao { + /** Newest-first listing for the Quarantine screen. */ + @Query("SELECT * FROM cached_quarantine_mine ORDER BY createdAt DESC") + fun observeAll(): Flow> + + /** Just the IDs — feed-filtering code reads this to hide tracks. */ + @Query("SELECT trackId FROM cached_quarantine_mine") + fun observeFlaggedTrackIds(): Flow> + + @Query( + "SELECT EXISTS(SELECT 1 FROM cached_quarantine_mine WHERE trackId = :trackId)", + ) + fun observeIsHidden(trackId: String): Flow + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun upsert(row: CachedQuarantineEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun upsertAll(rows: List) + + @Query("DELETE FROM cached_quarantine_mine WHERE trackId = :trackId") + suspend fun deleteByTrackId(trackId: String) + + @Query("DELETE FROM cached_quarantine_mine WHERE trackId IN (:trackIds)") + suspend fun deleteByTrackIds(trackIds: List) +} diff --git a/android/app/src/main/java/com/fabledsword/minstrel/cache/db/entities/CachedQuarantineEntity.kt b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/entities/CachedQuarantineEntity.kt new file mode 100644 index 00000000..5d50238f --- /dev/null +++ b/android/app/src/main/java/com/fabledsword/minstrel/cache/db/entities/CachedQuarantineEntity.kt @@ -0,0 +1,36 @@ +package com.fabledsword.minstrel.cache.db.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant + +/** + * The current user's quarantine flag for one track. Mirrors + * `flutter_client/lib/cache/db.dart`'s `CachedQuarantineMine` Drift + * table. + * + * The flat denormalized track/album/artist columns let the Quarantine + * screen render off this single table without a join — the server + * returns the full snapshot on /api/me/quarantine so we mirror that + * shape locally. + * + * `createdAt` is the server-issued timestamp preserved as an opaque + * ISO-8601 string (it's the canonical "when this was flagged" value); + * `fetchedAt` is our local sync-time marker. + */ +@Entity(tableName = "cached_quarantine_mine") +data class CachedQuarantineEntity( + @PrimaryKey val trackId: String, + val reason: String, + val notes: String? = null, + val createdAt: String, + val trackTitle: String, + val trackDurationMs: Int = 0, + val albumId: String, + val albumTitle: String, + val albumCoverArtPath: String? = null, + val artistId: String, + val artistName: String, + val fetchedAt: Instant = Clock.System.now(), +)