Add timeouts and better error handling for push notifications

- Add 10s timeout for service worker ready state
- Add 15s timeout for push subscription
- Check for PushManager support early (shows unsupported on incompatible devices)
- Provide specific error messages for different failure modes

This prevents the enable button from spinning forever on iOS devices
where push subscription may hang silently.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Gemini Agent
2026-01-23 20:54:40 +00:00
parent c9f3402e48
commit 54900b65c8

View File

@@ -31,15 +31,28 @@ export function NotificationPermission({ workspaceId }: NotificationPermissionPr
return return
} }
// Check if PushManager is available (not available in all browsers/contexts)
if (!('PushManager' in window)) {
setPermission('unsupported')
return
}
const perm = Notification.permission as PermissionState const perm = Notification.permission as PermissionState
setPermission(perm) setPermission(perm)
if (perm === 'granted') { if (perm === 'granted') {
// Check if already subscribed // Check if already subscribed with timeout
try { try {
const registration = await navigator.serviceWorker.ready const registrationPromise = navigator.serviceWorker.ready
const timeoutPromise = new Promise<ServiceWorkerRegistration>((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 5000)
)
const registration = await Promise.race([registrationPromise, timeoutPromise])
if (registration.pushManager) {
const subscription = await registration.pushManager.getSubscription() const subscription = await registration.pushManager.getSubscription()
setIsSubscribed(!!subscription) setIsSubscribed(!!subscription)
}
} catch (err) { } catch (err) {
console.error('Failed to check subscription:', err) console.error('Failed to check subscription:', err)
} }
@@ -66,14 +79,27 @@ export function NotificationPermission({ workspaceId }: NotificationPermissionPr
} }
const { publicKey } = await keyResponse.json() const { publicKey } = await keyResponse.json()
// Register service worker if not already registered // Wait for service worker with timeout
const registration = await navigator.serviceWorker.ready const registrationPromise = navigator.serviceWorker.ready
const timeoutPromise = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error('Service worker not ready - try refreshing the page')), 10000)
)
const registration = await Promise.race([registrationPromise, timeoutPromise])
// Subscribe to push // Check if push manager is available
const subscription = await registration.pushManager.subscribe({ if (!registration.pushManager) {
throw new Error('Push notifications not supported on this device')
}
// Subscribe to push with timeout
const subscribePromise = registration.pushManager.subscribe({
userVisibleOnly: true, userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicKey), applicationServerKey: urlBase64ToUint8Array(publicKey),
}) })
const subscribeTimeout = new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error('Push subscription timed out - this device may not support web push')), 15000)
)
const subscription = await Promise.race([subscribePromise, subscribeTimeout])
// Send subscription to server // Send subscription to server
const response = await fetch('/api/notifications/subscribe', { const response = await fetch('/api/notifications/subscribe', {