feat(backup): add ConversationCache Room DB (entity, DAO, database)

This commit is contained in:
null 2026-06-30 20:42:42 -05:00
parent 230e7f6201
commit ab59e7e5c9
3 changed files with 63 additions and 0 deletions

View File

@ -0,0 +1,24 @@
package app.closer.data.local
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import app.closer.data.local.entity.ConversationCacheEntity
@Dao
interface ConversationCacheDao {
/** Idempotent restore: re-restoring the same message id replaces (latest-wins). */
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsertAll(rows: List<ConversationCacheEntity>)
@Query("SELECT * FROM conversation_cache WHERE conversationId = :conversationId ORDER BY createdAt ASC")
suspend fun forConversation(conversationId: String): List<ConversationCacheEntity>
@Query("SELECT COUNT(*) FROM conversation_cache")
suspend fun count(): Int
@Query("DELETE FROM conversation_cache")
suspend fun clear()
}

View File

@ -0,0 +1,14 @@
package app.closer.data.local
import androidx.room.Database
import androidx.room.RoomDatabase
import app.closer.data.local.entity.ConversationCacheEntity
/**
* Separate Room DB for the restored conversation cache kept apart from the asset-backed [AppDatabase]
* (the prepopulated question bank) so backup/restore never risks that DB's asset schema hash.
*/
@Database(entities = [ConversationCacheEntity::class], version = 1, exportSchema = false)
abstract class ConversationCacheDatabase : RoomDatabase() {
abstract fun conversationCacheDao(): ConversationCacheDao
}

View File

@ -0,0 +1,25 @@
package app.closer.data.local.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
/**
* A restored/backed-up message in the local durable conversation cache. Keyed by the Firestore
* message id so restore is idempotent (upsert = latest-wins on re-restore). `encText` keeps the
* `enc:v1:` body verbatim; `reactionsJson` is a small JSON object of uidemoji. This store is the
* source of truth for restored history and the foundation for the later Option-B switch.
*/
@Entity(tableName = "conversation_cache")
data class ConversationCacheEntity(
@PrimaryKey val messageId: String,
val conversationId: String,
val conversationType: String,
val authorUserId: String,
val type: String,
val encText: String?,
val mediaUrl: String?,
val durationMs: Long?,
val createdAt: Long,
val deleted: Boolean,
val reactionsJson: String
)