Files
TX/artifacts/tx-os/public/sw.js
T

83 lines
2.4 KiB
JavaScript
Raw Normal View History

/* Tx OS service worker — Web Push only.
*
* Scope: served from the SPA's base path (e.g. `/sw.js`). The SPA
* registers it with `scope: BASE_URL` so the SW controls the full
* Tx OS surface but doesn't intercept anything outside the artifact.
*
* We deliberately do NOT cache app assets here — Tx OS is self-hosted
* on a single machine, so the network is fast and reliable. Adding
* cache layers would risk shipping stale React bundles after an
* upgrade.
*
* Events:
* - install / activate: claim clients immediately so the first push
* after registration lands without a reload.
* - push: render a system notification (lock-screen + banner on iPad
* PWA, system tray on macOS, etc.).
* - notificationclick: focus an existing Tx OS tab (or open one) and
* navigate to the payload's URL.
*/
self.addEventListener("install", () => {
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(self.clients.claim());
});
self.addEventListener("push", (event) => {
/** @type {{title?: string, body?: string, tag?: string, url?: string, type?: string}} */
let payload = {};
try {
payload = event.data ? event.data.json() : {};
} catch {
payload = { title: event.data ? event.data.text() : "Tx OS" };
}
const title = payload.title || "Tx OS";
const options = {
body: payload.body || "",
tag: payload.tag || payload.type || "tx-os",
renotify: true,
icon: "/icons/icon-192.png",
badge: "/icons/icon-192.png",
data: {
url: payload.url || "/",
type: payload.type || null,
},
};
event.waitUntil(self.registration.showNotification(title, options));
});
self.addEventListener("notificationclick", (event) => {
event.notification.close();
const targetPath = (event.notification.data && event.notification.data.url) || "/";
event.waitUntil(
(async () => {
const allClients = await self.clients.matchAll({
type: "window",
includeUncontrolled: true,
});
// Prefer an already-open Tx OS tab; navigate it to targetPath
// and focus.
for (const client of allClients) {
try {
await client.focus();
if ("navigate" in client) {
await client.navigate(targetPath);
}
return;
} catch {
/* try next */
}
}
if (self.clients.openWindow) {
await self.clients.openWindow(targetPath);
}
})(),
);
});