mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 21:02:28 +00:00
[ui] #34(b): My Videos mirror toggle reflects relay truth, not just the local flag
The per-row "Share to apps" toggle read only the device-local opt-in flag
(nip71MirrorFlags), so a fresh device / cleared cache / other device reported a
video as un-shared even when its NIP-71 mirror events (34235/34236) exist on the
user's relays.
- nip71MirrorService: add batched findMirrors(videos) -> Map<videoRootId,
{mirrored, kinds, duplicate}>, one lookup per root via the SubscriptionManager
chokepoint (no direct pool.list).
- MyVideosController: tag each mirror button with data-mirror-root; after the
synchronous (local-flag) render, refreshMirrorStates() looks up relay truth,
reconciles the local flag to match (flag stays a cache hint), and corrects each
button label. Fire-and-forget; detection failures fall back to the local flag.
- tests/my-videos-mirror-relay-truth.test.mjs: 4 jsdom behavioral tests
(promote unmirrored->shared, clear stale shared, per-row batch reconcile,
button tagging). Build + lint clean; mirror+my-videos suites 48/48.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
26d67d59b2
commit
eb4d071203
4 files changed
+235
-7
No files matched your search
@@ -336,6 +336,9 @@ export class MyVideosController {
|
||||
for (const video of rows) {
|
||||
this.listEl.appendChild(this.buildRow(video));
|
||||
}
|
||||
// After the synchronous render (which used the local flag), correct the mirror
|
||||
// labels from relay truth in the background. Fire-and-forget.
|
||||
this.refreshMirrorStates(rows);
|
||||
}
|
||||
|
||||
buildRow(video) {
|
||||
@@ -441,10 +444,63 @@ export class MyVideosController {
|
||||
btn.classList.add("opacity-50");
|
||||
return btn;
|
||||
}
|
||||
return this.buildActionButton(
|
||||
const btn = this.buildActionButton(
|
||||
enabled ? "Shared ✓" : "Share to apps",
|
||||
(event) => this.handleToggleMirror(video, event?.currentTarget || null),
|
||||
);
|
||||
// Tag with the root so the batched relay-truth refresh can update this button.
|
||||
const root = video?.videoRootId;
|
||||
if (root) {
|
||||
btn.dataset.mirrorRoot = String(root);
|
||||
}
|
||||
return btn;
|
||||
}
|
||||
|
||||
// Relay-truth detection (#34): the per-row mirror label initially uses the
|
||||
// device-local flag (synchronous), which can be stale/missing on a fresh device or
|
||||
// after a cache clear. After render, look up the ACTUAL published mirror events
|
||||
// (one batched query for the whole list), reconcile the local flag to match, and
|
||||
// correct any button labels. Fire-and-forget — never blocks the render.
|
||||
async refreshMirrorStates(videos) {
|
||||
if (!FEATURE_NIP71_MIRROR || !this.pubkey) {
|
||||
return;
|
||||
}
|
||||
if (typeof nip71MirrorService.findMirrors !== "function") {
|
||||
return;
|
||||
}
|
||||
let states;
|
||||
try {
|
||||
states = await nip71MirrorService.findMirrors(videos);
|
||||
} catch (error) {
|
||||
// Detection is best-effort; the local flag remains as a hint.
|
||||
return;
|
||||
}
|
||||
if (!(states instanceof Map) || !(this.listEl instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
for (const video of Array.isArray(videos) ? videos : []) {
|
||||
const root = video?.videoRootId ? String(video.videoRootId) : "";
|
||||
if (!root) {
|
||||
continue;
|
||||
}
|
||||
const mirrored = states.get(root)?.mirrored === true;
|
||||
// Reconcile the local flag so it's a correct cache going forward.
|
||||
try {
|
||||
setMirrorEnabled(this.pubkey, root, mirrored);
|
||||
} catch (error) {
|
||||
// ignore storage errors
|
||||
}
|
||||
const safeRoot =
|
||||
typeof CSS !== "undefined" && typeof CSS.escape === "function"
|
||||
? CSS.escape(root)
|
||||
: root.replace(/["\\]/g, "\\$&");
|
||||
const btn = this.listEl.querySelector(
|
||||
`[data-mirror-root="${safeRoot}"]`,
|
||||
);
|
||||
if (btn instanceof HTMLElement && !btn.disabled) {
|
||||
btn.textContent = mirrored ? "Shared ✓" : "Share to apps";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleToggleMirror(video, btn) {
|
||||
|
||||
Reference in new issue
Block a user