feat: implement complete web push notification system
- Add self-hosted push notifications using VAPID (no external services) - Create PushSubscription model in database - Implement server-side push logic with web-push library - Add notification helpers for list events (items, invitations, collaborators) - Create NotificationSettings UI component for profile page - Add usePushNotifications React hook for subscription management - Create API routes for subscription management and VAPID key - Update service worker with push event handlers - Integrate notifications into item actions (add, remove, complete) - Integrate notifications into sharing actions (invite, accept, remove) - Add VAPID key generation script - Add comprehensive documentation and setup guides Notifications are sent when: - Items are added/removed/completed in shared lists - Users are invited to lists - Invitations are accepted - Collaborators are removed from lists Works on iOS 16.4+ (when installed to home screen), Android, and desktop browsers.
This commit is contained in:
264
PUSH_NOTIFICATIONS_COMPLETE.md
Normal file
264
PUSH_NOTIFICATIONS_COMPLETE.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# Web Push Notifications - Complete Implementation ✅
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
A complete, self-hosted Web Push notification system for your PWA shopping list app.
|
||||
|
||||
## 📦 Files Created
|
||||
|
||||
### Core Implementation
|
||||
- `lib/push-notifications.ts` - Server-side push notification logic with VAPID
|
||||
- `lib/notification-helpers.ts` - Helper functions for specific notification events
|
||||
- `hooks/usePushNotifications.ts` - React hook for managing subscriptions
|
||||
- `components/NotificationSettings.tsx` - UI component for enabling/disabling notifications
|
||||
|
||||
### API Routes
|
||||
- `app/api/push/vapid-key/route.ts` - Returns public VAPID key
|
||||
- `app/api/push/subscribe/route.ts` - Saves push subscriptions
|
||||
- `app/api/push/unsubscribe/route.ts` - Removes push subscriptions
|
||||
|
||||
### Database
|
||||
- Updated `prisma/schema.prisma` - Added `PushSubscription` model
|
||||
|
||||
### Service Worker
|
||||
- Updated `public/sw.js` - Added push event handlers
|
||||
|
||||
### Scripts & Config
|
||||
- `scripts/generate-vapid-keys.js` - Generate VAPID keys
|
||||
- Updated `package.json` - Added web-push dependency and npm script
|
||||
- Updated `.env.example` - Added VAPID key placeholders
|
||||
|
||||
### Documentation
|
||||
- `PUSH_NOTIFICATIONS_QUICK_START.md` - 5-minute setup guide
|
||||
- `PUSH_NOTIFICATIONS_SETUP.md` - Complete setup and configuration
|
||||
- `PUSH_NOTIFICATIONS_INTEGRATION_EXAMPLE.md` - Code integration examples
|
||||
- `PUSH_NOTIFICATIONS_GUIDE.md` - Architecture and testing guide
|
||||
- `PUSH_NOTIFICATIONS_COMPLETE.md` - This file
|
||||
|
||||
## 🎯 How It Works
|
||||
|
||||
### The Push Notification Server
|
||||
|
||||
**You don't need an external service!** This implementation uses **VAPID** (Voluntary Application Server Identification), which is a standard protocol that allows your own server to send push notifications directly to browsers.
|
||||
|
||||
Here's the flow:
|
||||
|
||||
1. **Generate VAPID Keys** (one-time setup)
|
||||
- Run `npm run vapid:generate`
|
||||
- These are like JWT secrets - a public/private key pair
|
||||
- Public key: shared with browsers
|
||||
- Private key: kept secret on your server
|
||||
|
||||
2. **User Subscribes**
|
||||
- User clicks "Enable" in NotificationSettings
|
||||
- Browser creates a unique push subscription (includes endpoint URL)
|
||||
- Subscription saved to your database
|
||||
|
||||
3. **Event Occurs**
|
||||
- Someone adds an item to a shared list
|
||||
- Your server action calls `notifyItemAdded()`
|
||||
- Function looks up all subscriptions for list members
|
||||
- Sends push notification using `web-push` library
|
||||
|
||||
4. **Browser Receives**
|
||||
- Browser's push service receives the notification
|
||||
- Service worker wakes up and displays it
|
||||
- User sees notification even if app is closed
|
||||
|
||||
### The "Server" is Just Your App
|
||||
|
||||
The push notification "server" is simply:
|
||||
- The `web-push` npm package (installed in your app)
|
||||
- Your VAPID keys (in your .env file)
|
||||
- Your existing Next.js API routes
|
||||
|
||||
No external service, no additional infrastructure needed!
|
||||
|
||||
## 🚀 Setup Instructions
|
||||
|
||||
### Step 1: Install Dependencies
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### Step 2: Generate VAPID Keys
|
||||
```bash
|
||||
npm run vapid:generate
|
||||
```
|
||||
|
||||
Copy the output to your `.env` file:
|
||||
```env
|
||||
VAPID_PUBLIC_KEY=BNxxx...
|
||||
VAPID_PRIVATE_KEY=xxx...
|
||||
VAPID_SUBJECT=mailto:your-email@example.com
|
||||
```
|
||||
|
||||
### Step 3: Update Database
|
||||
```bash
|
||||
npx prisma db push
|
||||
```
|
||||
|
||||
### Step 4: Add UI Component
|
||||
|
||||
In `app/profile/page.tsx`:
|
||||
```typescript
|
||||
import NotificationSettings from '@/components/NotificationSettings';
|
||||
|
||||
export default function ProfilePage() {
|
||||
return (
|
||||
<div>
|
||||
{/* Your existing content */}
|
||||
<NotificationSettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Integrate Notifications
|
||||
|
||||
In your action files (see `PUSH_NOTIFICATIONS_INTEGRATION_EXAMPLE.md` for details):
|
||||
|
||||
```typescript
|
||||
import { notifyItemAdded } from '@/lib/notification-helpers';
|
||||
|
||||
// After creating an item:
|
||||
await notifyItemAdded(listId, item.title, session.user.id);
|
||||
```
|
||||
|
||||
## 📱 Notification Events
|
||||
|
||||
| Event | Function | When |
|
||||
|-------|----------|------|
|
||||
| Item added | `notifyItemAdded()` | Someone adds item to shared list |
|
||||
| Item removed | `notifyItemRemoved()` | Someone removes item |
|
||||
| Item completed | `notifyItemCompleted()` | Someone marks item complete |
|
||||
| List invitation | `notifyListInvitation()` | Someone invites you |
|
||||
| Invitation accepted | `notifyInvitationAccepted()` | Someone accepts your invite |
|
||||
| Removed from list | `notifyRemovedFromList()` | Owner removes you |
|
||||
|
||||
## 🌐 Browser Support
|
||||
|
||||
| Platform | Support | Notes |
|
||||
|----------|---------|-------|
|
||||
| Chrome Desktop | ✅ Full | Works perfectly |
|
||||
| Chrome Android | ✅ Full | Works perfectly |
|
||||
| Firefox Desktop | ✅ Full | Works perfectly |
|
||||
| Firefox Android | ✅ Full | Works perfectly |
|
||||
| Safari macOS | ✅ 16.4+ | Works when installed |
|
||||
| Safari iOS | ✅ 16.4+ | **Must be installed to home screen** |
|
||||
| Edge | ✅ Full | Chromium-based |
|
||||
|
||||
### iOS Requirements
|
||||
- iOS 16.4 or later
|
||||
- Must use "Add to Home Screen" (won't work in Safari browser)
|
||||
- Requires HTTPS in production
|
||||
|
||||
## 🔒 Security
|
||||
|
||||
- VAPID private key must be kept secret (like JWT secret)
|
||||
- Subscriptions are tied to authenticated users
|
||||
- Expired subscriptions automatically cleaned up
|
||||
- No cross-user notification access
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### Desktop (Chrome)
|
||||
1. Start dev server: `npm run dev`
|
||||
2. Open DevTools → Application → Service Workers
|
||||
3. Enable notifications in profile
|
||||
4. Trigger an action (add item, etc.)
|
||||
5. See notification appear
|
||||
|
||||
### iOS (Real Device)
|
||||
1. Deploy with HTTPS
|
||||
2. Open in Safari
|
||||
3. Share → Add to Home Screen
|
||||
4. Open installed app
|
||||
5. Enable notifications
|
||||
6. Test from another device
|
||||
|
||||
## 📊 Database Schema
|
||||
|
||||
New table added:
|
||||
```prisma
|
||||
model PushSubscription {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
endpoint String @db.Text
|
||||
p256dh String @db.Text
|
||||
auth String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
user User @relation(...)
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 Customization
|
||||
|
||||
### Change Notification Content
|
||||
Edit `lib/notification-helpers.ts` to customize titles, bodies, icons.
|
||||
|
||||
### Add Per-List Preferences
|
||||
1. Add `notificationsEnabled` to `Collaborator` model
|
||||
2. Check this field before sending notifications
|
||||
3. Add UI toggle in list settings
|
||||
|
||||
### Add Notification Sounds
|
||||
Update service worker push event:
|
||||
```javascript
|
||||
const options = {
|
||||
...existing,
|
||||
silent: false,
|
||||
sound: '/notification-sound.mp3',
|
||||
};
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### "Push notifications not configured"
|
||||
- Verify VAPID keys are in `.env`
|
||||
- Restart dev server
|
||||
|
||||
### Notifications not appearing
|
||||
- Check browser permission (must be "granted")
|
||||
- Verify service worker is active
|
||||
- Check console for errors
|
||||
- Ensure HTTPS (or localhost)
|
||||
|
||||
### iOS not working
|
||||
- Check iOS version (16.4+)
|
||||
- Must be installed to home screen
|
||||
- Check Settings → [App] → Notifications
|
||||
|
||||
## 📈 Performance
|
||||
|
||||
- Notifications sent asynchronously (non-blocking)
|
||||
- Multiple devices per user supported
|
||||
- Automatic cleanup of expired subscriptions
|
||||
- TTL: 24 hours
|
||||
|
||||
## ✅ Production Checklist
|
||||
|
||||
- [ ] Generate production VAPID keys
|
||||
- [ ] Add to production environment variables
|
||||
- [ ] Run `npx prisma db push` on production DB
|
||||
- [ ] Verify HTTPS is working
|
||||
- [ ] Test on actual iOS device
|
||||
- [ ] Monitor logs for errors
|
||||
|
||||
## 🎉 You're Done!
|
||||
|
||||
Your app now has:
|
||||
- ✅ Self-hosted push notifications
|
||||
- ✅ No external dependencies
|
||||
- ✅ iOS and Android support
|
||||
- ✅ Multi-device support
|
||||
- ✅ Automatic subscription management
|
||||
|
||||
Users will receive notifications when:
|
||||
- Items are added/removed/completed in shared lists
|
||||
- They're invited to lists
|
||||
- Their invitations are accepted
|
||||
- They're removed from lists
|
||||
|
||||
All without any external services or additional infrastructure!
|
||||
Reference in New Issue
Block a user