feat: add auto-prompt for push notifications on app load
- Create NotificationPrompt component that appears 3 seconds after app loads - Add to main layout so it shows on all pages (for logged-in users) - Prompt only shows if permission not yet requested - Users can dismiss temporarily or permanently - Add slide-up animation for smooth appearance - Create simplified SETUP_PUSH_NOTIFICATIONS.md guide Users will now be automatically prompted to enable notifications instead of having to go to the profile page.
This commit is contained in:
104
components/NotificationPrompt.tsx
Normal file
104
components/NotificationPrompt.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { usePushNotifications } from '@/hooks/usePushNotifications';
|
||||
import { Bell, X } from 'lucide-react';
|
||||
|
||||
export default function NotificationPrompt() {
|
||||
const { permission, isSubscribed, subscribe } = usePushNotifications();
|
||||
const [showPrompt, setShowPrompt] = useState(false);
|
||||
const [dismissed, setDismissed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if we should show the prompt
|
||||
const shouldShow =
|
||||
'Notification' in window &&
|
||||
'serviceWorker' in navigator &&
|
||||
permission === 'default' && // Not yet asked
|
||||
!isSubscribed && // Not subscribed
|
||||
!dismissed && // Not dismissed this session
|
||||
!localStorage.getItem('notification-prompt-dismissed'); // Not permanently dismissed
|
||||
|
||||
if (shouldShow) {
|
||||
// Show prompt after a short delay (better UX)
|
||||
const timer = setTimeout(() => {
|
||||
setShowPrompt(true);
|
||||
}, 3000); // 3 seconds after app loads
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [permission, isSubscribed, dismissed]);
|
||||
|
||||
const handleEnable = async () => {
|
||||
const success = await subscribe();
|
||||
if (success) {
|
||||
setShowPrompt(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDismiss = () => {
|
||||
setShowPrompt(false);
|
||||
setDismissed(true);
|
||||
};
|
||||
|
||||
const handleDismissForever = () => {
|
||||
localStorage.setItem('notification-prompt-dismissed', 'true');
|
||||
setShowPrompt(false);
|
||||
setDismissed(true);
|
||||
};
|
||||
|
||||
if (!showPrompt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 left-4 sm:left-auto sm:w-96 z-50 animate-slide-up">
|
||||
<div className="bg-white rounded-lg shadow-2xl border border-gray-200 p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center">
|
||||
<Bell className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-1">
|
||||
Stay Updated
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
Get notified when items are added, completed, or when you're invited to lists
|
||||
</p>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={handleEnable}
|
||||
className="flex-1 px-3 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Enable Notifications
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDismiss}
|
||||
className="px-3 py-2 text-gray-600 text-sm font-medium hover:text-gray-900 transition-colors"
|
||||
>
|
||||
Later
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleDismissForever}
|
||||
className="text-xs text-gray-500 hover:text-gray-700 mt-2 underline"
|
||||
>
|
||||
Don't ask again
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleDismiss}
|
||||
className="flex-shrink-0 text-gray-400 hover:text-gray-600 transition-colors"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user