7.4 KiB
7.4 KiB
Push Notifications Setup Guide
Quick Start
1. Install Dependencies
npm install
This will install web-push which is now in package.json.
2. Generate VAPID Keys
npm run vapid:generate
Copy the output and add to your .env file:
VAPID_PUBLIC_KEY=your-generated-public-key
VAPID_PRIVATE_KEY=your-generated-private-key
VAPID_SUBJECT=mailto:your-email@example.com
3. Update Database Schema
npx prisma db push
This adds the PushSubscription table to your database.
4. Add Notification Settings to Profile Page
Add the NotificationSettings component to your profile page:
import NotificationSettings from '@/components/NotificationSettings';
export default function ProfilePage() {
return (
<div>
{/* Your existing profile content */}
<NotificationSettings />
</div>
);
}
5. Integrate Notifications into Your Actions
Update your existing action files to send notifications:
In lib/actions.ts (or wherever you handle items):
import { notifyItemAdded, notifyItemRemoved, notifyItemCompleted } from '@/lib/notification-helpers';
// After creating an item:
export async function createItem(listId: string, data: ItemData) {
// ... your existing code to create item
const item = await prisma.item.create({ ... });
// Send notification
await notifyItemAdded(listId, item.title, session.user.id);
return item;
}
// After deleting an item:
export async function deleteItem(itemId: string) {
const item = await prisma.item.findUnique({ where: { id: itemId } });
// ... your existing delete code
if (item) {
await notifyItemRemoved(item.listId, item.title, session.user.id);
}
}
// After completing an item:
export async function toggleItemComplete(itemId: string) {
const item = await prisma.item.update({ ... });
if (item.completed) {
await notifyItemCompleted(item.listId, item.title, session.user.id);
}
}
In lib/sharing-actions.ts:
import { notifyListInvitation, notifyInvitationAccepted, notifyRemovedFromList } from '@/lib/notification-helpers';
// After creating an invitation:
export async function inviteToList(listId: string, email: string, permission: string) {
// ... your existing code
const invitation = await prisma.invitation.create({ ... });
// Send notification
await notifyListInvitation(invitation.id, email);
return invitation;
}
// After accepting an invitation:
export async function acceptInvitation(invitationId: string) {
// ... your existing code
await notifyInvitationAccepted(invitationId);
}
// After removing a collaborator:
export async function removeCollaborator(listId: string, userId: string) {
const list = await prisma.list.findUnique({ where: { id: listId } });
// ... your existing code
if (list) {
await notifyRemovedFromList(userId, list.title, session.user.id);
}
}
How It Works
Architecture
- Client requests permission → User clicks "Enable" in NotificationSettings
- Service worker subscribes → Browser creates a push subscription
- Subscription saved to DB → Stored in PushSubscription table
- Events trigger notifications → Server sends push via web-push library
- Service worker displays → User sees notification
Self-Hosted Solution
This implementation uses VAPID (Voluntary Application Server Identification), which means:
- ✅ No external services required (no Firebase, no third-party APIs)
- ✅ Works entirely from your own server
- ✅ Free and open-source
- ✅ Full control over your data
Browser Support
- ✅ Chrome/Edge (Desktop & Android)
- ✅ Firefox (Desktop & Android)
- ✅ Safari 16.4+ (iOS & macOS) - Must be installed to home screen on iOS
- ❌ Safari < 16.4
Testing
Local Testing (Desktop)
- Start your dev server:
npm run dev - Open Chrome DevTools → Application → Service Workers
- Go to your profile page and enable notifications
- Trigger an action (add item, invite someone, etc.)
- You should see a notification appear
iOS Testing (Real Device Required)
- Deploy to a server with HTTPS (required for PWA)
- Open Safari on iOS 16.4+
- Tap Share → Add to Home Screen
- Open the installed app
- Enable notifications in profile
- Test by triggering actions from another device
Testing Push Manually
Use Chrome DevTools to simulate a push:
- DevTools → Application → Service Workers
- Find your service worker
- Click "Push" to simulate receiving a notification
Notification Events
The following events trigger notifications:
| Event | Recipient | Trigger |
|---|---|---|
| Item Added | All list collaborators (except adder) | Someone adds an item to a shared list |
| Item Removed | All list collaborators (except remover) | Someone removes an item |
| Item Completed | All list collaborators (except completer) | Someone marks an item complete |
| List Invitation | Invitee | Someone invites you to a list |
| Invitation Accepted | Inviter | Someone accepts your invitation |
| Removed from List | Removed user | Owner removes you from a list |
Customization
Notification Content
Edit lib/notification-helpers.ts to customize notification titles, bodies, and icons.
Notification Preferences
To add per-list notification preferences:
- Add a
notificationsEnabledfield to theCollaboratormodel - Update
NotificationSettingsto show per-list toggles - Check this field in
notifyListCollaboratorsbefore sending
Notification Sounds
Add to service worker push event:
const options = {
// ... existing options
silent: false,
sound: '/notification-sound.mp3',
};
Troubleshooting
"Push notifications not configured" error
- Check that VAPID keys are in
.env - Restart your dev server after adding keys
Notifications not appearing
- Check browser permission (should be "granted")
- Verify service worker is active (DevTools → Application)
- Check browser console for errors
- Ensure you're testing on HTTPS (or localhost)
iOS notifications not working
- Must be iOS 16.4 or later
- Must be installed to home screen (not in Safari browser)
- Check Settings → [Your App] → Notifications
Subscription fails
- Verify VAPID public key matches between client and server
- Check service worker is registered and active
- Ensure you're on HTTPS (or localhost)
Security Notes
- ⚠️ Never commit VAPID_PRIVATE_KEY to git
- ✅ VAPID keys are like JWT secrets - keep them secure
- ✅ Subscriptions are tied to users - no cross-user notification access
- ✅ Expired subscriptions are automatically cleaned up
Production Checklist
- Generate production VAPID keys
- Add VAPID keys to production environment variables
- Run database migration:
npx prisma db push - Test on actual iOS device (if targeting iOS)
- Verify HTTPS is working (required for push)
- Test notification delivery
- Monitor for expired subscriptions in logs
Performance
- Notifications are sent asynchronously (non-blocking)
- Failed subscriptions are automatically removed
- Multiple devices per user are supported
- TTL is set to 24 hours (notifications expire if not delivered)
Next Steps
Consider adding:
- Notification preferences per list
- Quiet hours (don't notify during certain times)
- Notification history/inbox
- Rich notifications with action buttons
- Notification grouping/batching if