The wallet already rode the same login "restore from Nostr" offer as storage,
but a once-per-device "offered" flag suppressed it — so on a device without
your wallet you fell back to the manual Sync button. Now:
- encryptedSyncItem: add hasLocal(pubkey) (non-null payload => item present
locally, e.g. a connected wallet / configured storage).
- settingsRestorePrompt: re-offer an item whenever it's MISSING locally and a
Nostr backup exists — bypassing the "offered once" flag — so it surfaces
exactly when you'd want it. Only respect the once-per-device flag when the
item IS present locally (no nagging). A new per-device "dismissed" flag,
set when you decline, keeps it quiet after an explicit "no".
- Accepting enables sync, so it auto-pulls silently thereafter (the "both"
behavior: silent auto-pull when enabled + popup when missing & off).
- Unknown services (no hasLocal) keep the legacy behavior.
- tests: re-offer when missing despite prior offer; no re-offer when present;
decline stays quiet.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Two fixes from live testing:
- "NWC doesn't sync unless I click Restore": devices with sync ENABLED only
ever pushed-on-save — edits from another device sat unseen until the
manual Restore click. encryptedSyncItem.autoPullIfNewer pulls the remote
note at login when its created_at is newer than this device's last-synced
marker (set on push AND now after a successful pull, so the same note is
never re-pulled and the next conflict check stays honest). Wired into the
login restore flow ahead of the one-time offer; disabled items are never
touched.
- "Could not get user information" on a known-good creator address: that
reason string comes from the LNURL SERVER, and the failing fetch was the
PLATFORM-FEE address, not the creator's. prepareLightningContext now
wraps both metadata fetches so the toast names which address failed
(e.g. "Platform fee Lightning address (…): Could not get user
information") instead of blaming the creator.
Co-Authored-By: Claude Fable 5 <[email protected]>
walletSyncService and storageSyncService were near-identical copies (the "two
sync systems"). Extract the shared machinery into createEncryptedSyncItem() —
per-pubkey enable flags, conflict-checked push, cheap remote-exists check,
enable/disable/pull — so each service is now a thin config differing only in
d-tag, kind, availability, and buildPayload/applyPayload. Public surface
(isAvailable/isEnabled/hasRemote/push/pull/enable/disable + the *_DTAG export)
preserved; both dedicated suites pass (8).
Next (step 2): shared confirm dialog + consistent notifications + seamless
auto-sync in the two profile controllers (the user-facing UX unification).
Co-Authored-By: Claude Opus 4.8 <[email protected]>