// Tavolo service worker: installability and notification clicks, no // offline caching. // // The app is a thin client for a live game server: state arrives over // /api and /ws and is never safe to replay from a cache, and a new build // changes the hashed asset names, so stale cached files would break the // app until the cache expired. The worker therefore only performs the // passthrough fetch below — enough for the browser to treat the app as // installable — and leaves every request's caching to the network stack. // It also handles clicks on the move notifications raised by the game // page (see src/notify.rs). self.addEventListener("install", () => { // A new worker takes over on the next reload instead of waiting for // every tab of the old version to close. self.skipWaiting(); }); self.addEventListener("activate", (event) => { event.waitUntil(self.clients.claim()); }); // Clicking a move notification focuses the game: an existing window on // the game is focused as is, any other window of the app is navigated // there, and when the app is closed a new window is opened. self.addEventListener("notificationclick", (event) => { event.notification.close(); const url = (event.notification.data && event.notification.data.url) || "/"; event.waitUntil( (async () => { const target = new URL(url, self.location.origin); const windows = await self.clients.matchAll({ type: "window", includeUncontrolled: true, }); const onGame = windows.find((client) => { const current = new URL(client.url); return current.origin === target.origin && current.pathname === target.pathname; }); if (onGame) { return onGame.focus(); } const anyWindow = windows.find( (client) => new URL(client.url).origin === target.origin, ); if (anyWindow) { await anyWindow.focus(); try { return await anyWindow.navigate(url); } catch { return anyWindow; } } return self.clients.openWindow(url); })(), ); }); self.addEventListener("fetch", (event) => { const url = new URL(event.request.url); // Let the browser handle other origins and the live endpoints directly // (respondWith would only add a hop; /auth navigations must be allowed // to follow their redirects untouched). if ( url.origin !== self.location.origin || url.pathname.startsWith("/api/") || url.pathname.startsWith("/auth/") || url.pathname.startsWith("/ws") ) { return; } event.respondWith(fetch(event.request)); });