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]>
Introduced `waitForReady` in `AccessControl` to block until moderation lists are fetched, and updated `playVideoByEventId` and `trustBootstrap` to use it, preventing false "not whitelisted" errors in private tabs.
- Fixed `ModerationService.reconcileTrustedMuteSubscriptions` to always force re-subscription to trusted seeds, bypassing previous state comparisons.
- Enhanced `ModerationService` to filter trusted seeds against the global admin blacklist.
- Added blacklist change event support to `AccessControl` and `TrustBootstrap` to dynamically update seeds when the blacklist changes.
- Added detailed logging to `ModerationService`, `TrustBootstrap`, and `AccessControl` to trace trust seed initialization and application.
- Fixed `ModerationService.clearTrustedMuteTracking` to properly force re-subscription to trusted seeds by passing an empty previous state.
- Enhanced `ModerationService` to filter trusted seeds against the global admin blacklist.
- Added blacklist change event support to `AccessControl` and `TrustBootstrap` to dynamically update seeds when the blacklist changes.
- Moves `bootstrapTrustedSeeds` logic from `js/bootstrap.js` to a new service `js/services/trustBootstrap.js`.
- Updates `js/app.js` to explicitly await `nostrClient.init()` before calling `bootstrapTrustedSeeds()`, resolving race conditions where admin lists were fetched before the relay pool was ready.
- Ensures `waitForRelaysReady` checks for `nostrClient.pool` existence.