Resubscribe if service worker lost push subscription (#597)
* Also delete push subscription in IndexedDB * Fix pushsubscriptionchange function signature * Send SYNC_SUBSCRIPTION after successful registration * Resubscribe if service worker lost subscription --------- Co-authored-by: ekzyis <ek@stacker.news>
This commit is contained in:
parent
7040dbfce6
commit
5dfeb700bc
|
@ -88,6 +88,9 @@ export const ServiceWorkerProvider = ({ children }) => {
|
|||
const { endpoint } = subscription
|
||||
logger.info('unsubscribed from push notifications', { endpoint })
|
||||
await deletePushSubscription({ variables: { endpoint } })
|
||||
// also delete push subscription in IndexedDB so we can tell if the user disabled push subscriptions
|
||||
// or we lost the push subscription due to a bug
|
||||
navigator.serviceWorker.controller.postMessage({ action: 'DELETE_SUBSCRIPTION' })
|
||||
logger.info('deleted push subscription from server', { endpoint })
|
||||
}
|
||||
|
||||
|
@ -106,24 +109,36 @@ export const ServiceWorkerProvider = ({ children }) => {
|
|||
pushManager: 'PushManager' in window
|
||||
})
|
||||
setPermission({ notification: 'Notification' in window ? window.Notification.permission : 'denied' })
|
||||
// since (a lot of) browsers don't support the pushsubscriptionchange event,
|
||||
// we sync with server manually by checking on every page reload if the push subscription changed.
|
||||
// see https://medium.com/@madridserginho/how-to-handle-webpush-api-pushsubscriptionchange-event-in-modern-browsers-6e47840d756f
|
||||
navigator?.serviceWorker?.controller?.postMessage?.({ action: 'SYNC_SUBSCRIPTION' })
|
||||
logger.info('sent SYNC_SUBSCRIPTION to service worker')
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!support.serviceWorker) {
|
||||
if (!('serviceWorker' in navigator)) {
|
||||
logger.info('device does not support service worker')
|
||||
return
|
||||
}
|
||||
|
||||
const wb = new Workbox('/sw.js', { scope: '/' })
|
||||
wb.register().then(registration => {
|
||||
logger.info('service worker registration successful')
|
||||
setRegistration(registration)
|
||||
})
|
||||
}, [support.serviceWorker])
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// wait until successful registration
|
||||
if (!registration) return
|
||||
// setup channel between app and service worker
|
||||
const channel = new MessageChannel()
|
||||
navigator?.serviceWorker?.controller?.postMessage({ action: 'ACTION_PORT' }, [channel.port2])
|
||||
channel.port1.onmessage = (event) => {
|
||||
if (event.data.action === 'RESUBSCRIBE') {
|
||||
return subscribeToPushNotifications()
|
||||
}
|
||||
}
|
||||
// since (a lot of) browsers don't support the pushsubscriptionchange event,
|
||||
// we sync with server manually by checking on every page reload if the push subscription changed.
|
||||
// see https://medium.com/@madridserginho/how-to-handle-webpush-api-pushsubscriptionchange-event-in-modern-browsers-6e47840d756f
|
||||
navigator?.serviceWorker?.controller?.postMessage?.({ action: 'SYNC_SUBSCRIPTION' })
|
||||
logger.info('sent SYNC_SUBSCRIPTION to service worker')
|
||||
}, [registration])
|
||||
|
||||
return (
|
||||
<ServiceWorkerContext.Provider value={{ registration, support, permission, requestNotificationPermission, togglePushSubscription }}>
|
||||
|
|
|
@ -7,6 +7,7 @@ const storage = new ServiceWorkerStorage('sw:storage', 1)
|
|||
// for communication between app and service worker
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel
|
||||
let messageChannelPort
|
||||
let actionChannelPort
|
||||
|
||||
// keep track of item ids where we received a MENTION notification already to not show one again
|
||||
const itemMentions = []
|
||||
|
@ -103,14 +104,23 @@ export function onNotificationClick (sw) {
|
|||
|
||||
export function onPushSubscriptionChange (sw) {
|
||||
// https://medium.com/@madridserginho/how-to-handle-webpush-api-pushsubscriptionchange-event-in-modern-browsers-6e47840d756f
|
||||
return async (oldSubscription, newSubscription) => {
|
||||
// `isSync` is passed if function was called because of 'SYNC_SUBSCRIPTION' event
|
||||
// this makes sure we can differentiate between 'pushsubscriptionchange' events and our custom 'SYNC_SUBSCRIPTION' event
|
||||
return async (event, isSync) => {
|
||||
let { oldSubscription, newSubscription } = event
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/pushsubscriptionchange_event
|
||||
// fallbacks since browser may not set oldSubscription and newSubscription
|
||||
messageChannelPort?.postMessage({ message: '[sw:handlePushSubscriptionChange] invoked' })
|
||||
oldSubscription ??= await storage.getItem('subscription')
|
||||
newSubscription ??= await sw.registration.pushManager.getSubscription()
|
||||
if (!newSubscription) {
|
||||
// no subscription exists at the moment
|
||||
if (isSync && oldSubscription) {
|
||||
// service worker lost the push subscription somehow
|
||||
messageChannelPort?.postMessage({ message: '[sw:handlePushSubscriptionChange] service worker lost subscription' })
|
||||
actionChannelPort?.postMessage({ action: 'RESUBSCRIBE' })
|
||||
return
|
||||
}
|
||||
// no subscription exists at the moment
|
||||
messageChannelPort?.postMessage({ message: '[sw:handlePushSubscriptionChange] no existing subscription found' })
|
||||
return
|
||||
}
|
||||
|
@ -148,6 +158,10 @@ export function onPushSubscriptionChange (sw) {
|
|||
|
||||
export function onMessage (sw) {
|
||||
return (event) => {
|
||||
if (event.data.action === 'ACTION_PORT') {
|
||||
actionChannelPort = event.ports[0]
|
||||
return
|
||||
}
|
||||
if (event.data.action === 'MESSAGE_PORT') {
|
||||
messageChannelPort = event.ports[0]
|
||||
}
|
||||
|
@ -157,7 +171,10 @@ export function onMessage (sw) {
|
|||
return event.waitUntil(storage.setItem('subscription', event.data.subscription))
|
||||
}
|
||||
if (event.data.action === 'SYNC_SUBSCRIPTION') {
|
||||
return event.waitUntil(onPushSubscriptionChange(sw)(event.oldSubscription, event.newSubscription))
|
||||
return event.waitUntil(onPushSubscriptionChange(sw)(event, true))
|
||||
}
|
||||
if (event.data.action === 'DELETE_SUBSCRIPTION') {
|
||||
return event.waitUntil(storage.removeItem('subscription'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue