- 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.
77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
'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>
|
|
);
|
|
}
|