const CACHE_VERSION = "v6"; const CACHE_PREFIX = "wtr-"; const SHELL_CACHE = `${CACHE_PREFIX}shell-${CACHE_VERSION}`; const RUNTIME_CACHE = `${CACHE_PREFIX}runtime-${CACHE_VERSION}`; const API_CACHE = `${CACHE_PREFIX}api-${CACHE_VERSION}`; const ACTIVE_CACHES = new Set([SHELL_CACHE, RUNTIME_CACHE, API_CACHE]); const SHELL = [ "/", "/warnings", "/hydro", "/settings", "/offline", "/manifest.json", "/icons/icon.svg", "/icons/maskable.svg", "/icons/icon-192.png", "/icons/icon-512.png", "/icons/maskable-512.png", ]; function isCacheableResponse(response) { return response && response.ok && response.type !== "opaque"; } async function putInCache(cacheName, request, response) { if (!isCacheableResponse(response)) return; const cache = await caches.open(cacheName); await cache.put(request, response.clone()); } function isWeatherApiRequest(url) { return ( url.pathname.startsWith("/api/imgw/") || url.pathname === "/api/imgw-current" || url.pathname === "/api/current-weather" || url.pathname === "/api/forecast" ); } function isStaticAssetRequest(request, url) { return ( url.origin === self.location.origin && (url.pathname.startsWith("/_next/static/") || url.pathname.startsWith("/icons/") || url.pathname === "/manifest.json" || ["font", "image", "script", "style", "worker"].includes(request.destination)) ); } async function cacheFirst(request) { const cached = await caches.match(request); if (cached) return cached; const response = await fetch(request); await putInCache(RUNTIME_CACHE, request, response); return response; } async function networkFirst(request, cacheName) { try { const response = await fetch(request); await putInCache(cacheName, request, response); return response; } catch { return ( (await caches.match(request)) || new Response(JSON.stringify({ error: "Offline." }), { status: 503, headers: { "Content-Type": "application/json" }, }) ); } } async function handleNavigation(request) { try { const response = await fetch(request); await putInCache(SHELL_CACHE, request, response); return response; } catch { return (await caches.match(request)) || (await caches.match("/")) || (await caches.match("/offline")); } } self.addEventListener("install", (event) => { event.waitUntil( caches .open(SHELL_CACHE) .then((cache) => Promise.allSettled(SHELL.map((url) => cache.add(new Request(url, { cache: "reload" }))))), ); self.skipWaiting(); }); self.addEventListener("activate", (event) => { event.waitUntil( caches .keys() .then((keys) => Promise.all( keys .filter((key) => key.startsWith(CACHE_PREFIX) && !ACTIVE_CACHES.has(key)) .map((key) => caches.delete(key)), ), ), ); self.clients.claim(); }); self.addEventListener("fetch", (event) => { if (event.request.method !== "GET") return; const url = new URL(event.request.url); if (url.origin !== self.location.origin) return; if (isWeatherApiRequest(url)) { event.respondWith(networkFirst(event.request, API_CACHE)); return; } if (event.request.mode === "navigate") { event.respondWith(handleNavigation(event.request)); return; } if (isStaticAssetRequest(event.request, url)) { event.respondWith(cacheFirst(event.request)); } }); self.addEventListener("push", (event) => { const fallbackPayload = { title: "IMGW", body: "Nowe ostrze\u017cenie meteorologiczne.", url: "/warnings", }; let payload = fallbackPayload; if (event.data) { const text = event.data.text(); try { payload = { ...fallbackPayload, ...JSON.parse(text) }; } catch { payload = { ...fallbackPayload, body: text }; } } event.waitUntil( self.registration.showNotification(payload.title, { body: payload.body, icon: "/icons/icon-192.png", badge: "/icons/icon-192.png", data: { url: payload.url || "/warnings" }, }), ); }); self.addEventListener("notificationclick", (event) => { event.notification.close(); const targetUrl = new URL(event.notification.data?.url || "/warnings", self.location.origin).href; event.waitUntil( self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clients) => { const existingClient = clients.find((client) => client.url === targetUrl); if (existingClient) return existingClient.focus(); return self.clients.openWindow(targetUrl); }), ); });