Files
Todo/PUSH_NOTIFICATIONS_SETUP.md
2025-12-07 01:31:33 +00:00

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

  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:

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