Offline access: save through the service worker, not the page's fetch

Pre-loading relied on the page being controlled by the service worker, so
its requests would pass through it and be saved. A page loaded with a hard
reload (Ctrl+Shift+R) never is: pre-loading then waited, gave up silently,
and nothing new was saved — the app still could not start offline, and
Settings kept showing the old sync time.

The page now asks the worker by message to fetch and save each URL, which
works however the page was loaded. Sign-out reaches the worker through its
registration for the same reason. Thread URLs come from shared helpers in
api.ts, so pre-loading saves exactly what the app asks for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 19:27:36 +02:00
co-authored by Claude Opus 5.5
parent a60cd1fc33
commit 01bfbb37d4
4 changed files with 126 additions and 65 deletions
+25
View File
@@ -377,8 +377,33 @@ self.addEventListener("fetch", (event) => {
if (STATIC_RE.test(url.pathname)) event.respondWith(staticFile(req));
});
/**
* Pre-loading from the page (lib/offlinePrefetch.ts): fetch one URL and save it,
* answering on the message port with { status, body } — status 0 when the
* server could not be reached. By message rather than by the page's own fetch,
* so it works for a page this worker does not control (a hard reload, or the
* page that just turned offline access on).
*/
async function fetchForPage(url, port) {
const req = new Request(new URL(url, self.location.origin).href, { credentials: "same-origin" });
const cls = classify(new URL(req.url));
try {
const res = await network(req, API_TIMEOUT);
offline = false;
if (res.status === 401) await purge();
else if (cls && res.status === 200) await save(req, res.clone(), cls);
port.postMessage({ status: res.status, body: await res.text() });
} catch {
offline = true;
port.postMessage({ status: 0 });
}
}
self.addEventListener("message", (event) => {
if (event.data?.type === "purge") event.waitUntil(purge());
if (event.data?.type === "fetch" && event.ports[0]) {
event.waitUntil(fetchForPage(event.data.url, event.ports[0]));
}
});
self.addEventListener("install", (event) => {