diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..4daad6b --- /dev/null +++ b/public/sw.js @@ -0,0 +1,126 @@ +// Custom service worker for push notifications +// This will be merged with Workbox generated SW + +import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching' +import { clientsClaim, skipWaiting } from 'workbox-core' + +// Precache and route static assets +precacheAndRoute(self.__WB_MANIFEST) +cleanupOutdatedCaches() + +// Take control of all pages immediately +skipWaiting() +clientsClaim() + +// Push notification event handler +self.addEventListener('push', (event) => { + console.log('Push event received:', event) + + let notificationData = { + title: 'New Announcement', + body: 'You have a new admin announcement', + icon: '/pwa-192x192.png', + badge: '/pwa-192x192.png', + data: { + url: '/', + timestamp: Date.now() + }, + tag: 'admin-announcement', + requireInteraction: true, + actions: [ + { + action: 'view', + title: 'View', + icon: '/pwa-192x192.png' + }, + { + action: 'dismiss', + title: 'Dismiss' + } + ] + } + + // Parse push data if available + if (event.data) { + try { + const pushData = event.data.json() + notificationData = { + ...notificationData, + ...pushData + } + } catch (error) { + console.warn('Failed to parse push data:', error) + // Use default notification data + } + } + + event.waitUntil( + self.registration.showNotification(notificationData.title, notificationData) + ) +}) + +// Notification click handler +self.addEventListener('notificationclick', (event) => { + console.log('Notification clicked:', event) + + event.notification.close() + + const action = event.action + const notificationData = event.notification.data || {} + + if (action === 'dismiss') { + return // Just close the notification + } + + // Default action or 'view' action - open the app + const urlToOpen = notificationData.url || '/' + + event.waitUntil( + clients.matchAll({ type: 'window', includeUncontrolled: true }) + .then((clientList) => { + // Try to find an existing window with the app + for (const client of clientList) { + if (client.url.includes(self.location.origin) && 'focus' in client) { + client.focus() + // Navigate to the notification URL if different + if (client.url !== urlToOpen) { + client.navigate(urlToOpen) + } + return + } + } + // If no existing window, open a new one + if (clients.openWindow) { + return clients.openWindow(urlToOpen) + } + }) + ) +}) + +// Background sync for offline notification queue (future enhancement) +self.addEventListener('sync', (event) => { + if (event.tag === 'notification-queue') { + event.waitUntil( + // Process any queued notifications when back online + console.log('Background sync: notification-queue') + ) + } +}) + +// Message handler for communication with main app +self.addEventListener('message', (event) => { + console.log('Service worker received message:', event.data) + + if (event.data && event.data.type === 'SHOW_NOTIFICATION') { + const { title, body, data } = event.data.payload + + self.registration.showNotification(title, { + body, + icon: '/pwa-192x192.png', + badge: '/pwa-192x192.png', + data, + tag: 'manual-notification', + requireInteraction: false + }) + } +}) \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 6e5a91d..8c7f8ba 100644 --- a/src/App.vue +++ b/src/App.vue @@ -26,8 +26,8 @@ async function handleLoginSuccess() {