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:
76
components/NotificationSettings.tsx
Normal file
76
components/NotificationSettings.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
'use client';
|
||||
|
||||
import { usePushNotifications } from '@/hooks/usePushNotifications';
|
||||
import { Bell, BellOff } from 'lucide-react';
|
||||
|
||||
export default function NotificationSettings() {
|
||||
const { permission, isSubscribed, isLoading, error, subscribe, unsubscribe } =
|
||||
usePushNotifications();
|
||||
|
||||
const handleToggle = async () => {
|
||||
if (isSubscribed) {
|
||||
await unsubscribe();
|
||||
} else {
|
||||
await subscribe();
|
||||
}
|
||||
};
|
||||
|
||||
// Don't show if notifications aren't supported
|
||||
if (!('Notification' in window) || !('serviceWorker' in navigator)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold text-gray-900 flex items-center gap-2">
|
||||
{isSubscribed ? <Bell className="w-5 h-5" /> : <BellOff className="w-5 h-5" />}
|
||||
Push Notifications
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mt-1">
|
||||
Get notified when someone adds items, shares lists, or invites you to collaborate
|
||||
</p>
|
||||
|
||||
{permission === 'denied' && (
|
||||
<div className="mt-3 p-3 bg-red-50 border border-red-200 rounded-md">
|
||||
<p className="text-sm text-red-800">
|
||||
Notifications are blocked. Please enable them in your browser settings.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="mt-3 p-3 bg-red-50 border border-red-200 rounded-md">
|
||||
<p className="text-sm text-red-800">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isSubscribed && (
|
||||
<div className="mt-3 p-3 bg-green-50 border border-green-200 rounded-md">
|
||||
<p className="text-sm text-green-800">
|
||||
✓ You'll receive notifications on this device
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleToggle}
|
||||
disabled={isLoading || permission === 'denied'}
|
||||
className={`ml-4 px-4 py-2 rounded-md font-medium transition-colors ${
|
||||
isSubscribed
|
||||
? 'bg-gray-200 text-gray-700 hover:bg-gray-300'
|
||||
: 'bg-blue-600 text-white hover:bg-blue-700'
|
||||
} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||
>
|
||||
{isLoading ? 'Loading...' : isSubscribed ? 'Disable' : 'Enable'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-xs text-gray-500">
|
||||
<p>💡 Tip: Install this app to your home screen for the best notification experience</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user