/** * The service worker, which exists only so a push can arrive when no tab is * open. There is deliberately no `fetch` handler and nothing is cached: the * page this app is served from carries a credential and is sent `no-store`, * so caching it would hand the next visitor somebody else's session. * * `registration.scope` is the app's root in both places it runs — `/` on an * instance of its own, `/i/{id}/` through the portal — which is why the * push payload does not carry a URL. */ self.addEventListener("push", (event) => { const alert = event.data ? event.data.json() : {} const scope = new URL(self.registration.scope) event.waitUntil( self.registration.showNotification(alert.title || "Fluksio", { body: alert.body || "", // Under the portal the icons are the shared bundle's, not this // instance's path. icon: scope.pathname.startsWith("/i/") ? "/app-shell/icon-192.png" : `${scope.pathname}icon-192.png`, // The same fault repeating replaces its notification rather than // stacking another one up. tag: alert.flow || alert.title || "fluksio", timestamp: Date.now(), }), ) }) self.addEventListener("notificationclick", (event) => { event.notification.close() const scope = self.registration.scope event.waitUntil( self.clients .matchAll({ type: "window", includeUncontrolled: true }) .then((clients) => { // A tab on this instance is already open: raise it rather than // opening a second one. const open = clients.find((client) => client.url.startsWith(scope)) if (open) return open.focus() return self.clients.openWindow(scope) }), ) })