Private
Public Access
Actually fix WNS push, and stop the notification toggle hanging forever (#56)
The pywebpush version bump alone didn't fix WNS: even the latest release (2.4.0) has no WNS-specific header handling in its own source, confirmed by inspecting the installed package directly. Adds the required X-WNS-Cache-Policy header ourselves via webpush()'s own headers= param, gated to *.notify.windows.com endpoints. Also: subscribeToPush()'s permission request and service-worker-ready wait had no timeout, so a browser that never settles either (seen live on a fresh Windows/Edge install -- greyed out, no prompt, no error) left the toggle stuck forever with no feedback. Both now time out after 20s with an actionable message instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,33 @@
|
||||
import { getVapidPublicKey, subscribePush, unsubscribePush } from '../api/push'
|
||||
|
||||
// A stuck permission prompt or service-worker-ready wait would otherwise
|
||||
// leave TopBar's "Enable notifications" toggle permanently disabled with
|
||||
// no feedback at all -- confirmed live on a fresh Windows/Edge install
|
||||
// (never been used before): clicking it greyed the button out and never
|
||||
// showed the OS permission prompt, with no error and no way out short of
|
||||
// reloading. Most likely cause is Windows' own per-app notification
|
||||
// permission being off for Edge (Settings > System > Notifications), which
|
||||
// some Chromium versions handle by just never resolving the request
|
||||
// instead of rejecting it -- but whatever the cause, the UI should never
|
||||
// hang forever waiting on a browser API that may simply never settle.
|
||||
class PushTimeoutError extends Error {}
|
||||
|
||||
function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => reject(new PushTimeoutError(message)), ms)
|
||||
promise.then(
|
||||
(value) => {
|
||||
clearTimeout(timer)
|
||||
resolve(value)
|
||||
},
|
||||
(err: unknown) => {
|
||||
clearTimeout(timer)
|
||||
reject(err)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function urlBase64ToUint8Array(base64String: string): Uint8Array<ArrayBuffer> {
|
||||
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
|
||||
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
|
||||
@@ -27,7 +55,11 @@ export async function subscribeToPush(): Promise<void> {
|
||||
throw new Error('Push notifications are not supported in this browser')
|
||||
}
|
||||
|
||||
const permission = await Notification.requestPermission()
|
||||
const permission = await withTimeout(
|
||||
Notification.requestPermission(),
|
||||
20_000,
|
||||
"The browser never responded to the notification permission request. Check this browser's notification permission for this site, and your OS-level notification settings for the browser, then try again.",
|
||||
)
|
||||
if (permission !== 'granted') {
|
||||
throw new Error('Notification permission was not granted')
|
||||
}
|
||||
@@ -37,7 +69,11 @@ export async function subscribeToPush(): Promise<void> {
|
||||
throw new Error('Push notifications are not configured on the server')
|
||||
}
|
||||
|
||||
const registration = await navigator.serviceWorker.ready
|
||||
const registration = await withTimeout(
|
||||
navigator.serviceWorker.ready,
|
||||
20_000,
|
||||
'The browser never finished setting up its background service worker. Try reloading the page.',
|
||||
)
|
||||
const subscription = await registration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: urlBase64ToUint8Array(public_key),
|
||||
|
||||
Reference in New Issue
Block a user