Declarative Web Push support (#2300)
* Declarative Web Push support, standardized JSON format TODOs: - sane app badge count * URL backwards compatibility, add icon to the JSON payload, fix malformed payload recognition on classic push notifications * typo: wrong app_badge placement in JSON payload * adapt declarative JSON payload for legacy Push API using spec-conformant transformations
This commit is contained in:
		
							parent
							
								
									20147cae15
								
							
						
					
					
						commit
						9c8071339f
					
				@ -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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								sw/index.js
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								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))
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user