From e6658eae3db43949c6d5af33eff18b28ba015c07 Mon Sep 17 00:00:00 2001 From: twotalesanimation <80506065+twotalesanimation@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:14:33 +0200 Subject: [PATCH] Fix /set offline cache: save markers and page assets on first visit The worker isn't controlling the page on the first visit, so warm-up fetches bypassed it and no images were cached. The page now writes markers, its own JS/CSS/fonts and /set/ straight into the cache and only then shows 'Works offline'. Co-Authored-By: Claude Opus 5.5 --- components/set/MarkerPicker.tsx | 39 ++++++++++++++++++++++++--------- public/set/sw.js | 1 + 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/components/set/MarkerPicker.tsx b/components/set/MarkerPicker.tsx index 50211c5..7b186a2 100644 --- a/components/set/MarkerPicker.tsx +++ b/components/set/MarkerPicker.tsx @@ -8,6 +8,8 @@ type Showing = { src: string; label: string; detail: string; screen?: MarkerScre const KIND_LABEL: Record = { phone: "Phones", tablet: "Tablets", laptop: "Laptops", monitor: "Monitors" }; const MODE_KEY = "ttvfx-set-mode"; +// Must match CACHE in public/set/sw.js +const OFFLINE_CACHE = "ttvfx-set-v1"; function detectScreen() { const dpr = window.devicePixelRatio || 1; @@ -139,18 +141,35 @@ export default function MarkerPicker({ screens, extras }: { screens: MarkerScree if (!showing && document.fullscreenElement) document.exitFullscreen?.().catch(() => {}); }, [showing]); - // Offline: register the service worker, then warm its cache with every marker + // Offline: register the service worker, then put every marker plus this page's own + // scripts/styles/fonts straight into its cache. Writing from the page (rather than + // relying on the worker intercepting fetches) also covers the very first visit, + // before the worker controls the page. useEffect(() => { - if (process.env.NODE_ENV !== "production" || !("serviceWorker" in navigator)) return; - navigator.serviceWorker - .register("/set/sw.js", { scope: "/set/" }) - .then(async () => { + if (process.env.NODE_ENV !== "production" || !("serviceWorker" in navigator) || !("caches" in window)) return; + let cancelled = false; + (async () => { + try { + await navigator.serviceWorker.register("/set/sw.js", { scope: "/set/" }); + await navigator.serviceWorker.ready; + if (cancelled) return; setOffline("saving"); - const urls = [...screens.flatMap((s) => [s.full, s.edge]), ...extras.map((e) => e.src)].filter(Boolean) as string[]; - await Promise.all(urls.map((u) => fetch(u).catch(() => null))); - setOffline("ready"); - }) - .catch(() => {}); + const markers = [...screens.flatMap((s) => [s.full, s.edge]), ...extras.map((e) => e.src)].filter(Boolean) as string[]; + const assets = performance + .getEntriesByType("resource") + .map((e) => new URL(e.name)) + .filter((u) => u.origin === location.origin && /^\/(_next\/|icon|apple-icon)/.test(u.pathname)) + .map((u) => u.pathname + u.search); + const cache = await caches.open(OFFLINE_CACHE); + await cache.addAll(["/set/", ...new Set([...markers, ...assets])]); + if (!cancelled) setOffline("ready"); + } catch { + if (!cancelled) setOffline("idle"); + } + })(); + return () => { + cancelled = true; + }; }, [screens, extras]); const showScreen = (s: MarkerScreen) => { diff --git a/public/set/sw.js b/public/set/sw.js index 64e8860..907a114 100644 --- a/public/set/sw.js +++ b/public/set/sw.js @@ -1,5 +1,6 @@ // Offline cache for /set (screen markers). Crews often have no signal on set: // once the page has loaded once, it and every marker image work offline. +// Must match OFFLINE_CACHE in components/set/MarkerPicker.tsx const CACHE = "ttvfx-set-v1"; self.addEventListener("install", (event) => {