Notification preferences API and screen (1n) (Feature 19) (#166)
CI / ci (push) Successful in 5m53s
CI / ci (push) Successful in 5m53s
Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com>
This commit was merged in pull request #166.
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
decodeVapidKey,
|
||||
encodeBase64url,
|
||||
PermissionGestureError,
|
||||
pushStatus,
|
||||
subscribePush,
|
||||
subscriptionBody,
|
||||
unsubscribePush,
|
||||
@@ -306,3 +307,31 @@ describe('unsubscribePush', () => {
|
||||
await expect(unsubscribePush(api, env)).rejects.toThrow(/did not remove its push subscription/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('pushStatus', () => {
|
||||
it('is on with permission granted and a subscription', async () => {
|
||||
const { env } = setup({ permission: 'granted', current: subscription() })
|
||||
expect(await pushStatus(env)).toBe('on')
|
||||
})
|
||||
|
||||
it.each([
|
||||
['not subscribed', { permission: 'granted' as const }],
|
||||
['permission not yet asked', { permission: 'default' as const }],
|
||||
['no service worker registered', { permission: 'granted' as const, registered: false }],
|
||||
])('is off when %s', async (_, options) => {
|
||||
const { env, notification } = setup(options)
|
||||
expect(await pushStatus(env)).toBe('off')
|
||||
expect(notification.requestPermission).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('is denied when the browser blocks notifications', async () => {
|
||||
const { env } = setup({ permission: 'denied', current: subscription() })
|
||||
expect(await pushStatus(env)).toBe('denied')
|
||||
})
|
||||
|
||||
it('is unsupported without Web Push', async () => {
|
||||
const { env } = setup()
|
||||
expect(await pushStatus({ ...env, pushManager: false })).toBe('unsupported')
|
||||
expect(await pushStatus({ ...env, notification: undefined })).toBe('unsupported')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,6 +19,11 @@ export type SubscribePushResult = 'subscribed' | 'denied' | 'dismissed' | 'unsup
|
||||
// to remove, or a browser without Web Push. Any other failure rejects.
|
||||
export type UnsubscribePushResult = 'unsubscribed' | 'not-subscribed' | 'unsupported'
|
||||
|
||||
// PushStatus is whether Web Push is on for this device: subscribed with
|
||||
// permission granted, off (not subscribed, or permission not yet given),
|
||||
// blocked by the browser's permission, or a browser without Web Push.
|
||||
export type PushStatus = 'on' | 'off' | 'denied' | 'unsupported'
|
||||
|
||||
// The parts of the browser's Push API this module uses.
|
||||
export type BrowserPushSubscription = {
|
||||
readonly endpoint: string
|
||||
@@ -207,3 +212,16 @@ export async function unsubscribePush(api: PushApi, env: PushEnvironment): Promi
|
||||
await removeSubscription(api, subscription)
|
||||
return 'unsubscribed'
|
||||
}
|
||||
|
||||
// pushStatus reads whether Web Push is on for this device, without asking for
|
||||
// permission or calling the server. A page with no service worker registered
|
||||
// is off: subscribePush then says why it cannot turn on.
|
||||
export async function pushStatus(env: PushEnvironment): Promise<PushStatus> {
|
||||
const { serviceWorker, notification } = env
|
||||
if (serviceWorker === undefined || !env.pushManager || notification === undefined) return 'unsupported'
|
||||
if (notification.permission === 'denied') return 'denied'
|
||||
const found = await serviceWorker.getRegistration()
|
||||
if (found === undefined) return 'off'
|
||||
const subscription = await found.pushManager.getSubscription()
|
||||
return subscription !== null && notification.permission === 'granted' ? 'on' : 'off'
|
||||
}
|
||||
|
||||
@@ -2,25 +2,30 @@ import { useMemo } from 'react'
|
||||
import { useApi } from '../api/ApiContext.tsx'
|
||||
import {
|
||||
browserPushEnvironment,
|
||||
pushStatus,
|
||||
subscribePush,
|
||||
unsubscribePush,
|
||||
type PushStatus,
|
||||
type SubscribePushResult,
|
||||
type UnsubscribePushResult,
|
||||
} from './subscribe.ts'
|
||||
|
||||
export type Push = {
|
||||
// pushStatus reads whether Web Push is on for this device.
|
||||
pushStatus(): Promise<PushStatus>
|
||||
// subscribePush turns on Web Push on this device; call it only from a tap.
|
||||
subscribePush(): Promise<SubscribePushResult>
|
||||
// unsubscribePush turns off Web Push on this device.
|
||||
unsubscribePush(): Promise<UnsubscribePushResult>
|
||||
}
|
||||
|
||||
// usePush binds subscribePush and unsubscribePush to the app's API client
|
||||
// and this browser.
|
||||
// usePush binds pushStatus, subscribePush and unsubscribePush to the app's
|
||||
// API client and this browser.
|
||||
export function usePush(): Push {
|
||||
const api = useApi()
|
||||
return useMemo(
|
||||
() => ({
|
||||
pushStatus: () => pushStatus(browserPushEnvironment()),
|
||||
subscribePush: () => subscribePush(api, browserPushEnvironment()),
|
||||
unsubscribePush: () => unsubscribePush(api, browserPushEnvironment()),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user