Web frontend
This commit is contained in:
79
web/public/sw.js
Normal file
79
web/public/sw.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// A deliberately small service worker: it exists so iOS treats this as an installable
|
||||
// app and so the shell survives a flaky wifi moment. It does NOT try to make the player
|
||||
// work offline - the audio, the library and the state all live on the mouse, and a
|
||||
// cached answer for any of those would be a lie.
|
||||
|
||||
const SHELL = "musikdelphin-shell-v1";
|
||||
const COVERS = "musikdelphin-covers-v1";
|
||||
|
||||
self.addEventListener("install", (event) => {
|
||||
event.waitUntil(
|
||||
caches
|
||||
.open(SHELL)
|
||||
.then((cache) =>
|
||||
cache.addAll([
|
||||
"/",
|
||||
"/manifest.webmanifest",
|
||||
"/dolphin-mascot.png",
|
||||
"/fonts/nunito.css",
|
||||
"/fonts/nunito-latin.woff2",
|
||||
"/fonts/nunito-latin-ext.woff2",
|
||||
]),
|
||||
)
|
||||
.then(() => self.skipWaiting()),
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(
|
||||
caches
|
||||
.keys()
|
||||
.then((names) =>
|
||||
Promise.all(names.filter((n) => n !== SHELL && n !== COVERS).map((n) => caches.delete(n))),
|
||||
)
|
||||
.then(() => self.clients.claim()),
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener("fetch", (event) => {
|
||||
const request = event.request;
|
||||
if (request.method !== "GET") return;
|
||||
|
||||
const url = new URL(request.url);
|
||||
if (url.origin !== self.location.origin) return;
|
||||
|
||||
// Cover art is immutable per album id and is the only heavy thing here.
|
||||
if (url.pathname.startsWith("/api/albums/")) {
|
||||
event.respondWith(
|
||||
caches.open(COVERS).then(async (cache) => {
|
||||
const hit = await cache.match(request);
|
||||
if (hit) return hit;
|
||||
const response = await fetch(request);
|
||||
if (response.ok) cache.put(request, response.clone());
|
||||
return response;
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Everything else under /api is live state. Never cache it, never serve it stale.
|
||||
if (url.pathname.startsWith("/api/")) return;
|
||||
|
||||
// The shell: network first so a rebuilt frontend is picked up on the next load,
|
||||
// falling back to the cache when the mouse is off or out of range.
|
||||
event.respondWith(
|
||||
fetch(request)
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
const copy = response.clone();
|
||||
caches.open(SHELL).then((cache) => cache.put(request, copy));
|
||||
}
|
||||
return response;
|
||||
})
|
||||
.catch(async () => {
|
||||
const hit = await caches.match(request);
|
||||
// A client-side app: any in-scope navigation resolves to the one document.
|
||||
return hit ?? (await caches.match("/")) ?? Response.error();
|
||||
}),
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user