feat(engine): Phase M — sync transparency + local data backup
Closes out the 13-phase menu redesign. Three parts:
1. Post-sync merge summary: the pull poller now keeps the
`ConflictReport`s the merge produces (previously discarded) in a new
`SyncConflictLog` resource and toasts "Synced — N conflicts, kept
newer values" when a merge wasn't clean. No wire changes — the
server-side `SyncResponse.conflicts` field already existed.
2. Account tab detail: a per-field conflict list under the sync row
("win_streak_current — this device: 3 / server: 5") plus a
clean-merge caption, so the last merge is always inspectable.
3. Local data export/import: new `solitaire_data::transfer` module —
one versioned JSON bundle (settings, stats, achievements, progress)
written atomically next to the other saves, with a version-gated
loader that surfaces every failure instead of defaulting. Account
tab grows an Export/Import button pair (desktop+Android only); the
handler runs in `Last`, off the annotated Update spine. Import
rewrites the live resources, persists via the existing save fns,
and fires SettingsChangedEvent so appliers react normally.
Tests: bundle round-trip/version-gate/missing-file in solitaire_data;
ECS-level export→import round trip and failed-import-warns in
settings_plugin. Gate: workspace tests green, clippy -D warnings clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,25 @@ pub enum SyncStatus {
|
||||
#[derive(Resource, Debug, Clone, Default)]
|
||||
pub struct SyncStatusResource(pub SyncStatus);
|
||||
|
||||
/// Outcome of the most recent successful sync merge (Phase M — sync
|
||||
/// transparency). `None` until the first pull of the session resolves.
|
||||
///
|
||||
/// The Account tab renders the conflict details; the sync poller emits a
|
||||
/// one-line toast summary when `conflicts` is non-empty.
|
||||
#[derive(Resource, Debug, Clone, Default)]
|
||||
pub struct SyncConflictLog(pub Option<SyncMergeSummary>);
|
||||
|
||||
/// Timestamp + per-field conflict list captured from
|
||||
/// [`solitaire_sync::merge`] after a pull resolves.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SyncMergeSummary {
|
||||
/// When the merge resolved.
|
||||
pub at: DateTime<Utc>,
|
||||
/// Fields that could not be merged deterministically (best-effort
|
||||
/// resolution already applied — see `solitaire_sync::merge`).
|
||||
pub conflicts: Vec<solitaire_sync::ConflictReport>,
|
||||
}
|
||||
|
||||
/// Tracks which hint the player is currently cycling through.
|
||||
///
|
||||
/// Incremented on each H press so repeated presses reveal different moves.
|
||||
|
||||
Reference in New Issue
Block a user