Only close notifications manually on iOS (#729)
* Only close notifications manually on iOS * Use function instead of hardcoded string --------- Co-authored-by: ekzyis <ek@stacker.news> Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
This commit is contained in:
parent
ade35b9ea0
commit
31cef5a7b7
|
@ -11,7 +11,7 @@ const generateFancyName = () => {
|
|||
return `${adj}-${noun}-${id}`
|
||||
}
|
||||
|
||||
function detectOS () {
|
||||
export function detectOS () {
|
||||
if (!window.navigator) return ''
|
||||
|
||||
const userAgent = window.navigator.userAgent
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react'
|
||||
import { Workbox } from 'workbox-window'
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import { useLogger } from './logger'
|
||||
import { detectOS, useLogger } from './logger'
|
||||
|
||||
const applicationServerKey = process.env.NEXT_PUBLIC_VAPID_PUBKEY
|
||||
|
||||
const ServiceWorkerContext = createContext()
|
||||
|
||||
// message types for communication between app and service worker
|
||||
export const STORE_OS = 'STORE_OS'
|
||||
|
||||
export const ServiceWorkerProvider = ({ children }) => {
|
||||
const [registration, setRegistration] = useState(null)
|
||||
const [support, setSupport] = useState({ serviceWorker: undefined, pushManager: undefined })
|
||||
|
@ -151,6 +154,7 @@ export const ServiceWorkerProvider = ({ children }) => {
|
|||
// 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')
|
||||
navigator?.serviceWorker?.controller?.postMessage?.({ action: STORE_OS, os: detectOS() })
|
||||
}, [registration])
|
||||
|
||||
const contextValue = useMemo(() => ({
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import ServiceWorkerStorage from 'serviceworker-storage'
|
||||
import { numWithUnits } from '../lib/format'
|
||||
import { CLEAR_NOTIFICATIONS, clearAppBadge, setAppBadge } from '../lib/badge'
|
||||
import { STORE_OS } from '../components/serviceworker'
|
||||
|
||||
// we store existing push subscriptions to keep them in sync with server
|
||||
const storage = new ServiceWorkerStorage('sw:storage', 1)
|
||||
|
@ -10,6 +11,10 @@ const storage = new ServiceWorkerStorage('sw:storage', 1)
|
|||
let messageChannelPort
|
||||
let actionChannelPort
|
||||
|
||||
// operating system. the value will be received via a STORE_OS message from app since service workers don't have access to window.navigator
|
||||
let os = ''
|
||||
const iOS = () => os === 'iOS'
|
||||
|
||||
// current push notification count for badge purposes
|
||||
let activeCount = 0
|
||||
|
||||
|
@ -47,8 +52,16 @@ export function onPush (sw) {
|
|||
// we therefore close them manually and then we display the notification.
|
||||
log(`[sw:push] ${nid} - ${tag} notifications replace previous notifications`)
|
||||
setAppBadge(sw, ++activeCount)
|
||||
log(`[sw:push] ${nid} - closing existing notifications`)
|
||||
filtered.forEach(n => n.close())
|
||||
// due to missing proper tag support in Safari on iOS, we can't rely on the tag property to replace notifications.
|
||||
// see https://bugs.webkit.org/show_bug.cgi?id=258922 for more information
|
||||
// we therefore fetch all notifications with the same tag (+ manual filter),
|
||||
// close them and then we display the notification.
|
||||
const notifications = await sw.registration.getNotifications({ tag })
|
||||
// we only close notifications manually on iOS because we don't want to degrade android UX just because iOS is behind in their support.
|
||||
if (iOS()) {
|
||||
log(`[sw:push] ${nid} - closing existing notifications`)
|
||||
notifications.filter(({ tag: nTag }) => nTag === tag).forEach(n => n.close())
|
||||
}
|
||||
log(`[sw:push] ${nid} - show notification: ${payload.title} ${JSON.stringify(payload.options)}`)
|
||||
return await sw.registration.showNotification(payload.title, payload.options)
|
||||
}
|
||||
|
@ -144,8 +157,11 @@ const mergeAndShowNotification = async (sw, payload, currentNotifications, tag,
|
|||
log(`[sw:push] ${nid} - calculated title: ${title}`)
|
||||
|
||||
// close all current notifications before showing new one to "merge" notifications
|
||||
log(`[sw:push] ${nid} - closing existing notifications`)
|
||||
currentNotifications.forEach(n => n.close())
|
||||
// we only do this on iOS because we don't want to degrade android UX just because iOS is behind in their support.
|
||||
if (iOS()) {
|
||||
log(`[sw:push] ${nid} - closing existing notifications`)
|
||||
currentNotifications.forEach(n => n.close())
|
||||
}
|
||||
|
||||
const options = { icon: payload.options?.icon, tag, data: { url: '/notifications', ...mergedPayload } }
|
||||
log(`[sw:push] ${nid} - show notification: ${title} ${JSON.stringify(options)}`)
|
||||
|
@ -231,6 +247,10 @@ export function onMessage (sw) {
|
|||
actionChannelPort = event.ports[0]
|
||||
return
|
||||
}
|
||||
if (event.data.action === STORE_OS) {
|
||||
os = event.data.os
|
||||
return
|
||||
}
|
||||
if (event.data.action === 'MESSAGE_PORT') {
|
||||
messageChannelPort = event.ports[0]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue