diff --git a/components/profile-modal.html b/components/profile-modal.html index 51421cdf..1f19b708 100644 --- a/components/profile-modal.html +++ b/components/profile-modal.html @@ -1028,6 +1028,10 @@ role="status" aria-live="polite" >
+{ void syncNip71MirrorAfterDelete({ videoRootId, video, pubkey }); }); + const offPublish = nostrService.on("videos:published", (detail) => { + void syncNip71MirrorAfterPublish(detail); + }); return () => { registered = false; if (typeof offEdit === "function") offEdit(); if (typeof offDelete === "function") offDelete(); + if (typeof offPublish === "function") offPublish(); }; } diff --git a/js/ui/profileModal/MyVideosController.js b/js/ui/profileModal/MyVideosController.js index da22c043..2ef0992f 100644 --- a/js/ui/profileModal/MyVideosController.js +++ b/js/ui/profileModal/MyVideosController.js @@ -10,6 +10,8 @@ import { isMirrorEnabled, setMirrorEnabled, resolveMirrorToggle, + isAutoShareEnabled, + setAutoShareEnabled, } from "../../services/nip71MirrorFlags.js"; const MIRROR_REASON_TEXT = { @@ -48,6 +50,8 @@ export class MyVideosController { this.refreshBtn = null; this.orphansSection = null; this.orphanListEl = null; + this.autoShareRow = null; + this.autoShareInput = null; this.loading = false; this.publicBaseUrl = ""; this.pubkey = ""; @@ -63,6 +67,10 @@ export class MyVideosController { document.getElementById("profileMyVideosOrphans") || null; this.orphanListEl = document.getElementById("profileMyVideosOrphanList") || null; + this.autoShareRow = + document.getElementById("profileMyVideosAutoShareRow") || null; + this.autoShareInput = + document.getElementById("profileMyVideosAutoShare") || null; } registerEventListeners() { @@ -71,6 +79,26 @@ export class MyVideosController { void this.populate({ forceFetch: true }); }); } + if (this.autoShareInput instanceof HTMLInputElement) { + this.autoShareInput.addEventListener("change", () => { + const pubkey = this.getPubkey(); + if (!pubkey) { + return; + } + setAutoShareEnabled(pubkey, this.autoShareInput.checked === true); + }); + } + } + + refreshAutoShareToggle(pubkey) { + if (!(this.autoShareRow instanceof HTMLElement)) { + return; + } + const eligible = FEATURE_NIP71_MIRROR && !!pubkey; + this.autoShareRow.classList.toggle("hidden", !eligible); + if (eligible && this.autoShareInput instanceof HTMLInputElement) { + this.autoShareInput.checked = isAutoShareEnabled(pubkey); + } } getPubkey() { @@ -114,6 +142,7 @@ export class MyVideosController { return; } const pubkey = this.getPubkey(); + this.refreshAutoShareToggle(pubkey); if (!pubkey) { this.renderRows([]); this.setSummary(""); diff --git a/tests/nip71-mirror-flags.test.mjs b/tests/nip71-mirror-flags.test.mjs index f3d36245..62318e16 100644 --- a/tests/nip71-mirror-flags.test.mjs +++ b/tests/nip71-mirror-flags.test.mjs @@ -10,6 +10,9 @@ import { resolveMirrorToggle, resolveEditSync, resolveDeleteSync, + isAutoShareEnabled, + setAutoShareEnabled, + resolvePublishSync, } from "../js/services/nip71MirrorFlags.js"; const PK = "a".repeat(64); @@ -72,3 +75,20 @@ test("resolveDeleteSync: removes the mirror only when it was shared", () => { assert.deepEqual(resolveDeleteSync({ featureOn: true, enabled: false }), { action: "none" }); assert.deepEqual(resolveDeleteSync({ featureOn: false, enabled: true }), { action: "none" }); }); + +test("auto-share preference persists per pubkey, off by default", () => { + localStorage.clear(); + assert.equal(isAutoShareEnabled(PK), false); + setAutoShareEnabled(PK, true); + assert.equal(isAutoShareEnabled(PK), true); + assert.equal(isAutoShareEnabled("c".repeat(64)), false, "scoped per pubkey"); + setAutoShareEnabled(PK, false); + assert.equal(isAutoShareEnabled(PK), false); +}); + +test("resolvePublishSync: auto-mirror a new video only when auto-share on AND eligible", () => { + assert.deepEqual(resolvePublishSync({ featureOn: true, autoShare: true, eligible: true }), { action: "publish" }); + assert.deepEqual(resolvePublishSync({ featureOn: true, autoShare: true, eligible: false }), { action: "none" }); + assert.deepEqual(resolvePublishSync({ featureOn: true, autoShare: false, eligible: true }), { action: "none" }); + assert.deepEqual(resolvePublishSync({ featureOn: false, autoShare: true, eligible: true }), { action: "none" }); +}); diff --git a/tests/nip71-mirror-sync.test.mjs b/tests/nip71-mirror-sync.test.mjs index 9bca3c29..f76d210e 100644 --- a/tests/nip71-mirror-sync.test.mjs +++ b/tests/nip71-mirror-sync.test.mjs @@ -37,6 +37,7 @@ test("initNip71MirrorSync subscribes to edit+delete events, idempotently", () => const off = initNip71MirrorSync(svc); assert.equal(events["videos:edited"]?.length, 1, "subscribes to videos:edited"); assert.equal(events["videos:deleted"]?.length, 1, "subscribes to videos:deleted"); + assert.equal(events["videos:published"]?.length, 1, "subscribes to videos:published"); initNip71MirrorSync(svc); // guard prevents double-registration assert.equal(events["videos:edited"].length, 1, "no double-register");