1/* global self, caches, Response */ 2const CACHE = "ss-notification-metadata-v1"; 3const SESSION = "/__notification_session"; 4const ID = /^[a-f\d]{24}$/i; 5async function session() { 6 const response = await (await caches.open(CACHE)).match(SESSION); 7 return response ? response.text() : null; 8} 9self.addEventListener("message", event => { 10 if (event.data?.type !== "notification-session" || (event.data.userId !== null && !ID.test(event.data.userId || ""))) return; 11 event.waitUntil((async () => { 12 const cache = await caches.open(CACHE); 13 const previous = await session(); 14 if (previous !== event.data.userId) { 15 for (const notice of await self.registration.getNotifications()) notice.close(); 16 for (const request of await cache.keys()) await cache.delete(request); 17 } 18 if (event.data.userId) await cache.put(SESSION, new Response(event.data.userId)); 19 event.ports[0]?.postMessage({ accepted: true }); 20 })()); 21}); 22self.addEventListener("push", event => { 23 event.waitUntil((async () => { 24 let payload; try { payload = event.data?.json(); } catch { return; } 25 const data = payload?.data; 26 if (!data || ![data.recipientId, data.organizationId, data.deviceId].every(value => ID.test(value || "")) || !/^(message|notification):[a-f\d]{24}$/i.test(data.eventId || "") || !Number.isSafeInteger(data.revision) || data.revision < 1 || !(+new Date(payload.expiresAt) > Date.now()) || await session() !== data.recipientId) return; 27 const occurrence = `ss:${data.recipientId}:${data.organizationId}:${data.eventId}:${data.revision}`; 28 // Mensagem agrupa por conversa, como uma thread do iOS: a mais recente 29 // substitui a anterior da mesma conversa e volta a alertar. 30 const thread = data.kind === "message" && ID.test(data.conversationId || ""); 31 const tag = thread ? `ss:${data.recipientId}:${data.organizationId}:conversation:${data.conversationId}` : occurrence; 32 const cache = await caches.open(CACHE), key = `/__notification_event/${encodeURIComponent(occurrence)}`; 33 if (await cache.match(key)) return; 34 await self.registration.showNotification(String(payload.title || "SocialSell").slice(0, 160), { 35 icon: "/logo_pwa_196x196.png", 36 body: String(payload.body || "").slice(0, 400), tag, renotify: thread && payload.silent !== true, silent: payload.silent === true, 37 data: { recipientId: data.recipientId, organizationId: data.organizationId, deviceId: data.deviceId, eventId: data.eventId, revision: data.revision }, 38 }); 39 await cache.put(key, new Response(String(Date.now()))); 40 for (const request of await cache.keys()) { 41 if (new URL(request.url).pathname === SESSION) continue; 42 if (Date.now() - Number(await (await cache.match(request)).text()) > 3600000) await cache.delete(request); 43 } 44 for (const client of await self.clients.matchAll({ type: "window", includeUncontrolled: true })) client.postMessage({ type: "notification-push-presented", data }); 45 })()); 46}); 47self.addEventListener("notificationclick", event => { 48 event.notification.close(); 49 event.waitUntil((async () => { 50 const data = event.notification.data; 51 if (await session() !== data?.recipientId) return; 52 const params = new URLSearchParams({ pushEvent: data.eventId, pushOrganization: data.organizationId, pushRecipient: data.recipientId, pushDevice: data.deviceId }); 53 const url = `${self.location.origin}/dashboard?${params}`; 54 const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true }); 55 const client = clients.find(item => new URL(item.url).origin === self.location.origin); 56 if (client) { client.postMessage({ type: "notification-push-open", data }); await client.focus(); } 57 else await self.clients.openWindow(url); 58 })()); 59}); 60 61self.addEventListener("pushsubscriptionchange", event => { 62 event.waitUntil((async () => { 63 // Permissão já concedida: renovar somente a inscrição anterior. A API 64 // recebe a rotação no próximo acesso autenticado, preservando opt-ins. 65 if (!await session() || !event.oldSubscription?.options?.applicationServerKey) return; 66 if (!event.newSubscription) await self.registration.pushManager.subscribe({ 67 userVisibleOnly: true, applicationServerKey: event.oldSubscription.options.applicationServerKey, 68 }); 69 for (const client of await self.clients.matchAll({ type: "window", includeUncontrolled: true })) client.postMessage({ type: "notification-subscription-changed" }); 70 })()); 71});
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.