diff --git a/js/services/nip71IngestService.js b/js/services/nip71IngestService.js index f28e3cf7..16cd4958 100644 --- a/js/services/nip71IngestService.js +++ b/js/services/nip71IngestService.js @@ -26,7 +26,7 @@ const FLUSH_DEBOUNCE_MS = 200; // Coalesce ingest-driven feed refreshes so a burst of incoming events can't // trigger a re-render/re-subscription storm. The injected videos are already in // the active store; this only controls how often we *signal* the feed. -const REFRESH_THROTTLE_MS = 8000; +const REFRESH_THROTTLE_MS = 2500; const DEFAULT_LIMIT = 200; const SUBSCRIPTION_KEY = "nip71-ingest"; // The admin whitelist hydrates asynchronously (remote fetch, then a second hex @@ -157,10 +157,22 @@ export function createNip71IngestService({ } } - let injected = 0; + const injectedVideos = []; for (const video of newestByRoot.values()) { if (injectVideo(video)) { - injected += 1; + injectedVideos.push(video); + } + } + const injected = injectedVideos.length; + + // Also cache into nostrService.videosMap so ingested videos persist and show + // up in the author-scoped feeds + the optimistic/persisted render paths — not + // just the live activeMap engine run. Keeps NIP-71 present across navigation. + if (injected > 0 && typeof nostrService?.cacheVideos === "function") { + try { + nostrService.cacheVideos(injectedVideos); + } catch (error) { + logger?.warn?.("[nip71Ingest] cacheVideos failed", error); } } diff --git a/tests/nip71-ingest-service.test.mjs b/tests/nip71-ingest-service.test.mjs index 41b7918b..5db3c472 100644 --- a/tests/nip71-ingest-service.test.mjs +++ b/tests/nip71-ingest-service.test.mjs @@ -44,6 +44,7 @@ function makeHarness({ whitelistOn = false, whitelist = [] } = {}) { getSubscriptionManager: () => manager, }; const feedListeners = new Set(); + const cached = []; const nostrService = { emit(name, detail) { emitted.push({ name, detail }); @@ -55,6 +56,7 @@ function makeHarness({ whitelistOn = false, whitelist = [] } = {}) { return () => feedListeners.delete(fn); }, getFilteredActiveVideos: () => Array.from(activeMap.values()), + cacheVideos: (videos) => cached.push(...videos), }; const accessControl = { whitelistMode: () => whitelistOn, @@ -71,6 +73,7 @@ function makeHarness({ whitelistOn = false, whitelist = [] } = {}) { activeMap, allEvents, emitted, + cached, get capturedFilters() { return capturedFilters; }, @@ -136,6 +139,16 @@ test("ingests a foreign NIP-71 video into the active store and signals a refresh assert.equal(refresh.detail.reason, "nip71-ingest"); }); +test("caches injected videos into nostrService (persisted/author-feed paths)", () => { + const h = makeHarness(); + const svc = makeService(h); + svc.start(); + h.fireEvent(foreignEvent({ id: "e1", title: "Hello" })); + svc.flush(); + assert.equal(h.cached.length, 1, "injected video forwarded to cacheVideos"); + assert.equal(h.cached[0].id, "e1"); +}); + test("never clobbers a native bitvid video sharing the same root", () => { const h = makeHarness(); const native = { id: "native", videoRootId: "shared", source: "nostr", created_at: 500, title: "Native" };