Files

53 lines
2.8 KiB
JavaScript

// Service worker: push-уведомления + офлайн-оболочка мобильной версии (/m).
const CACHE = "shell-m-v3";
const SHELL = ["/m", "/static/m/m.js", "/static/m/m.css", "/static/m/manifest.webmanifest",
"/static/m/icon-192.png", "/static/m/icon-512.png", "/static/m/apple-touch-icon.png", "/static/assistant.js"];
self.addEventListener("install", e => {
e.waitUntil(caches.open(CACHE).then(c => Promise.allSettled(SHELL.map(u => c.add(u)))).then(() => self.skipWaiting()));
});
self.addEventListener("activate", e => {
e.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))).then(() => self.clients.claim()));
});
const isShell = url => url.origin === self.location.origin && (url.pathname === "/m" || url.pathname === "/m/" || url.pathname.startsWith("/static/m/") || url.pathname === "/static/assistant.js");
// Сеть в приоритете (чтобы обновления приходили сразу), при отсутствии связи — кэш. API не перехватываем.
self.addEventListener("fetch", e => {
const req = e.request, url = new URL(req.url);
if (req.method !== "GET" || !isShell(url)) return;
e.respondWith((async () => {
const cache = await caches.open(CACHE);
try {
const net = await Promise.race([fetch(req), new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), 4000))]);
if (net && net.ok) cache.put(url.pathname === "/m/" ? "/m" : req, net.clone());
return net;
} catch (err) {
const hit = await cache.match(url.pathname === "/m/" ? "/m" : req, { ignoreSearch: true });
if (hit) return hit;
throw err;
}
})());
});
self.addEventListener("push", e => {
let d = {};
try { d = e.data.json(); } catch (_) { d = { title: "Чек-листы", body: e.data ? e.data.text() : "" }; }
e.waitUntil(self.registration.showNotification(d.title || "Чек-листы", {
body: d.body || "", data: { url: d.url || "/" }, tag: d.url || undefined, icon: "/static/m/icon-192.png", badge: "/static/m/icon-192.png",
}));
});
self.addEventListener("notificationclick", e => {
e.notification.close();
let target = (e.notification.data && e.notification.data.url) || "/";
// на телефоне (по User-Agent) открываем мобильную версию: /#/order/5 -> /m#/o/5
const m = target.match(/#\/order\/(\d+)/);
if (m && /Android|iPhone|iPad|iPod/i.test(self.navigator.userAgent)) target = "/m#/o/" + m[1];
const url = new URL(target, self.location.origin).href;
e.waitUntil(self.clients.matchAll({ type: "window", includeUncontrolled: true }).then(list => {
for (const c of list) if ("focus" in c) { c.navigate(url); return c.focus(); }
return self.clients.openWindow(url);
}));
});