tack-up-now/app/worker.ts

133 lines
3.5 KiB
TypeScript

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<typeof database>
) {
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<SubscriptionRecord, "id">
}
db.version(1).stores({ subscriptions: "id++, subscriptionId&" })
return db
}