mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
fix(liveness): eager-probe hide-until-verified cards (fixes vanished foreign feed)
Setting CARD_LIVENESS_POLICY=hide-foreign made all foreign/NIP-71 content disappear: a hide-until-verified card starts display:none, which has no layout box, so the IntersectionObserver never fires for it — its liveness probe never runs, it never gets a healthy verdict, and it stays hidden forever (deadlock). Fix: cardNeedsEagerLivenessProbe() identifies cards governed by a hide-until-verified policy (foreign under hide-foreign; all non-owner under hide-all). Both health observers now kick the probe for those cards from onCardRegister — which fires for every card regardless of visibility — instead of waiting for an intersection that a hidden card can never produce. The URL probe is deferred one microtask so attachUrlHealthBadges has wired state.onCheck after observe(). Probes stay concurrency-capped + priority-ordered; show-pending is unchanged (no eager probing). Tests: cardNeedsEagerLivenessProbe predicate coverage in tests/video-card-source-visibility.test.mjs. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
a3b66f8841
commit
129d877d38
5 files changed
+131
-39
No files matched your search
+49
-34
@@ -2,6 +2,7 @@ import { createCardObserver } from "./dom/cardObserver.js";
|
||||
import { safeDecodeURIComponent } from "./utils/safeDecode.js";
|
||||
import { userLogger } from "./utils/logger.js";
|
||||
import { getLivenessProbePrefetchMargin } from "./constants.js";
|
||||
import { cardNeedsEagerLivenessProbe } from "./utils/cardSourceVisibility.js";
|
||||
|
||||
// Prefetch margin lets cards just below the fold start probing before they scroll
|
||||
// into view, so they're verified by the time the user reaches them.
|
||||
@@ -20,44 +21,58 @@ const urlCardObserver = createCardObserver({
|
||||
return isIntersecting && ratio > 0;
|
||||
},
|
||||
createState: () => ({ onCheck: null }),
|
||||
onCardVisible: ({ card, state }) => {
|
||||
if (!state || typeof state.onCheck !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
const badgeEl = card.querySelector("[data-url-health-state]");
|
||||
if (!(badgeEl instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const encodedUrl =
|
||||
card.dataset.urlHealthUrl || badgeEl.dataset.urlHealthUrl || "";
|
||||
const eventId =
|
||||
card.dataset.urlHealthEventId || badgeEl.dataset.urlHealthEventId || "";
|
||||
|
||||
if (!encodedUrl || !eventId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentState =
|
||||
card.dataset.urlHealthState || badgeEl.dataset.urlHealthState;
|
||||
if (currentState && currentState !== "checking") {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = safeDecodeURIComponent(encodedUrl);
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
state.onCheck({ card, badgeEl, url, eventId });
|
||||
} catch (err) {
|
||||
userLogger.warn("[urlHealthObserver] onCheck handler failed", err);
|
||||
// Hide-until-verified cards start `display:none`, so the IntersectionObserver
|
||||
// never fires for them — kick the URL probe eagerly on register so they can be
|
||||
// verified and revealed (the alternative is they stay hidden forever).
|
||||
onCardRegister: ({ card, state }) => {
|
||||
if (cardNeedsEagerLivenessProbe(card)) {
|
||||
// attachUrlHealthBadges sets state.onCheck right AFTER observe() returns, so
|
||||
// defer one microtask to let that wiring land before we probe.
|
||||
queueMicrotask(() => triggerUrlCheck(card, state));
|
||||
}
|
||||
},
|
||||
onCardVisible: ({ card, state }) => {
|
||||
triggerUrlCheck(card, state);
|
||||
},
|
||||
});
|
||||
|
||||
function triggerUrlCheck(card, state) {
|
||||
if (!state || typeof state.onCheck !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
const badgeEl = card.querySelector("[data-url-health-state]");
|
||||
if (!(badgeEl instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const encodedUrl =
|
||||
card.dataset.urlHealthUrl || badgeEl.dataset.urlHealthUrl || "";
|
||||
const eventId =
|
||||
card.dataset.urlHealthEventId || badgeEl.dataset.urlHealthEventId || "";
|
||||
|
||||
if (!encodedUrl || !eventId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentState =
|
||||
card.dataset.urlHealthState || badgeEl.dataset.urlHealthState;
|
||||
if (currentState && currentState !== "checking") {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = safeDecodeURIComponent(encodedUrl);
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
state.onCheck({ card, badgeEl, url, eventId });
|
||||
} catch (err) {
|
||||
userLogger.warn("[urlHealthObserver] onCheck handler failed", err);
|
||||
}
|
||||
}
|
||||
|
||||
export function attachUrlHealthBadges(container, onCheck) {
|
||||
if (!(container instanceof HTMLElement)) {
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user