diff --git a/lib/webPush.js b/lib/webPush.js index 34c6cb89..61a132ce 100644 --- a/lib/webPush.js +++ b/lib/webPush.js @@ -15,14 +15,19 @@ function log (...args) { function createPayload (notification) { // https://web.dev/push-notifications-display-a-notification/#visual-options + // https://webkit.org/blog/16535/meet-declarative-web-push/ + // DEV: localhost in URLs is not supported by declarative web push let { title, body, ...options } = notification if (body) body = removeMd(body) return JSON.stringify({ - title, - options: { + web_push: 8030, // Declarative Web Push JSON format + notification: { + title, body, timestamp: Date.now(), - icon: '/icons/icon_x96.png', + icon: process.env.NEXT_PUBLIC_URL + '/icons/icon_x96.png', + navigate: process.env.NEXT_PUBLIC_URL + '/notifications', // navigate is required + app_badge: 1, // TODO: establish a proper badge count system ...options } }) @@ -58,6 +63,10 @@ async function sendNotification (subscription, payload) { subject: process.env.VAPID_MAILTO, publicKey: process.env.NEXT_PUBLIC_VAPID_PUBKEY, privateKey: process.env.VAPID_PRIVKEY + }, + // conformant to declarative web push spec + headers: { + 'Content-Type': 'application/notification+json' } }) .catch(async (err) => { @@ -95,7 +104,10 @@ async function sendUserNotification (userId, notification) { } notification.data ??= {} if (notification.itemId) { + // legacy Push API notificationclick event needs data.url as the navigate key is consumed by the browser notification.data.url ??= await createItemUrl(notification.itemId) + // Declarative Web Push can't use relative paths + notification.navigate ??= process.env.NEXT_PUBLIC_URL + notification.data.url delete notification.itemId } diff --git a/sw/index.js b/sw/index.js index 2f236479..e49ff436 100644 --- a/sw/index.js +++ b/sw/index.js @@ -73,13 +73,17 @@ setDefaultHandler(new NetworkOnly({ offlineFallback({ pageFallback: '/offline' }) self.addEventListener('push', function (event) { - let payload + let title, options try { - payload = event.data?.json() - if (!payload) { + const { notification } = event.data?.json() + if (!notification) { throw new Error('no payload in push event') } + + // adapt declarative payload for legacy Push API + options = notification || {} + title = notification.title } catch (err) { // we show a default nofication on any error because we *must* show a notification // else the browser will show one for us or worse, remove our push subscription @@ -94,7 +98,7 @@ self.addEventListener('push', function (event) { } event.waitUntil( - self.registration.showNotification(payload.title, payload.options) + self.registration.showNotification(title, options) .then(() => self.registration.getNotifications()) .then(notifications => self.navigator.setAppBadge?.(notifications.length)) )