1/** 2 * Push notification service worker for DuceCRM. 3 * Handles incoming push events and notification clicks. 4 * This is separate from the PWA service worker to avoid caching conflicts. 5 */ 6 7self.addEventListener("push", (event) => { 8 if (!event.data) return; 9 10 let payload; 11 try { 12 payload = event.data.json(); 13 } catch { 14 payload = { title: "DuceCRM", body: event.data.text() }; 15 } 16 17 const { title = "DuceCRM", body, icon, link, tag, module } = payload; 18 19 const MODULE_ICONS: Record<string, string> = { 20 inbox: "ð¬", 21 deals: "ð¤", 22 tasks: "ð", 23 support: "ðï¸", 24 intelligence: "ð", 25 billing: "ð°", 26 performance: "ð", 27 commissions: "ðµ", 28 system: "âï¸", 29 }; 30 31 const badgeIcon = MODULE_ICONS[module || "system"] || "ð"; 32 33 event.waitUntil( 34 self.registration.showNotification(title, { 35 body: body || "", 36 icon: icon || "/pwa-192x192.png", 37 badge: "/pwa-192x192.png", 38 tag: tag || `ducecrm-${Date.now()}`, 39 data: { link: link || "/" }, 40 requireInteraction: false, 41 silent: false, 42 }) 43 ); 44}); 45 46self.addEventListener("notificationclick", (event) => { 47 event.notification.close(); 48 const link = event.notification.data?.link || "/"; 49 50 event.waitUntil( 51 clients.matchAll({ type: "window", includeUncontrolled: true }).then((windowClients) => { 52 // Focus existing tab if available 53 for (const client of windowClients) { 54 if (client.url.includes(self.location.origin) && "focus" in client) { 55 client.focus(); 56 client.navigate(link); 57 return; 58 } 59 } 60 // Otherwise open new window 61 return clients.openWindow(link); 62 }) 63 ); 64});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.