From 24a8d1da80e8c52f96da92d26b1f38e9f4b153e9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 02:47:05 +0000 Subject: [PATCH] optimize user block list loading performance Prioritizes fetching standard NIP-51 (Kind 10000) mute lists with a fail-fast timeout (6s) and applies them immediately upon receipt. Legacy block lists (Kind 30002, Kind 10000 d=user-blocks) are now fetched in the background without blocking the UI initialization. This resolves an issue where slow relays handling legacy queries could cause the entire block list loading process to hang for minutes. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/userBlocks.js | 63 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/js/userBlocks.js b/js/userBlocks.js index 41834dca..48daef32 100644 --- a/js/userBlocks.js +++ b/js/userBlocks.js @@ -1468,14 +1468,18 @@ class UserBlockListManager { : 0; // Concurrent incremental fetch for all variants - const [muteEvents, blockEvents, legacyEvents] = await Promise.all([ - nostrClient.fetchListIncrementally({ - kind: 10000, - pubkey: normalized, - relayUrls: relays, - since: fetchSince, - timeoutMs: 12000, - }), + // We prioritize standard NIP-51 lists (Kind 10000) to ensure the UI loads quickly. + // Legacy variants are fetched in parallel but processed separately to avoid blocking. + + const standardFetchPromise = nostrClient.fetchListIncrementally({ + kind: 10000, + pubkey: normalized, + relayUrls: relays, + since: fetchSince, + timeoutMs: 6000, + }); + + const legacyFetchPromise = Promise.all([ nostrClient.fetchListIncrementally({ kind: 10000, pubkey: normalized, @@ -1494,18 +1498,41 @@ class UserBlockListManager { }), ]); - const combined = [...muteEvents, ...blockEvents, ...legacyEvents]; - - // If combined is empty, check if we have loaded state - if (!combined.length) { - if (!this.loaded) { - // Assuming empty if we have no prior state - await applyEvents([], { source: "incremental" }); - } - } else { - await applyEvents(combined, { source: "incremental" }); + // FAST PATH: Process Standard List immediately + let standardEvents = []; + try { + standardEvents = await standardFetchPromise; + if (Array.isArray(standardEvents) && standardEvents.length > 0) { + await applyEvents(standardEvents, { source: "fast-standard" }); + this.loaded = true; + } + } catch (standardError) { + devLogger.warn("[UserBlockList] Standard fetch failed:", standardError); } + // SLOW PATH: Process Legacy Lists (and Standard if it was empty/failed) + // We do NOT await this for the main return, allowing the UI to unblock immediately + // if the standard list was found. + legacyFetchPromise + .then(async ([blockEvents, legacyEvents]) => { + const combined = [...standardEvents, ...blockEvents, ...legacyEvents]; + + if (!combined.length) { + // Only apply empty if we haven't already marked loaded (meaning standard failed or was empty) + // or if we want to ensure we settle state. + // If standard was found, we don't need to do anything for empty legacy. + if (!this.loaded) { + await applyEvents([], { source: "empty-result" }); + } + } else { + // Re-apply merged results to catch any legacy blocks + await applyEvents(combined, { source: "incremental-merge" }); + } + }) + .catch((legacyError) => { + devLogger.warn("[UserBlockList] Legacy fetch failed:", legacyError); + }); + } catch (error) { userLogger.error("[UserBlockList] loadBlocks failed (keeping local state):", error);