mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
Cold refresh stalled on "Fetching moderation filters…" and then (logged in) "Fetching subscriptions…" before the grid could render. Boot was SERIAL behind relay fetches even though every stage already had a local cache: 1. _initAccessControl awaited the full admin-list relay refresh (up to 15s) before auto-login — but accessControl hydrates whitelist/blacklist from localStorage at construction, and isHydrated() existed for exactly this case (unused). Now: when cached lists are present (every visit after the first), boot proceeds immediately and the refresh continues in the background — the existing whitelist/blacklist change listeners re-filter the grids when it lands. First-ever visits still wait (a whitelist-mode instance must never render unfiltered). The admin-pane refresh no longer blocks boot at all. 2. bootstrapTrustedSeeds awaited the same relay refresh (up to 3.5s more). waitForAccessControl now fast-paths on isHydrated(): cached editors seed trust immediately, and its onEditors/Whitelist/BlacklistChange listeners already re-apply seeds when the real refresh lands. 3. The post-login feed gate held the grid for the FULL list sync + NIP-07 decryption (up to 8s). New js/app/feedListCacheGate.js: a probe that checks all three list services (blocks/subscriptions/hashtags) hold a valid cache for the active pubkey, raced into the gate — warm boots open as soon as the caches hydrate (first ticks) and the background sync re-filters on arrival. Cold boots (no caches) behave exactly as before. Net effect: warm cold-refresh renders from IndexedDB-cached events with cached filters/lists near-instantly; relay truth streams in behind it. No moderation semantics changed — every stage that lacks a cache still waits. Tests: tests/feed-list-cache-gate.test.mjs (5 — all-three-caches probe incl. stale-pubkey/missing-service, late-hydration early release, cold-boot timeout, throwing probe). Access-control + switch suites green. Lint + build green (coordinator compacted to stay under its size cap). Co-Authored-By: Claude Opus 4.8 <[email protected]>
80 lines
2.6 KiB
JavaScript
80 lines
2.6 KiB
JavaScript
// Cache-aware early release for the post-login feed gate. The login flow holds
|
|
// the video grid until blocks/subscriptions/hashtag lists settle (so the feed
|
|
// never flashes unfiltered content), time-boxed at several seconds. But on every
|
|
// warm boot those services hydrate valid per-pubkey CACHES almost immediately —
|
|
// filtering with cached lists is accurate, and the relay sync keeps running in
|
|
// the background (its change events re-filter when fresh data lands). This probe
|
|
// lets the gate open as soon as all three caches are usable instead of waiting
|
|
// for full relay sync + NIP-07 decryption ("Fetching subscriptions…" stall).
|
|
|
|
// Builds a () => boolean probe: true when all three list services hold a valid
|
|
// cache for the active pubkey. Mirrors the hasValid*Cache checks used by the
|
|
// login list-sync bookkeeping.
|
|
export function createListCacheProbe({
|
|
userBlocks,
|
|
subscriptions,
|
|
hashtagPreferences,
|
|
normalizeHexPubkey,
|
|
activePubkey,
|
|
} = {}) {
|
|
const normalize =
|
|
typeof normalizeHexPubkey === "function"
|
|
? normalizeHexPubkey
|
|
: (value) => (typeof value === "string" ? value.trim().toLowerCase() : "");
|
|
const target = normalize(activePubkey);
|
|
|
|
return () => {
|
|
if (!target) {
|
|
return false;
|
|
}
|
|
const blocksReady =
|
|
Boolean(userBlocks) &&
|
|
userBlocks.loaded === true &&
|
|
normalize(userBlocks.activePubkey) === target;
|
|
const subscriptionsReady =
|
|
Boolean(subscriptions) &&
|
|
subscriptions.loaded === true &&
|
|
normalize(subscriptions.currentUserPubkey) === target;
|
|
const hashtagsReady =
|
|
Boolean(hashtagPreferences) &&
|
|
hashtagPreferences.loaded === true &&
|
|
normalize(hashtagPreferences.activePubkey) === target;
|
|
return blocksReady && subscriptionsReady && hashtagsReady;
|
|
};
|
|
}
|
|
|
|
// Resolves "cache" as soon as the probe passes, or null at the timeout. Polls on
|
|
// a short interval — the caches hydrate within the first ticks on warm boots.
|
|
export function waitForListCaches(
|
|
probe,
|
|
{ timeoutMs = 8000, intervalMs = 150 } = {},
|
|
) {
|
|
return new Promise((resolve) => {
|
|
if (typeof probe !== "function") {
|
|
resolve(null);
|
|
return;
|
|
}
|
|
const startedAt = Date.now();
|
|
const check = () => {
|
|
let ready = false;
|
|
try {
|
|
ready = probe() === true;
|
|
} catch (error) {
|
|
ready = false;
|
|
}
|
|
if (ready) {
|
|
resolve("cache");
|
|
return;
|
|
}
|
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
resolve(null);
|
|
return;
|
|
}
|
|
setTimeout(check, intervalMs);
|
|
};
|
|
check();
|
|
});
|
|
}
|
|
|
|
export default waitForListCaches;
|