// Shows the server's web push notifications and opens the conversation they belong to. // No fetch handler: the app always loads from the network. self.addEventListener("install", () => { self.skipWaiting(); }); self.addEventListener("activate", (event) => { event.waitUntil(self.clients.claim()); }); function appPath(value) { return typeof value === "string" && value.startsWith("/") && !value.startsWith("//") ? value : "/app"; } self.addEventListener("push", (event) => { event.waitUntil( (async () => { let data = {}; try { data = event.data ? event.data.json() : {}; } catch { data = {}; } const windows = await self.clients.matchAll({ type: "window", includeUncontrolled: true }); // The app in front of the user already shows what happened. if (windows.some((client) => client.focused && client.visibilityState === "visible")) return; await self.registration.showNotification( typeof data.title === "string" && data.title ? data.title : "evOo", { body: typeof data.body === "string" ? data.body : "", // Same tag as the in-app notification, so one replaces the other. tag: typeof data.tag === "string" && data.tag ? data.tag : undefined, icon: "/icon-192.png", badge: "/icon-192.png", data: { url: appPath(data.url) }, }, ); })(), ); }); self.addEventListener("notificationclick", (event) => { event.notification.close(); event.waitUntil( (async () => { const target = new URL(appPath(event.notification.data?.url), self.location.origin).href; const windows = await self.clients.matchAll({ type: "window", includeUncontrolled: true }); const open = windows.find((client) => client.url === target) ?? windows.find((client) => new URL(client.url).origin === self.location.origin); if (!open) { await self.clients.openWindow(target); return; } const focused = await open.focus(); if (focused.url !== target) await focused.navigate(target).catch(() => undefined); })(), ); });