diff options
| -rw-r--r-- | site/public/firebase-messaging-sw.js | 118 | ||||
| -rw-r--r-- | site/src/components/NotificationButton.tsx | 26 | ||||
| -rw-r--r-- | site/src/firebase.ts | 80 |
3 files changed, 67 insertions, 157 deletions
diff --git a/site/public/firebase-messaging-sw.js b/site/public/firebase-messaging-sw.js index 056ce2b..225652c 100644 --- a/site/public/firebase-messaging-sw.js +++ b/site/public/firebase-messaging-sw.js @@ -12,114 +12,54 @@ firebase.initializeApp({ const messaging = firebase.messaging(); -// Handle background messages -messaging.onBackgroundMessage(function(payload) { - console.log('[firebase-messaging-sw.js] Received background message', payload); - - // Extract notification data - const notificationTitle = payload.notification?.title || 'New Update'; - const notificationBody = payload.notification?.body || 'You have a new notification'; - - // Build notification options with enhanced features - const notificationOptions = { - body: notificationBody, - icon: payload.notification?.icon || '/android/android-launchericon-192-192.png', - badge: '/android/android-launchericon-72-72.png', - vibrate: [200, 100, 200], - tag: payload.data?.tag || 'default-tag', - requireInteraction: payload.data?.requireInteraction === 'true', - renotify: true, - silent: false, - timestamp: Date.now(), - data: { - url: payload.data?.url || '/', - gameId: payload.data?.gameId, - ...payload.data - } - }; - - // Add image if provided - if (payload.notification?.image) { - notificationOptions.image = payload.notification.image; - } - - // Add actions if provided - if (payload.data?.actions) { - try { - notificationOptions.actions = JSON.parse(payload.data.actions); - } catch (e) { - console.error('Failed to parse notification actions:', e); - } - } - - // Show the notification - return self.registration.showNotification(notificationTitle, notificationOptions); -}); - // Handle notification clicks self.addEventListener('notificationclick', function(event) { console.log('[firebase-messaging-sw.js] Notification click received.', event); - event.notification.close(); - // Handle action clicks + const clickUrl = event.notification.data?.url || '/'; + if (event.action) { - console.log('Action clicked:', event.action); - // You can handle different actions here if (event.action === 'view') { - event.waitUntil( - clients.openWindow(event.notification.data?.url || '/') - ); - } else if (event.action === 'dismiss') { - // Just close the notification - return; + event.waitUntil(clients.openWindow(clickUrl)); } - } else { - // Default click behavior - open the URL - const clickUrl = event.notification.data?.url || '/'; - - event.waitUntil( - clients.matchAll({ - type: 'window', - includeUncontrolled: true - }).then(function(clientList) { - // Check if there's already a window/tab open with the target URL - for (const client of clientList) { - if (client.url === clickUrl && 'focus' in client) { - return client.focus(); - } - } - // If no existing window/tab, open a new one - if (clients.openWindow) { - return clients.openWindow(clickUrl); - } - }) - ); + return; } + + // Default click behavior + event.waitUntil( + clients.matchAll({ type: 'window', includeUncontrolled: true }).then(clientList => { + for (const client of clientList) { + if (client.url === clickUrl && 'focus' in client) { + return client.focus(); + } + } + if (clients.openWindow) { + return clients.openWindow(clickUrl); + } + }) + ); }); // Handle notification close self.addEventListener('notificationclose', function(event) { console.log('[firebase-messaging-sw.js] Notification was closed', event); - // You can track notification dismissals here if needed }); -// Handle service worker installation -self.addEventListener('install', function(event) { +// Install & activate +self.addEventListener('install', event => { console.log('[firebase-messaging-sw.js] Service Worker installing.'); self.skipWaiting(); }); -// Handle service worker activation -self.addEventListener('activate', function(event) { +self.addEventListener('activate', event => { console.log('[firebase-messaging-sw.js] Service Worker activated.'); event.waitUntil(clients.claim()); }); -// Handle push events (for debugging) -self.addEventListener('push', function(event) { +// Debugging push events +self.addEventListener('push', event => { console.log('[firebase-messaging-sw.js] Push event received', event); - if (event.data) { try { const data = event.data.json(); @@ -129,15 +69,3 @@ self.addEventListener('push', function(event) { } } }); - -// Error handling -self.addEventListener('error', function(event) { - console.error('[firebase-messaging-sw.js] Service Worker error:', event); -}); - -// Fetch event handler for offline support (optional) -self.addEventListener('fetch', function(event) { - // You can add offline caching strategies here if needed - // For now, just pass through the request - return; -});
\ No newline at end of file diff --git a/site/src/components/NotificationButton.tsx b/site/src/components/NotificationButton.tsx index 8f4fb61..66109a3 100644 --- a/site/src/components/NotificationButton.tsx +++ b/site/src/components/NotificationButton.tsx @@ -25,7 +25,7 @@ export default function NotificationButton({ className = "", isMoe = false }: No if ('serviceWorker' in navigator) { const registration = await navigator.serviceWorker.getRegistration('/firebase-messaging-sw.js'); setIsRegistered(!!registration); - + // Initialize foreground notifications if already registered if (registration && Notification.permission === "granted") { initializeForegroundNotifications(); @@ -45,15 +45,23 @@ export default function NotificationButton({ className = "", isMoe = false }: No setPermission(permissionResult); if (permissionResult === "granted") { - // Register service worker - const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js'); - console.log("Service Worker registered:", registration); - const token = await getToken(messaging, { vapidKey: VAPID_KEY }); + let registration = await navigator.serviceWorker.getRegistration('/firebase-messaging-sw.js'); + if (!registration) { + registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js'); + console.log("Service Worker registered:", registration); + } else { + console.log("Reusing existing Service Worker:", registration); + } + + const token = await getToken(messaging, { + vapidKey: VAPID_KEY, + serviceWorkerRegistration: registration, + }); console.log("FCM Token:", token); - // Store token locally (you might want to send this to your server) + localStorage.setItem('fcm_token', token); - // Initialize foreground notification handler + // Foreground notification listener initializeForegroundNotifications(); setIsRegistered(true); @@ -76,6 +84,7 @@ export default function NotificationButton({ className = "", isMoe = false }: No await deleteToken(messaging); console.log("FCM token deleted"); localStorage.removeItem('fcm_token'); + if ('serviceWorker' in navigator) { const registration = await navigator.serviceWorker.getRegistration('/firebase-messaging-sw.js'); if (registration) { @@ -93,7 +102,7 @@ export default function NotificationButton({ className = "", isMoe = false }: No } }; - // Determine button state and action + // Determine button content const getButtonContent = () => { if (loading) { return ( @@ -141,7 +150,6 @@ export default function NotificationButton({ className = "", isMoe = false }: No const handleClick = () => { if (permission === "denied") { - // Can't re-request permission if denied alert("Notifications are blocked. Please enable them in your browser settings."); return; } diff --git a/site/src/firebase.ts b/site/src/firebase.ts index e908e58..af408fb 100644 --- a/site/src/firebase.ts +++ b/site/src/firebase.ts @@ -13,69 +13,43 @@ const firebaseConfig = { const app = initializeApp(firebaseConfig); export const messaging: Messaging = getMessaging(app); +let foregroundInitialized = false; // Handle foreground messages export const initializeForegroundNotifications = () => { - onMessage(messaging, (payload) => { - console.log('[firebase.ts] Message received in foreground:', payload); - - // Check if browser supports notifications - if (!("Notification" in window)) { - console.log("This browser does not support desktop notifications"); - return; - } + if (foregroundInitialized) return; // Prevent double registration + foregroundInitialized = true; - // Check notification permission - if (Notification.permission === "granted") { - // Create notification - const notificationTitle = payload.notification?.title || 'New Update'; - const notificationOptions: NotificationOptions = { - body: payload.notification?.body || 'You have a new notification', - icon: payload.notification?.icon || '/android/android-launchericon-192-192.png', - badge: '/android/android-launchericon-72-72.png', - tag: payload.data?.tag || 'default-tag', - requireInteraction: payload.data?.requireInteraction === 'true', - silent: false, - data: { - url: payload.data?.url || '/', - gameId: payload.data?.gameId, - ...payload.data - } - }; - - // Add image if provided - if (payload.notification?.image) { - notificationOptions.badge = payload.notification.image; - } + onMessage(messaging, (payload) => { + console.log("[firebase.ts] Foreground message received", payload); - // Create and show the notification - const notification = new Notification(notificationTitle, notificationOptions); + if (Notification.permission !== "granted") return; - // Handle notification click - notification.onclick = (event) => { - event.preventDefault(); - notification.close(); + const data = payload.data || {}; + const title = data.title || "New Update"; + const options: NotificationOptions = { + body: data.body || "You have a new notification", + icon: data.icon || "/android/android-launchericon-192-192.png", + badge: data.badge || "/android/android-launchericon-72-72.png", + tag: data.tag || "default-tag", + requireInteraction: data.requireInteraction === "true", + silent: false, + data, + }; - // Navigate to the URL if provided - const url = payload.data?.url || '/'; - window.open(url, '_blank'); - }; + const notification = new Notification(title, options); - // Handle notification error - notification.onerror = (event) => { - console.error('[firebase.ts] Notification error:', event); - }; + notification.onclick = (event) => { + event.preventDefault(); + notification.close(); + const url = data.url || "/"; + window.open(url, "_blank"); + }; - // Auto-close notification after 10 seconds if not require interaction - if (payload.data?.requireInteraction !== 'true') { - setTimeout(() => { - notification.close(); - }, 10000); - } - } else { - console.log('[firebase.ts] Notification permission not granted'); + if (data.requireInteraction !== "true") { + setTimeout(() => notification.close(), 10000); } }); - console.log('[firebase.ts] Foreground notification handler initialized'); + console.log("[firebase.ts] Foreground notification handler initialized"); }; |
