Skip to content
Clone
eeff292
94 lines 3.4 KB JavaScript
/*
* GitData Service Worker — Web Push 接收端。
*
* 只处理推送与通知点击,不做离线缓存:这个站点是实时协作工具,缓存旧版本
* 的界面比直接报网络错误更糟。
*/
self.addEventListener('install', () => {
// 新版本立刻接管,推送处理逻辑没有需要等待的迁移步骤。
self.skipWaiting()
})
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim())
})
self.addEventListener('push', (event) => {
let payload = {}
try {
payload = event.data ? event.data.json() : {}
} catch {
// 推送服务只保证把字节送到;解析失败时退化成一条通用提醒,而不是静默丢弃。
payload = {}
}
const title = typeof payload.title === 'string' && payload.title ? payload.title : 'GitDataAI'
const url = typeof payload.url === 'string' && payload.url ? payload.url : '/app/@me/notify'
event.waitUntil(
self.registration.showNotification(title, {
body: typeof payload.body === 'string' ? payload.body : undefined,
icon: typeof payload.icon === 'string' ? payload.icon : '/favicon.png',
tag: typeof payload.tag === 'string' ? payload.tag : undefined,
// 同一 tag 的新通知要能再次打断用户:聊天场景里"上一条已经被顶掉"比
// 静默替换更容易错过。
renotify: true,
data: { url },
}),
)
})
self.addEventListener('notificationclick', (event) => {
event.notification.close()
const target = new URL(event.notification.data?.url || '/app/@me/notify', self.location.origin)
event.waitUntil(
(async () => {
const clientList = await self.clients.matchAll({
type: 'window',
includeUncontrolled: true,
})
// 优先复用已经打开的窗口:直接 openWindow 会让同一个应用出现两个标签页,
// 而用户期望的是"通知把我带回刚才那个界面"。
for (const client of clientList) {
if (new URL(client.url).origin !== target.origin) continue
await client.focus()
if ('navigate' in client && client.url !== target.href) {
await client.navigate(target.href).catch(() => {})
}
return
}
await self.clients.openWindow(target.href)
})(),
)
})
self.addEventListener('pushsubscriptionchange', (event) => {
// 浏览器会不定期轮换订阅(尤其是长期未使用的设备)。不重新注册的话,服务端
// 存着的 endpoint 会失效,推送从此静默丢失。
event.waitUntil(
(async () => {
const applicationServerKey =
event.oldSubscription?.options?.applicationServerKey ??
(await self.registration.pushManager.getSubscription())?.options?.applicationServerKey
if (!applicationServerKey) return
const subscription = await self.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey,
})
const json = subscription.toJSON()
await fetch('/api/v1/me/push-subscriptions', {
method: 'POST',
credentials: 'include',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
endpoint: subscription.endpoint,
p256dh_key: json.keys?.p256dh ?? '',
auth_key: json.keys?.auth ?? '',
platform: 'web_push',
user_agent: self.navigator.userAgent,
}),
})
})(),
)
})