Coverage Summary for Class: SyncMeta (com.georgeci.moneysurfer.sync.repository)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| SyncMeta |
100%
(1/1)
|
100%
(1/1)
|
|
100%
(6/6)
|
100%
(27/27)
|
package com.georgeci.moneysurfer.sync.repository
import kotlinx.coroutines.flow.Flow
import kotlin.time.Instant
/**
* Per-(scopeKey, collection) sync metadata.
*
* - [cursor] — `lastPulledAt` value used as `WHERE updatedAt > cursor` filter
* in the next pull. Updated only when remote rows are successfully written
* to local Room within the same transaction.
* - [lastSyncSuccessAt] / [lastSyncAttemptAt] — timestamps for the UI badge.
*
* [scopeKey] is typically a workspace id string. [collection] is an opaque
* Firestore collection name string (e.g. "accounts", "transactions").
*
* Lives in `sync:api` rather than next to its Room-backed implementation because
* the Settings → Sync screen reads the cursors: "a cursor newer than the remote
* change" is the answer to "why did the pull bring nothing", and a feature module
* must not depend on `sync:default` to ask it.
*
* See SyncCoordinatorFAQ.md №14, sync.md §4.3.
*/
interface SyncMetaRepository {
suspend fun cursor(scopeKey: String, collection: String): Instant?
suspend fun setCursor(scopeKey: String, collection: String, cursor: Instant)
suspend fun markAttempt(scopeKey: String, collection: String, at: Instant)
suspend fun markSuccess(scopeKey: String, collection: String, at: Instant)
suspend fun clearScope(scopeKey: String)
fun observe(scopeKey: String): Flow<List<SyncMeta>>
}
data class SyncMeta(
val scopeKey: String,
val collection: String,
val lastPulledAt: Instant?,
val lastSyncSuccessAt: Instant?,
val lastSyncAttemptAt: Instant?,
)