Files
bitvid/js/nostr/encryptedSyncFacade.js
T
thePR0M3TH3ANandClaude Opus 4.8 7376ff91e0 feat(sync): Storage pane UI for encrypted settings sync (opt-in toggle + restore)
Wires the storage-credential sync (6f7f4196) into the profile Storage pane.

UI (components/profile-modal.html): a "Cross-device sync" card shown only when
storage is unlocked — an opt-in toggle ("Sync these settings to my Nostr account
(encrypted)"), a "Restore from my Nostr account" button, the public-relay /
key-compromise warning copy, and a status line.

Controller (ProfileStorageController):
- toggle on -> storageSyncService.enable() (encrypt + publish), with rollback +
  message if nothing is stored yet; toggle off -> disable() (wipe the note)
- restore -> pull() + re-render the pane so the restored connection shows
- after a successful Save, re-push the synced copy when sync is enabled

Also routes the core's relay READ through the subscription manager (injected
listEvents) instead of pool.list, satisfying lint:pool-access for the new file;
the facade supplies it via nostrClient.getSubscriptionManager().list.

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

58 lines
2.1 KiB
JavaScript

// Default encrypted-sync instance wired to the live Nostr client + active signer.
// The pure core lives in ./encryptedSync.js; this file is the only place that
// reaches into the singletons, so the core stays unit-testable.
import { nostrClient } from "../nostrClientFacade.js";
import { getActiveSigner } from "./index.js";
import {
publishEventToRelays,
summarizePublishResults,
} from "../nostrPublish.js";
import { createEncryptedSyncManager } from "./encryptedSync.js";
function resolveReadRelays() {
const read = Array.isArray(nostrClient?.readRelays) ? nostrClient.readRelays : [];
if (read.length) {
return read;
}
return Array.isArray(nostrClient?.relays) ? nostrClient.relays : [];
}
export const encryptedSync = createEncryptedSyncManager({
getActivePubkey: () => nostrClient?.pubkey || "",
getSigner: () => getActiveSigner(),
// Write to the full write set (not the capped read set) so the update reaches
// everywhere — mirrors getDeletePublishRelays from the watch-history work.
getWriteRelays: () =>
typeof nostrClient?.getDeletePublishRelays === "function"
? nostrClient.getDeletePublishRelays()
: [],
getReadRelays: resolveReadRelays,
getPool: () => nostrClient?.pool || null,
// Reads are routed through the subscription manager (deduped + health-gated),
// never pool.list directly (lint:pool-access).
listEvents: (relays, filters) => {
const manager =
typeof nostrClient?.getSubscriptionManager === "function"
? nostrClient.getSubscriptionManager()
: null;
if (!manager || typeof manager.list !== "function") {
return Promise.resolve([]);
}
return manager.list({ filters, relays });
},
publishEventToRelays,
summarizePublishResults,
signEvent: async (template) => {
const signer = getActiveSigner();
if (!signer || typeof signer.signEvent !== "function") {
const err = new Error("Active signer missing signEvent support.");
err.code = "sign-event-missing";
throw err;
}
return signer.signEvent(template);
},
});
export default encryptedSync;