/* Сервис-воркер Synapse: пуш-уведомления и клики по ним.
*
* Специально минималистичный: кэширование статики (offline shell) не делаем —
* приложение гоняется через API и протухший кэш хуже плохого оффлайна;
* fetch — pass-through (Chrome требует обработчик для installable, не перехватываем).
* Захват только push/notificationclick: payload {title, body, event_id, url} (docs/06).
*/
// При выпуске новой версии SW менять строку — активация вытеснит старую на activate.
const VERSION = "synapse-sw-v1";
self.addEventListener("install", () => self.skipWaiting());
self.addEventListener("activate", (event) => {
event.waitUntil(
(async () => {
// подчищаем старые версии воркера
for (const key of await caches.keys()) await caches.delete(key);
await self.clients.claim();
})(),
);
});
// Chrome требует fetch-обработчик для installable-страниц; ничего не кэшируем.
self.addEventListener("fetch", () => {});
self.addEventListener("push", (event) => {
let data = {};
try {
data = event.data ? event.data.json() : {};
} catch {
// текстовый payload не по нашему формату: показываем сырой текст
data = { body: event.data ? event.data.text() : "" };
}
const payload = {
title: data.title || "Synapse",
body: data.body || "",
url: data.url || "/my-events",
tag: data.event_id || undefined, // дедуп показов одного события
};
event.waitUntil(
self.registration.showNotification(payload.title, {
body: payload.body,
tag: payload.tag,
icon: "/assets/imgs/synapse-icon-192.png",
badge: "/assets/imgs/synapse-icon-192.png",
data: { url: payload.url },
}),
);
});
self.addEventListener("notificationclick", (event) => {
const url = (event.notification.data && event.notification.data.url) || "/my-events";
event.notification.close();
event.waitUntil(
(async () => {
const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
for (const client of clients) {
if ("focus" in client) {
await client.focus();
if (client.navigate) await client.navigate(url);
return;
}
}
await self.clients.openWindow(url);
})(),
);
});