From 791296266a52a3fdcec6b56b0a184b6a26cb2f30 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 6 Jul 2026 15:48:17 -0400 Subject: [PATCH] playlists(#37): playlist view resolves from cache first (no needless relay fetch) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The view always re-fetched the creator's videos from relays, so arriving from the channel (where they're already loaded) or re-visiting a playlist lagged on a round-trip. Now it resolves from the active cache first and only fetches when some playlist coordinates are still missing (a cold deep-link). Channel→playlist navigation is instant; deep-links still fetch just what they need. Co-Authored-By: Claude Opus 4.8 --- js/playlistView.js | 59 +++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/js/playlistView.js b/js/playlistView.js index 4b39cbd3..b763fba7 100644 --- a/js/playlistView.js +++ b/js/playlistView.js @@ -54,29 +54,13 @@ function resolveOrderedVideos(playlist, videoPool) { return ordered; } -// The videos to resolve against: the referenced creators' videos (fetched from -// relays so a DIRECT load / cold cache still works) plus whatever's already -// active. Without the fetch, a deep-linked playlist resolves nothing and the -// shared grid shows the default feed instead. +// The videos to resolve against. Resolve from the ALREADY-active cache first — +// when you arrive from the creator's channel (or re-visit), the videos are +// already loaded, so this is instant with no relay round-trip. Only when some +// playlist coordinates are still missing (a cold deep-link) do we fetch the +// referenced creators' videos. Without any fetch, a cold deep-link resolves +// nothing and the shared grid shows the default feed instead. async function gatherVideoPool(playlist, app) { - const authors = [ - ...new Set( - playlist.items - .filter((item) => item.type === "a") - .map((item) => item.value.split(":")[1]) - .filter(Boolean), - ), - ]; - - let fetched = []; - if (authors.length && typeof app?.nostrService?.fetchVideosByAuthors === "function") { - try { - fetched = (await app.nostrService.fetchVideosByAuthors(authors)) || []; - } catch (error) { - devLogger.warn("[playlistView] Failed to fetch playlist videos:", error); - } - } - let active = []; try { active = nostrClient.getActiveVideos() || []; @@ -84,6 +68,37 @@ async function gatherVideoPool(playlist, app) { active = []; } + const covered = new Set(); + for (const video of active) { + const coordinate = buildVideoAddressPointer(video); + if (coordinate) { + covered.add(coordinate); + } + } + + const neededCoords = playlist.items + .filter((item) => item.type === "a") + .map((item) => item.value); + const allCovered = neededCoords.every((coord) => covered.has(coord)); + if (allCovered) { + return active; + } + + const authors = [ + ...new Set(neededCoords.map((coord) => coord.split(":")[1]).filter(Boolean)), + ]; + let fetched = []; + if ( + authors.length && + typeof app?.nostrService?.fetchVideosByAuthors === "function" + ) { + try { + fetched = (await app.nostrService.fetchVideosByAuthors(authors)) || []; + } catch (error) { + devLogger.warn("[playlistView] Failed to fetch playlist videos:", error); + } + } + return [...fetched, ...active]; }