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:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-06-25 09:33:41 -04:00
1 parent a3b66f8841
commit 129d877d38
5 files changed
+131 -39

No files matched your search

+22
View File
@@ -1,5 +1,27 @@
import { getCardLivenessPolicy } from "../constants.js";
/**
* Whether a card is governed by a hide-until-verified policy (so it starts hidden
* and must be PROBED to ever appear). Such cards can't rely on the viewport
* IntersectionObserver — a `display:none` card never intersects, so the probe
* must be kicked eagerly on register. Owner's own cards are always visible and
* never gated.
*/
export function cardNeedsEagerLivenessProbe(cardLike) {
const card = resolveCardElement(cardLike);
if (!card || card.dataset.ownerIsViewer === "true") {
return false;
}
const policy = getCardLivenessPolicy();
if (policy === "hide-all") {
return true;
}
if (policy === "hide-foreign") {
return card.dataset.foreign === "true";
}
return false;
}
function resolveCardElement(cardLike) {
if (!cardLike) {
return null;