diff options
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/web/app/reader/_components/entry-list.tsx | 15 | ||||
| -rw-r--r-- | apps/web/app/sw.ts | 22 | ||||
| -rw-r--r-- | apps/web/lib/hooks/use-prefetch-entry-details.ts | 45 |
3 files changed, 75 insertions, 7 deletions
diff --git a/apps/web/app/reader/_components/entry-list.tsx b/apps/web/app/reader/_components/entry-list.tsx index acc0990..65e35f7 100644 --- a/apps/web/app/reader/_components/entry-list.tsx +++ b/apps/web/app/reader/_components/entry-list.tsx @@ -1,12 +1,13 @@ "use client" -import { useRef, useEffect } from "react" +import { useRef, useEffect, useMemo } from "react" import { useVirtualizer } from "@tanstack/react-virtual" import { useTimeline } from "@/lib/queries/use-timeline" import { useSavedEntries } from "@/lib/queries/use-saved-entries" import { useCustomFeedTimeline } from "@/lib/queries/use-custom-feed-timeline" import { useUserInterfaceStore } from "@/lib/stores/user-interface-store" import { EntryListItem } from "./entry-list-item" +import { usePrefetchEntryDetails } from "@/lib/hooks/use-prefetch-entry-details" interface EntryListProperties { feedFilter: "all" | "saved" @@ -77,11 +78,15 @@ export function EntryList({ const firstEntryIdentifier = allEntries[0]?.entryIdentifier const lastEntryIdentifier = allEntries[allEntries.length - 1]?.entryIdentifier + const prefetchIdentifiers = useMemo( + () => allEntries.map((entry) => entry.entryIdentifier), + [firstEntryIdentifier, lastEntryIdentifier, allEntries.length] + ) + usePrefetchEntryDetails(prefetchIdentifiers) + useEffect(() => { - setNavigableEntryIdentifiers( - allEntries.map((entry) => entry.entryIdentifier) - ) - }, [firstEntryIdentifier, lastEntryIdentifier, allEntries.length, setNavigableEntryIdentifiers]) + setNavigableEntryIdentifiers(prefetchIdentifiers) + }, [prefetchIdentifiers, setNavigableEntryIdentifiers]) function getEstimatedItemSize() { switch (entryListViewMode) { diff --git a/apps/web/app/sw.ts b/apps/web/app/sw.ts index 0469377..e18cc5a 100644 --- a/apps/web/app/sw.ts +++ b/apps/web/app/sw.ts @@ -1,7 +1,7 @@ /// <reference lib="webworker" /> import { defaultCache } from "@serwist/next/worker" import type { PrecacheEntry, SerwistGlobalConfig } from "serwist" -import { Serwist } from "serwist" +import { Serwist, NetworkFirst, ExpirationPlugin } from "serwist" declare global { interface WorkerGlobalScope extends SerwistGlobalConfig { @@ -27,7 +27,25 @@ const serwist = new Serwist({ skipWaiting: true, clientsClaim: true, navigationPreload: true, - runtimeCaching: sameOriginCache, + runtimeCaching: [ + { + matcher: ({ url, request }) => + url.hostname.endsWith(".supabase.co") && + url.pathname.startsWith("/rest/v1/") && + request.method === "GET", + handler: new NetworkFirst({ + cacheName: "supabase-rest-get", + networkTimeoutSeconds: 10, + plugins: [ + new ExpirationPlugin({ + maxEntries: 200, + maxAgeSeconds: 60 * 60 * 24, + }), + ], + }), + }, + ...sameOriginCache, + ], }) serwist.addEventListeners() diff --git a/apps/web/lib/hooks/use-prefetch-entry-details.ts b/apps/web/lib/hooks/use-prefetch-entry-details.ts new file mode 100644 index 0000000..8b88bb8 --- /dev/null +++ b/apps/web/lib/hooks/use-prefetch-entry-details.ts @@ -0,0 +1,45 @@ +"use client" + +import { useEffect } from "react" +import { useQueryClient } from "@tanstack/react-query" +import { createSupabaseBrowserClient } from "@/lib/supabase/client" +import { queryKeys } from "@/lib/queries/query-keys" + +const PREFETCH_BATCH_SIZE = 10 + +export function usePrefetchEntryDetails(entryIdentifiers: string[]) { + const queryClient = useQueryClient() + + useEffect(() => { + if (entryIdentifiers.length === 0) return + + const supabaseClient = createSupabaseBrowserClient() + const identifiersToPrefetch = entryIdentifiers.slice(0, PREFETCH_BATCH_SIZE) + + for (const entryIdentifier of identifiersToPrefetch) { + const existingData = queryClient.getQueryData( + queryKeys.entryDetail.single(entryIdentifier) + ) + + if (existingData) continue + + queryClient.prefetchQuery({ + queryKey: queryKeys.entryDetail.single(entryIdentifier), + queryFn: async () => { + const { data, error } = await supabaseClient + .from("entries") + .select( + "id, title, url, author, content_html, summary, published_at, enclosure_url, feeds!inner(title)" + ) + .eq("id", entryIdentifier) + .single() + + if (error) throw error + + return data + }, + staleTime: 5 * 60 * 1000, + }) + } + }, [entryIdentifiers, queryClient]) +} |