feat(android): port cached_quarantine_mine (M8 4.2 slice 4)
Mirrors flutter_client/lib/cache/db.dart's CachedQuarantineMine — the user's flagged-as-hidden tracks. Server returns the full denormalized snapshot on /api/me/quarantine; the cache mirrors that shape so the Quarantine screen renders without a join. `createdAt` is a server ISO-8601 string (canonical timestamp); `fetchedAt` is our local sync marker. DAO covers the three known consumers: - observeAll for the Quarantine screen (newest first) - observeFlaggedTrackIds for feed-level filtering - observeIsHidden(trackId) scalar Flow for per-tile UI Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
+36
@@ -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<List<CachedQuarantineEntity>>
|
||||
|
||||
/** Just the IDs — feed-filtering code reads this to hide tracks. */
|
||||
@Query("SELECT trackId FROM cached_quarantine_mine")
|
||||
fun observeFlaggedTrackIds(): Flow<List<String>>
|
||||
|
||||
@Query(
|
||||
"SELECT EXISTS(SELECT 1 FROM cached_quarantine_mine WHERE trackId = :trackId)",
|
||||
)
|
||||
fun observeIsHidden(trackId: String): Flow<Boolean>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsert(row: CachedQuarantineEntity)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertAll(rows: List<CachedQuarantineEntity>)
|
||||
|
||||
@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<String>)
|
||||
}
|
||||
Vendored
+36
@@ -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(),
|
||||
)
|
||||
Reference in New Issue
Block a user