diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest index dd061cf..405cfde 100644 --- a/public/manifest.webmanifest +++ b/public/manifest.webmanifest @@ -9,6 +9,7 @@ "orientation": "portrait", "background_color": "#000000", "theme_color": "#000000", + "gcm_sender_id": "103953800507", "icons": [ { "src": "/images/app-icon-192.png", diff --git a/public/sw.js b/public/sw.js index 339da63..c8bc1e2 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,4 +1,4 @@ -const CACHE_NAME = 'l484-pwa-v3' +const CACHE_NAME = 'l484-pwa-v4' const APP_SHELL = [ '/', '/manifest.webmanifest', diff --git a/src/services/notifications.js b/src/services/notifications.js index 5c57c63..2721d1e 100644 --- a/src/services/notifications.js +++ b/src/services/notifications.js @@ -19,29 +19,7 @@ export const notificationSupport = () => ({ secure: window.isSecureContext || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1', }) -export const subscribeToNotifications = async () => { - const support = notificationSupport() - if (!support.supported) throw new Error('Push notifications are not supported in this browser.') - if (!support.secure) throw new Error('Push notifications require HTTPS.') - - const keyResponse = await fetch('/api/notifications/vapid-public-key') - const keyData = await keyResponse.json().catch(() => ({})) - if (!keyResponse.ok || !keyData.publicKey) throw new Error('VAPID public key is not configured.') - - const requested = await Notification.requestPermission() - permission.value = requested - if (requested !== 'granted') throw new Error('Notification permission was not granted.') - - const registration = await navigator.serviceWorker.register('/sw.js') - await navigator.serviceWorker.ready - const existing = await registration.pushManager.getSubscription() - if (existing) await existing.unsubscribe() - - const subscription = await registration.pushManager.subscribe({ - userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(keyData.publicKey), - }) - +const saveSubscription = async (subscription) => { const response = await fetch('/api/notifications/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -51,3 +29,42 @@ export const subscribeToNotifications = async () => { if (!response.ok) throw new Error(data.error || 'Could not save notification subscription.') return data } + +const subscribeWithRetry = async (registration, applicationServerKey) => { + try { + return await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey }) + } catch (error) { + await registration.update().catch(() => {}) + await new Promise((resolve) => window.setTimeout(resolve, 350)) + try { + return await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey }) + } catch { + const message = error instanceof Error ? error.message : 'Push service error.' + throw new Error(`Push registration failed: ${message}`) + } + } +} + +export const subscribeToNotifications = async () => { + const support = notificationSupport() + if (!support.supported) throw new Error('Push notifications are not supported in this browser.') + if (!support.secure) throw new Error('Push notifications require HTTPS.') + + const keyResponse = await fetch('/api/notifications/vapid-public-key') + const keyData = await keyResponse.json().catch(() => ({})) + if (!keyResponse.ok || !keyData.publicKey) throw new Error('VAPID public key is not configured.') + if (!keyData.configured) throw new Error('VAPID private key is not configured on the server.') + const applicationServerKey = urlBase64ToUint8Array(keyData.publicKey) + + const requested = await Notification.requestPermission() + permission.value = requested + if (requested !== 'granted') throw new Error('Notification permission was not granted.') + + const registration = await navigator.serviceWorker.register('/sw.js') + await navigator.serviceWorker.ready + const existing = await registration.pushManager.getSubscription() + if (existing) return saveSubscription(existing) + + const subscription = await subscribeWithRetry(registration, applicationServerKey) + return saveSubscription(subscription) +}