import Dexie, { EntityTable } from "dexie" import urlB64ToUint8Array from "../b64ToUInt8" import pushPublicKey from "../pushPublicKey" interface SubscriptionRecord { id: number subscriptionId: number } export default function start(self: ServiceWorkerGlobalScope) { self.addEventListener("install", (event) => { event.waitUntil(self.skipWaiting()) }) self.addEventListener("activate", (event) => { event.waitUntil(self.clients.claim()) }) self.addEventListener("push", function (event: PushEvent) { const { title, body, badge, icon, destination } = event.data?.json() ?? { title: "It was missing yo", badge: "image/blah", icon: "image/blah", destination: "https://tackupnow.com", } event.waitUntil( self.registration.showNotification(title, { body, badge, icon, data: { destination }, }) ) }) self.addEventListener("notificationclick", function (event) { event.notification.close() const destination = event.notification.data.destination event.waitUntil( (async () => { const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true, }) const existingClient = clients.find( (client) => client.url === event.notification.data.destination ) if (existingClient === undefined) { await self.clients.openWindow(destination) } else { await existingClient.focus() } })() ) }) self.addEventListener("message", function (event) { const waitEvent = event as ExtendableEvent if ("type" in event.data && event.data.type === "subscribed") { waitEvent.waitUntil(submitSubscription(event.data.subscription)) } }) self.addEventListener("pushsubscriptionchange", function (event) { const waitEvent = event as ExtendableEvent waitEvent.waitUntil( (async () => { const applicationServerKey = urlB64ToUint8Array(pushPublicKey) const newSubscription = await self.registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: applicationServerKey, }) await submitSubscription(newSubscription.toJSON()) })() ) }) } async function submitSubscription(subscription: PushSubscriptionJSON) { const db = database() const existingSubscriptionId = await db.subscriptions.get(1) await (existingSubscriptionId === undefined ? postSubscription(subscription, db) : putSubscription(subscription, existingSubscriptionId.subscriptionId)) } async function postSubscription( subscription: PushSubscriptionJSON, db: ReturnType ) { const response = await fetch(`${process.env.BASE_URL}/api/subscription/`, { method: "POST", body: JSON.stringify(subscription), headers: { "Content-Type": "application/json", }, }) db.subscriptions.put({ id: 1, subscriptionId: (await response.json()).subscriptionId, }) } function putSubscription(subscription: PushSubscriptionJSON, id: number) { return fetch(`${process.env.BASE_URL}/api/subscription/${id}`, { method: "PUT", body: JSON.stringify(subscription), headers: { "Content-Type": "application/json", }, }) } function database() { const db = new Dexie("tack-up-now", { indexedDB }) as Dexie & { subscriptions: EntityTable } db.version(1).stores({ subscriptions: "id++, subscriptionId&" }) return db }