aboutsummaryrefslogtreecommitdiffstats
path: root/site
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-10-03 02:57:04 -0700
committerPinapelz <yukais@pinapelz.com>2025-10-03 02:57:04 -0700
commit98ead632b7715b8f1768c962a37b9efa0a684484 (patch)
treeb606131dbc389cd9a7f9a60645cf1000342abb58 /site
parent3d1d33c2aac15e07c3b840a1fb9428e3feda8330 (diff)
fix: double notification bug
Diffstat (limited to 'site')
-rw-r--r--site/public/firebase-messaging-sw.js118
-rw-r--r--site/src/components/NotificationButton.tsx26
-rw-r--r--site/src/firebase.ts80
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");
};
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage