041ad0e44a
Notifications phase 2: a /push-sw.js service worker shows a system notification on push and, on click, focuses an open CRM tab (or opens one) and deep-links to the thread. usePushNotifications registers the SW, requests permission, subscribes with IIOS's VAPID key, and stores the subscription via the be-crm door. The topbar bell becomes a real enable/disable control (hidden when push is unsupported/demo). Dashboard listens for the SW's notif-click message + a ?thread= deep link. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
1.9 KiB
JavaScript
51 lines
1.9 KiB
JavaScript
/* Web Push service worker for CRM offline notifications.
|
|
*
|
|
* Receives push payloads from IIOS (WebPushDelivery), shows a system notification, and on click
|
|
* focuses an open CRM tab (or opens one) and posts the thread to deep-link to. The payload shape is
|
|
* IIOS's NotificationPayload: { title, body, data: { threadId, interactionId? } }.
|
|
*
|
|
* Served from /public at /push-sw.js → root scope ('/'), so it controls the whole app.
|
|
*/
|
|
|
|
self.addEventListener("push", (event) => {
|
|
let payload = {};
|
|
try {
|
|
payload = event.data ? event.data.json() : {};
|
|
} catch {
|
|
payload = { title: "New notification", body: event.data ? event.data.text() : "" };
|
|
}
|
|
const title = payload.title || "New message";
|
|
const threadId = payload.data && payload.data.threadId;
|
|
event.waitUntil(
|
|
self.registration.showNotification(title, {
|
|
body: payload.body || "",
|
|
// Coalesce repeated pings for the same thread into one notification.
|
|
tag: threadId ? `thread:${threadId}` : undefined,
|
|
renotify: Boolean(threadId),
|
|
data: payload.data || {},
|
|
icon: "/icons/i_bell.svg",
|
|
}),
|
|
);
|
|
});
|
|
|
|
self.addEventListener("notificationclick", (event) => {
|
|
event.notification.close();
|
|
const threadId = event.notification.data && event.notification.data.threadId;
|
|
event.waitUntil(
|
|
(async () => {
|
|
const all = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
|
|
// Prefer an already-open CRM tab: focus it and tell the app which thread to open.
|
|
for (const client of all) {
|
|
if ("focus" in client) {
|
|
await client.focus();
|
|
client.postMessage({ type: "notif-click", threadId: threadId || null });
|
|
return;
|
|
}
|
|
}
|
|
// No tab open — launch one deep-linked via the query string.
|
|
const url = threadId ? `/dashboard?thread=${encodeURIComponent(threadId)}` : "/dashboard";
|
|
if (self.clients.openWindow) await self.clients.openWindow(url);
|
|
})(),
|
|
);
|
|
});
|