feat: Implement push notification system for admin announcements
- Add a notification manager to handle push notifications and integrate with Nostr events. - Create a push notification service to manage subscription and permission requests. - Introduce components for notification settings and permission prompts in the UI. - Update Nostr store to manage push notification state and enable/disable functionality. - Enhance NostrFeed to send notifications for new admin announcements. - Implement test notification functionality for development purposes.
This commit is contained in:
parent
6c1caac84b
commit
c05f40f1ec
17 changed files with 1316 additions and 13 deletions
126
public/sw.js
Normal file
126
public/sw.js
Normal file
|
|
@ -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
|
||||
})
|
||||
}
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue