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:
@@ -433,6 +433,9 @@ pub(super) fn handle_settings_buttons(
|
||||
SettingsButton::OpenThemeStore => {
|
||||
// Handled by `handle_sync_buttons`.
|
||||
}
|
||||
SettingsButton::ExportData | SettingsButton::ImportData => {
|
||||
// Handled by `handle_sync_buttons`.
|
||||
}
|
||||
SettingsButton::Done => {
|
||||
screen.0 = false;
|
||||
}
|
||||
@@ -460,14 +463,18 @@ pub(super) fn handle_tab_buttons(
|
||||
}
|
||||
|
||||
/// Handles sync-related settings buttons: Sync Now, Connect, Disconnect,
|
||||
/// and Delete Account. Split from `handle_settings_buttons` to stay within
|
||||
/// Bevy's 16-parameter system limit.
|
||||
/// Delete Account, and the Phase M Export/Import data pair. Split from
|
||||
/// `handle_settings_buttons` to stay within Bevy's 16-parameter system
|
||||
/// limit.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn handle_sync_buttons(
|
||||
interaction_query: Query<(&Interaction, &SettingsButton), Changed<Interaction>>,
|
||||
mut manual_sync: MessageWriter<ManualSyncRequestEvent>,
|
||||
mut configure_sync: MessageWriter<SyncConfigureRequestEvent>,
|
||||
mut logout_sync: MessageWriter<SyncLogoutRequestEvent>,
|
||||
mut delete_account: MessageWriter<DeleteAccountRequestEvent>,
|
||||
mut export_data: MessageWriter<crate::events::DataExportRequestEvent>,
|
||||
mut import_data: MessageWriter<crate::events::DataImportRequestEvent>,
|
||||
#[cfg(not(target_arch = "wasm32"))] mut open_theme_store: MessageWriter<
|
||||
crate::events::ThemeStoreOpenRequestEvent,
|
||||
>,
|
||||
@@ -500,6 +507,12 @@ pub(super) fn handle_sync_buttons(
|
||||
SettingsButton::DeleteAccount => {
|
||||
delete_account.write(DeleteAccountRequestEvent);
|
||||
}
|
||||
SettingsButton::ExportData => {
|
||||
export_data.write(crate::events::DataExportRequestEvent);
|
||||
}
|
||||
SettingsButton::ImportData => {
|
||||
import_data.write(crate::events::DataImportRequestEvent);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user