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
|
const { endpoint } = subscription
|
||||||
logger.info('unsubscribed from push notifications', { endpoint })
|
logger.info('unsubscribed from push notifications', { endpoint })
|
||||||
await deletePushSubscription({ variables: { 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 })
|
logger.info('deleted push subscription from server', { endpoint })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,24 +109,36 @@ export const ServiceWorkerProvider = ({ children }) => {
|
|||||||
pushManager: 'PushManager' in window
|
pushManager: 'PushManager' in window
|
||||||
})
|
})
|
||||||
setPermission({ notification: 'Notification' in window ? window.Notification.permission : 'denied' })
|
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 (!('serviceWorker' in navigator)) {
|
||||||
if (!support.serviceWorker) {
|
|
||||||
logger.info('device does not support service worker')
|
logger.info('device does not support service worker')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const wb = new Workbox('/sw.js', { scope: '/' })
|
const wb = new Workbox('/sw.js', { scope: '/' })
|
||||||
wb.register().then(registration => {
|
wb.register().then(registration => {
|
||||||
logger.info('service worker registration successful')
|
logger.info('service worker registration successful')
|
||||||
setRegistration(registration)
|
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 (
|
return (
|
||||||
<ServiceWorkerContext.Provider value={{ registration, support, permission, requestNotificationPermission, togglePushSubscription }}>
|
<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
|
// for communication between app and service worker
|
||||||
// see https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel
|
// see https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel
|
||||||
let messageChannelPort
|
let messageChannelPort
|
||||||
|
let actionChannelPort
|
||||||
|
|
||||||
// keep track of item ids where we received a MENTION notification already to not show one again
|
// keep track of item ids where we received a MENTION notification already to not show one again
|
||||||
const itemMentions = []
|
const itemMentions = []
|
||||||
@ -103,13 +104,22 @@ export function onNotificationClick (sw) {
|
|||||||
|
|
||||||
export function onPushSubscriptionChange (sw) {
|
export function onPushSubscriptionChange (sw) {
|
||||||
// https://medium.com/@madridserginho/how-to-handle-webpush-api-pushsubscriptionchange-event-in-modern-browsers-6e47840d756f
|
// 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
|
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/pushsubscriptionchange_event
|
||||||
// fallbacks since browser may not set oldSubscription and newSubscription
|
// fallbacks since browser may not set oldSubscription and newSubscription
|
||||||
messageChannelPort?.postMessage({ message: '[sw:handlePushSubscriptionChange] invoked' })
|
messageChannelPort?.postMessage({ message: '[sw:handlePushSubscriptionChange] invoked' })
|
||||||
oldSubscription ??= await storage.getItem('subscription')
|
oldSubscription ??= await storage.getItem('subscription')
|
||||||
newSubscription ??= await sw.registration.pushManager.getSubscription()
|
newSubscription ??= await sw.registration.pushManager.getSubscription()
|
||||||
if (!newSubscription) {
|
if (!newSubscription) {
|
||||||
|
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
|
// no subscription exists at the moment
|
||||||
messageChannelPort?.postMessage({ message: '[sw:handlePushSubscriptionChange] no existing subscription found' })
|
messageChannelPort?.postMessage({ message: '[sw:handlePushSubscriptionChange] no existing subscription found' })
|
||||||
return
|
return
|
||||||
@ -148,6 +158,10 @@ export function onPushSubscriptionChange (sw) {
|
|||||||
|
|
||||||
export function onMessage (sw) {
|
export function onMessage (sw) {
|
||||||
return (event) => {
|
return (event) => {
|
||||||
|
if (event.data.action === 'ACTION_PORT') {
|
||||||
|
actionChannelPort = event.ports[0]
|
||||||
|
return
|
||||||
|
}
|
||||||
if (event.data.action === 'MESSAGE_PORT') {
|
if (event.data.action === 'MESSAGE_PORT') {
|
||||||
messageChannelPort = event.ports[0]
|
messageChannelPort = event.ports[0]
|
||||||
}
|
}
|
||||||
@ -157,7 +171,10 @@ export function onMessage (sw) {
|
|||||||
return event.waitUntil(storage.setItem('subscription', event.data.subscription))
|
return event.waitUntil(storage.setItem('subscription', event.data.subscription))
|
||||||
}
|
}
|
||||||
if (event.data.action === 'SYNC_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…
x
Reference in New Issue
Block a user