Overview
Add Blazor client-side push notification support: service worker handlers, JS interop, subscription synchronization, and a notification settings page. Follows the Recollections baseline (neptuo/Recollections#395) adapted for Money's Blazor PWA architecture.
Service Worker — Push Event Handling
Update service-worker.published.js
Add push notification event listeners:
self.addEventListener('push', event => event.waitUntil(onPush(event)));
self.addEventListener('notificationclick', event => event.waitUntil(onNotificationClick(event)));
async function onPush(event) {
const data = event.data?.json() || {};
await self.registration.showNotification(data.title || 'Money', {
body: data.body || 'You have scheduled expenses due today.',
icon: '/icon-192.png',
badge: '/icon-badge-96.png',
tag: data.tag || 'money-expense-template',
renotify: true,
data: { url: data.url || '/expense-templates' }
});
}
async function onNotificationClick(event) {
event.notification.close();
const url = event.notification.data?.url || '/\;
const clients = await self.clients.matchAll({ type: 'window' });
for (const client of clients) {
if (client.url.includes(url) && 'focus' in client) return client.focus();
}
if (self.clients.openWindow) return self.clients.openWindow(url);
}
Badge Asset
- Add
icon-badge-96.png (monochrome 96×96) for Android notification badge
JS Interop Layer
New JS Module (wwwroot/js/notifications.js)
Money.Notifications = {
isSupported: () => 'serviceWorker' in navigator && 'PushManager' in window,
getPermission: () => Notification.permission,
getTimeZone: () => Intl.DateTimeFormat().resolvedOptions().timeZone,
getSubscription: async () => { /* from SW registration */ },
subscribe: async (publicKey) => { /* PushManager.subscribe with applicationServerKey */ },
unsubscribe: async () => { /* get subscription, unsubscribe() */ }
};
C# Interop (Money.Blazor.Host/Services/PushNotificationInterop.cs)
IsSupportedAsync() — Check browser Push API support
GetPermissionAsync() — Returns "granted" | "denied" | "default"
GetTimeZoneAsync() — Browser timezone for preferred hour calculation
GetSubscriptionAsync() — Current push subscription (if any)
SubscribeAsync(publicKey) — Request browser permission + subscribe
UnsubscribeAsync() — Revoke browser subscription
Subscription Synchronizer
NotificationSubscriptionSynchronizer.cs
- On app start: auto-restore subscription if permission granted + localStorage flag set
- On explicit enable: subscribe browser → send to API → set localStorage flag
- On disable: unsubscribe browser → send revoke to API → clear flag
- On app update (version change): re-subscribe to refresh endpoint
Notification Settings Page
New Page: /notifications
File: Money.Blazor.Host/Pages/Notifications.razor
UI Elements:
- Global notifications toggle (
IsEnabled)
- "Scheduled expenses" section:
- Toggle (
IsEnabled)
- Preferred time picker (hour selector, 0-23)
- Timezone display (auto-detected, read-only)
- Browser subscription status:
- "Enable on this browser" button (if permission not granted)
- "Subscribed ✓" indicator (if active)
- "Notifications blocked" warning (if permission denied by browser)
CQRS Integration:
- Dispatches
SetNotificationSettings, SetExpenseTemplateNotificationSettings, SubscribePushNotification, UnsubscribePushNotification
- Queries
GetNotificationSettings on page load
Navigation
Add link to notification settings from user menu/settings area.
Client-Side Registration
- Register
PushNotificationInterop and NotificationSubscriptionSynchronizer in Program.cs
- Add notification command/query URLs to client-side
CommandMapper/QueryMapper
References
Overview
Add Blazor client-side push notification support: service worker handlers, JS interop, subscription synchronization, and a notification settings page. Follows the Recollections baseline (neptuo/Recollections#395) adapted for Money's Blazor PWA architecture.
Service Worker — Push Event Handling
Update
service-worker.published.jsAdd push notification event listeners:
Badge Asset
icon-badge-96.png(monochrome 96×96) for Android notification badgeJS Interop Layer
New JS Module (
wwwroot/js/notifications.js)C# Interop (
Money.Blazor.Host/Services/PushNotificationInterop.cs)IsSupportedAsync()— Check browser Push API supportGetPermissionAsync()— Returns "granted" | "denied" | "default"GetTimeZoneAsync()— Browser timezone for preferred hour calculationGetSubscriptionAsync()— Current push subscription (if any)SubscribeAsync(publicKey)— Request browser permission + subscribeUnsubscribeAsync()— Revoke browser subscriptionSubscription Synchronizer
NotificationSubscriptionSynchronizer.csNotification Settings Page
New Page:
/notificationsFile:
Money.Blazor.Host/Pages/Notifications.razorUI Elements:
IsEnabled)IsEnabled)CQRS Integration:
SetNotificationSettings,SetExpenseTemplateNotificationSettings,SubscribePushNotification,UnsubscribePushNotificationGetNotificationSettingson page loadNavigation
Add link to notification settings from user menu/settings area.
Client-Side Registration
PushNotificationInteropandNotificationSubscriptionSynchronizerinProgram.csCommandMapper/QueryMapperReferences