PageSourceSearch

https://socialsell.ai/notification-sw.js

js socialsell.ai collected 2026-10-05 15:08:39 UTC 4,372 bytes, 71 lines download raw bytes

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.