96 lines
2.7 KiB
TypeScript
96 lines
2.7 KiB
TypeScript
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
|
/// <reference types="vite/client" />
|
|
/// <reference lib="webworker" />
|
|
|
|
import { clientsClaim } from 'workbox-core'
|
|
import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'
|
|
|
|
import { makeNotificationOptions } from './lib/notificationOptions'
|
|
|
|
import type { NotificationData, NotificationPayload } from './lib/serverEventsTypes'
|
|
|
|
declare const self: ServiceWorkerGlobalScope
|
|
|
|
void self.skipWaiting()
|
|
clientsClaim()
|
|
cleanupOutdatedCaches()
|
|
precacheAndRoute(self.__WB_MANIFEST)
|
|
|
|
self.addEventListener('message', (event) => {
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
if (event.data?.type === 'SKIP_WAITING') {
|
|
void self.skipWaiting()
|
|
}
|
|
})
|
|
|
|
self.addEventListener('push', (event) => {
|
|
if (!event.data) {
|
|
console.error('Push event received but no data')
|
|
return
|
|
}
|
|
|
|
const payload = parseNotificationPayload(event.data)
|
|
event.waitUntil(showPushNotification(payload))
|
|
})
|
|
|
|
self.addEventListener('notificationclick', (event) => {
|
|
event.notification.close()
|
|
|
|
const data: NotificationData = event.notification.data
|
|
|
|
const url =
|
|
data.payload?.actions.find((action) => action.action === event.action)?.url || data.defaultActionUrl
|
|
|
|
event.waitUntil(handleNotificationClick(url))
|
|
})
|
|
|
|
async function handleNotificationClick(url: string) {
|
|
const clients = await self.clients.matchAll({ type: 'window' })
|
|
|
|
for (const client of clients) {
|
|
if (client.url === url && 'focus' in client) {
|
|
await client.focus()
|
|
return
|
|
}
|
|
}
|
|
|
|
await self.clients.openWindow(url)
|
|
}
|
|
|
|
async function showPushNotification(payload: NotificationPayload | null) {
|
|
await self.registration.showNotification(
|
|
payload?.title ?? 'New Notification',
|
|
makeNotificationOptions(payload)
|
|
)
|
|
}
|
|
|
|
function parseNotificationPayload(data: PushMessageData) {
|
|
try {
|
|
const payload = data.json()
|
|
if (isValidNotificationPayload(payload)) return payload
|
|
|
|
console.error('Invalid push notification payload:', payload)
|
|
return null
|
|
} catch (error) {
|
|
console.error('Error parsing push notification data:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
function isValidNotificationPayload(payload: unknown): payload is NotificationPayload {
|
|
if (typeof payload !== 'object' || payload === null) return false
|
|
|
|
const p = payload as Record<string, unknown>
|
|
|
|
return (
|
|
typeof p.title === 'string' &&
|
|
(typeof p.body === 'string' || p.body === null) &&
|
|
Array.isArray(p.actions) &&
|
|
p.actions.every((action) => {
|
|
if (typeof action !== 'object' || action === null) return false
|
|
const a = action as Record<string, unknown>
|
|
return typeof a.action === 'string' && typeof a.title === 'string'
|
|
})
|
|
)
|
|
}
|