feat(sync): per-domain settings sync control - #160
Merged
Conversation
Collapses the three copied json-collection loops into one helper and gives it a skip set, so the TypeScript side can withhold a config file from the uploaded blob. Refs #42
…omment collect_json_dir_skip_matches_the_prefixed_key_not_the_bare_filename proves skip matching is on prefix+filename, not the bare name, in both directions. Also restores the client-side/hash-verified note dropped when the three collection loops were consolidated.
mode, the light/dark pair, both schedule times and location reached other devices only via theme.json. Refs #42
Re-enabling a domain must publish this device's values instead of losing the next merge to an old local timestamp. Refs #42
…wire Themes now travel only in the settings bundle: applyRemoteTheme is gone and theme.json is withheld from the blob, so the themes toggle actually holds. settings.json is written already-filtered, since that file is what backup_export uploads. Refs #42
- themes.ts: replace customThemes on import instead of upserting, so a remote deletion actually propagates instead of resurrecting on the next export. - sync.ts: split theme.json withholding into a server variant (always withheld — themes travel in the settings bundle) and a plugin variant (withheld only when the themes domain is off — it's the plugin path's only theme route via runtime.ts importStates). - sync.ts/runtime.ts: extract writeFilteredSettings() and call it from both push() and the plugin exportState path, so exportState no longer uploads a stale, unfiltered settings.json (same class as issue #47). - registry.ts: mergeUserDataBundle falls back to a handler's live export()/getTimestamp() when its section is absent from the local bundle, so re-enabling a domain runs a real LWW comparison instead of letting a stale remote win unconditionally. Refs #42
Final review fix wave for per-setting cloud sync toggles: - schedule a push when a domain is switched off, so the server copy is withdrawn instead of held forever (the "never uploaded" guarantee was false for data already synced before the toggle) - gate the plugin import path's theme apply on the themes domain toggle, matching the outbound filter - stop swallowing settings_save failures in writeFilteredSettings, since a hidden failure meant backup_export uploaded the unfiltered pre-toggle file - warn in the manual-import UI that including Themes replaces this device's custom themes (import now REPLACES, not merges) - add a drift test tying SYNC_SETTING_DOMAINS to USER_DATA_HANDLERS so a new handler can't silently end up permanently synced - doc/comment cleanup: stale getSkippedSyncFiles docstring, the filterOutgoing/filterIncoming duplication, buildUserDataBundle's unfiltered-by-design contract, and export() side-effect-free requirement
It is a consequence notice, not a failed import; error-red read as "something is broken". Refs #42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 1 of #42. Adds per-domain cloud-sync control: a user can switch sync off for Themes, Interface, Shortcuts, App settings or Recent people, and the held-back domain never enters the uploaded blob.
Per-setting (leaf-level) control is PR 2 and is not in this change.
What changed
The guarantee is enforced at write time.
settings.jsonis both the local merge base and part of the encrypted blob (backup_exportslurps every*.jsonin the config dir), so a switched-off domain has to be absent from that file, not merely ignored on arrival.push()now writesfilterOutgoing(buildUserDataBundle())before the exporter reads it, andfilterIncomingdrops switched-off sections from a remote bundle before the merge.The duplicate theme wire is retired. Themes previously travelled twice: the
themesbundle section and the wholetheme.jsonfile, which the exporter uploaded wholesale andapplyRemoteThemeapplied on pull. The second wire would have defeated the toggle entirely. It is gone, andbackup_exportgained a genericskip_filesparameter (mirroring the existingexcluded_ids) to withhold named config files.That deletion is only safe because
themesHandlerwas first widened to carry the six fields onlytheme.jsonheld —mode,lightThemeId,darkThemeId, both schedule times, andlocation. Verified on the live app: without that, auto light/dark and the scheduled switch would have silently stopped syncing.Re-enabling publishes this device. Each handler gained
touch(). Switching a domain back on stamps it as changed now, so a value curated locally while sync was off is not lost to the other device's newer timestamp. Switching a domain off also schedules a push, so the copy already on the server is withdrawn rather than sitting there indefinitely.Settings → Sync gains a second toggle group.
vaultsis deliberately absent — it is tombstone-merged data, not a preference, and switching it off would strand deletes.Verification
Live, on a real decrypted blob (headless build, WebDriver), run twice — once mid-branch and again at final HEAD:
theme.jsonin blobfalsefalsetruefalseactiveThemeId"voltius-light"nullmode(automation)"manual"nullWith themes off, only that section disappears — the other five survive. Plugin destinations skip
[]when themes are on and["theme.json"]when off.Suites: 490 files / 3793 tests green;
tsc --noEmitclean;cargo test commands::sync7/7.Behaviour changes worth knowing
mergeUserDataBundlereads live stores when a section is missing from the local merge base. Without it, re-enabling a domain let a stale remote win unconditionally —lastWriteWinsnever compared timestamps because the local side was absent. Handlers'export()must stay side-effect free; that is now stated on the interface.theme.json(themes travel in the bundle); plugin/gist destinations withhold it only when themes are off, because that path appliestheme.jsondirectly and never applies the settings bundle. Deleting its wire would have silently ended theme sync for gist users.Needs a human
Russian strings are machine-written and want a native check —
раскладкиfor "layouts" reads as keyboard layout in common usage, which collides with the separate Shortcuts domain (макет/компоновкаsuggested), andНедавние людиis a stiff calque.Cross-version
An older client pulling a new blob finds no
theme.json, keeps its local theme, and still receives themes through the bundle section — present since v0.1.40. A newer client pulling an older blob ignores thetheme.jsonstill in it.