mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
156 lines
5.1 KiB
JavaScript
156 lines
5.1 KiB
JavaScript
// Per-video opt-in state for the NIP-71 mirror, persisted locally per
|
|
// pubkey+videoRootId. Off by default — a publisher explicitly opts a video in
|
|
// (from the My Videos tab). Local-only is fine: it's a convenience for the
|
|
// management UI; the authoritative state is whether the mirror event exists on
|
|
// relays.
|
|
|
|
const FLAG_KEY = "bitvid:nip71-mirror:v1";
|
|
const AUTO_SHARE_KEY = "bitvid:nip71-autoshare:v1";
|
|
|
|
function readFlags() {
|
|
try {
|
|
if (typeof localStorage === "undefined") {
|
|
return {};
|
|
}
|
|
const raw = localStorage.getItem(FLAG_KEY);
|
|
const parsed = raw ? JSON.parse(raw) : {};
|
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
} catch (error) {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function writeFlags(flags) {
|
|
try {
|
|
if (typeof localStorage !== "undefined") {
|
|
localStorage.setItem(FLAG_KEY, JSON.stringify(flags || {}));
|
|
}
|
|
} catch (error) {
|
|
// Best-effort.
|
|
}
|
|
}
|
|
|
|
function norm(value) {
|
|
return typeof value === "string" ? value.trim() : "";
|
|
}
|
|
|
|
export function isMirrorEnabled(pubkey, videoRootId) {
|
|
const pk = norm(pubkey);
|
|
const root = norm(videoRootId);
|
|
if (!pk || !root) {
|
|
return false;
|
|
}
|
|
return readFlags()[pk]?.[root] === true;
|
|
}
|
|
|
|
export function setMirrorEnabled(pubkey, videoRootId, enabled) {
|
|
const pk = norm(pubkey);
|
|
const root = norm(videoRootId);
|
|
if (!pk || !root) {
|
|
return;
|
|
}
|
|
const flags = readFlags();
|
|
const entry = flags[pk] && typeof flags[pk] === "object" ? flags[pk] : {};
|
|
if (enabled === true) {
|
|
entry[root] = true;
|
|
} else {
|
|
delete entry[root];
|
|
}
|
|
flags[pk] = entry;
|
|
writeFlags(flags);
|
|
}
|
|
|
|
// Pure decision for a toggle click: given the current opt-in state and the
|
|
// service's eligibility check (canMirror), what should happen?
|
|
// { action: "remove" } — currently on → tear down
|
|
// { action: "publish" } — eligible + off → publish
|
|
// { action: "blocked", reason: "<why>" } — off but ineligible (private / nsfw / no-url)
|
|
export function resolveMirrorToggle({ enabled, eligibility } = {}) {
|
|
if (enabled === true) {
|
|
return { action: "remove" };
|
|
}
|
|
if (!eligibility || eligibility.ok !== true) {
|
|
return { action: "blocked", reason: eligibility?.reason || "ineligible" };
|
|
}
|
|
return { action: "publish" };
|
|
}
|
|
|
|
// Account-level default for new public videos (per pubkey, on by default). A
|
|
// missing value means "share" so existing users receive the new default; an
|
|
// explicit false is the durable account-level opt-out. An individual upload can
|
|
// still opt out without changing this preference.
|
|
function readAutoShare() {
|
|
try {
|
|
if (typeof localStorage === "undefined") {
|
|
return {};
|
|
}
|
|
const raw = localStorage.getItem(AUTO_SHARE_KEY);
|
|
const parsed = raw ? JSON.parse(raw) : {};
|
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
} catch (error) {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
export function isAutoShareEnabled(pubkey) {
|
|
const pk = norm(pubkey);
|
|
return pk ? readAutoShare()[pk] !== false : false;
|
|
}
|
|
|
|
export function setAutoShareEnabled(pubkey, enabled) {
|
|
const pk = norm(pubkey);
|
|
if (!pk) {
|
|
return;
|
|
}
|
|
const map = readAutoShare();
|
|
map[pk] = enabled === true;
|
|
try {
|
|
if (typeof localStorage !== "undefined") {
|
|
localStorage.setItem(AUTO_SHARE_KEY, JSON.stringify(map));
|
|
}
|
|
} catch (error) {
|
|
// best-effort
|
|
}
|
|
}
|
|
|
|
// On publish of a NEW video: mirror it only when the account default and the
|
|
// per-upload choice permit it, and it is eligible (public + hosted URL +
|
|
// allowed). Otherwise do nothing.
|
|
export function resolvePublishSync({ featureOn, autoShare, eligible } = {}) {
|
|
if (featureOn !== true || autoShare !== true) {
|
|
return { action: "none" };
|
|
}
|
|
return eligible === true ? { action: "publish" } : { action: "none" };
|
|
}
|
|
|
|
// Lifecycle decisions for keeping an opted-in mirror in sync (used by the
|
|
// nostrService edit/delete hooks). Pure so they're cheat-resistant to test.
|
|
//
|
|
// On EDIT of a mirrored video: re-publish if still eligible; if it became
|
|
// ineligible (e.g. flipped private), pull the mirror down and clear the flag.
|
|
export function resolveEditSync({ featureOn, enabled, eligible } = {}) {
|
|
if (featureOn !== true || enabled !== true) {
|
|
return { action: "none" };
|
|
}
|
|
return eligible === true ? { action: "publish" } : { action: "unshare" };
|
|
}
|
|
|
|
// On DELETE of a video: always attempt to tear the mirror down whenever the
|
|
// feature is on — do NOT gate on the local `enabled` flag. That flag lives only
|
|
// in this browser's localStorage (see top of file), so trusting it here orphans
|
|
// the NIP-71 mirror whenever the video was shared on one device/session and
|
|
// deleted from another (or after the cache was cleared): the mirror keeps living
|
|
// on other apps even though the bitvid video is gone. `remove()` is idempotent
|
|
// and cheap (NIP-09 + empty tombstone are no-ops if no mirror exists), so an
|
|
// unconditional teardown is safe. `enabled` is accepted for signature symmetry
|
|
// with resolveEditSync but is intentionally ignored.
|
|
export function resolveDeleteSync({ featureOn, enabled } = {}) {
|
|
void enabled;
|
|
if (featureOn !== true) {
|
|
return { action: "none" };
|
|
}
|
|
return { action: "unshare" };
|
|
}
|
|
|
|
export const NIP71_MIRROR_FLAG_KEY = FLAG_KEY;
|