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) { // console.log("[Service Worker] Push Received.") // console.log(`[Service Worker] Push had this data: "${event.data?.text()}"`) // const title = "Push Codelab" // const options = { // body: "Yay it works.", // icon: "images/icon.png", // badge: "images/badge.png", // } // event.waitUntil(self.registration.showNotification(title, options)) // }) // self.addEventListener("notificationclick", function (event) { // console.log("[Service Worker] Notification click Received.") // event.notification.close() // event.waitUntil( // self.clients.openWindow("https://developers.google.com/web/") // ) // }) async function sendMessage(message: any) { const clients = await self.clients.matchAll() await Promise.all( clients.map(async (client) => client.postMessage(message)) ) } self.addEventListener("message", function (event) { const waitEvent = event as ExtendableEvent waitEvent.waitUntil( (async () => { await sendMessage({ message: "Got message", }) if ( "type" in event.data && event.data.type === "subscribed" && "subscription" in event.data ) { try { await event.waitUntil( submitSubscription( self.registration, event.data.subscription as PushSubscription ) ) } catch (e) { await sendMessage({ message: "Got error processing subscription", error: (e as Error).toString(), }) } } await sendMessage({ message: "Processed subscription", }) })() ) }) self.addEventListener("pushsubscriptionchange", function (event) { const waitEvent = event as ExtendableEvent waitEvent.waitUntil( (async () => { const existingSubscription = await self.registration.pushManager.getSubscription() const newSubscription = await submitSubscription( self.registration, existingSubscription ) await sendMessage({ type: "sent subscription", newSubscription }) })() ) }) } async function submitSubscription( registration: ServiceWorkerRegistration, subscription: PushSubscription | null ) { const db = database() if (subscription === null) return const existingSubscriptionId = await db.subscriptions.get(1) const applicationServerKey = urlB64ToUint8Array(pushPublicKey) const newSubscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: applicationServerKey, }) await (existingSubscriptionId === undefined ? postSubscription(newSubscription, db) : putSubscription(newSubscription, existingSubscriptionId.subscriptionId)) return newSubscription } async function postSubscription( subscription: PushSubscription, db: ReturnType ) { const response = await fetch(`${process.env.BASE_URL}/api/subscription/`, { method: "POST", body: JSON.stringify(subscription), }) db.subscriptions.put({ id: 1, subscriptionId: (await response.json()).subscriptionId, }) } function putSubscription(subscription: PushSubscription, id: number) { return fetch(`${process.env.BASE_URL}/api/subscription/${id}`, { method: "PUT", body: JSON.stringify(subscription), }) } function database() { const db = new Dexie("tack-up-now", { indexedDB }) as Dexie & { subscriptions: EntityTable } db.version(1).stores({ subscriptions: "id++, subscriptionId&" }) return db }