272 lines
7.4 KiB
Markdown
272 lines
7.4 KiB
Markdown
# Push Notifications Setup Guide
|
|
|
|
## Quick Start
|
|
|
|
### 1. Install Dependencies
|
|
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
This will install `web-push` which is now in package.json.
|
|
|
|
### 2. Generate VAPID Keys
|
|
|
|
```bash
|
|
npm run vapid:generate
|
|
```
|
|
|
|
Copy the output and add to your `.env` file:
|
|
|
|
```env
|
|
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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```tsx
|
|
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):
|
|
|
|
```typescript
|
|
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`:
|
|
|
|
```typescript
|
|
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
|
|
|
|
1. **Client requests permission** → User clicks "Enable" in NotificationSettings
|
|
2. **Service worker subscribes** → Browser creates a push subscription
|
|
3. **Subscription saved to DB** → Stored in PushSubscription table
|
|
4. **Events trigger notifications** → Server sends push via web-push library
|
|
5. **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)
|
|
|
|
1. Start your dev server: `npm run dev`
|
|
2. Open Chrome DevTools → Application → Service Workers
|
|
3. Go to your profile page and enable notifications
|
|
4. Trigger an action (add item, invite someone, etc.)
|
|
5. You should see a notification appear
|
|
|
|
### iOS Testing (Real Device Required)
|
|
|
|
1. Deploy to a server with HTTPS (required for PWA)
|
|
2. Open Safari on iOS 16.4+
|
|
3. Tap Share → Add to Home Screen
|
|
4. Open the installed app
|
|
5. Enable notifications in profile
|
|
6. Test by triggering actions from another device
|
|
|
|
### Testing Push Manually
|
|
|
|
Use Chrome DevTools to simulate a push:
|
|
|
|
1. DevTools → Application → Service Workers
|
|
2. Find your service worker
|
|
3. 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:
|
|
|
|
1. Add a `notificationsEnabled` field to the `Collaborator` model
|
|
2. Update `NotificationSettings` to show per-list toggles
|
|
3. Check this field in `notifyListCollaborators` before sending
|
|
|
|
### Notification Sounds
|
|
|
|
Add to service worker push event:
|
|
|
|
```javascript
|
|
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 |