Files
bitvid/js/services/settingsSyncFlags.js
T
thePR0M3TH3ANandClaude Opus 4.8 d552e9017c feat(sync): NWC wallet sync (opt-in, spend-gated) + shared opt-in flags
Adds encrypted cross-login sync for the NWC wallet connection (todo #15),
reusing the encrypted-sync core under d-tag "bitvid:nwc".

- js/services/settingsSyncFlags.js: shared per-pubkey opt-in flag store
  (storage/wallet); storageSyncService refactored onto it (identical behavior,
  tests still green).
- js/services/walletSyncService.js: factory (NwcSettingsService is per-app, not a
  singleton). push() syncs the current nwcUri (+defaultZap); pull() applies it via
  updateActiveNwcSettings; disable() wipes the note.
- Wallet pane (components/profile-modal.html + ProfileWalletController): opt-in
  toggle + Restore button + a strong SPENDING warning, shown only when a signer
  can encrypt. Enabling requires an explicit window.confirm (the URI can spend).
  Re-pushes on save when enabled; and on wallet DISCONNECT while synced it clears
  the note so a removed spending URI doesn't linger (encrypted) on relays.

tests/wallet-sync-service.test.mjs: enable-pushes-uri, pull-restores-via-
updateActiveNwcSettings, disable-clears, no-wallet-doesn't-publish. Mutation-
verified (skip-apply + skip-clear both fail).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-21 17:57:39 -04:00

59 lines
1.5 KiB
JavaScript

// Per-pubkey opt-in flags for encrypted settings sync (todo #15). Shared by the
// storage and wallet sync services so the toggle state persists locally.
// Shape: { [pubkey]: { storage?: boolean, wallet?: boolean } }. Off by default.
const FLAG_STORAGE_KEY = "bitvid:settings-sync:v1";
function readFlags() {
try {
if (typeof localStorage === "undefined") {
return {};
}
const raw = localStorage.getItem(FLAG_STORAGE_KEY);
if (!raw) {
return {};
}
const parsed = JSON.parse(raw);
return parsed && typeof parsed === "object" ? parsed : {};
} catch (error) {
return {};
}
}
function writeFlags(flags) {
try {
if (typeof localStorage === "undefined") {
return;
}
localStorage.setItem(FLAG_STORAGE_KEY, JSON.stringify(flags || {}));
} catch (error) {
// Best-effort; the flag is a convenience, not a source of truth.
}
}
function normalizePubkey(pubkey) {
return typeof pubkey === "string" ? pubkey.trim() : "";
}
export function isSyncEnabled(pubkey, kind) {
const key = normalizePubkey(pubkey);
if (!key || !kind) {
return false;
}
return readFlags()[key]?.[kind] === true;
}
export function setSyncEnabled(pubkey, kind, enabled) {
const key = normalizePubkey(pubkey);
if (!key || !kind) {
return;
}
const flags = readFlags();
const entry = flags[key] && typeof flags[key] === "object" ? flags[key] : {};
entry[kind] = enabled === true;
flags[key] = entry;
writeFlags(flags);
}
export const SETTINGS_SYNC_FLAG_KEY = FLAG_STORAGE_KEY;