'use client'; import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react'; import { useEffectiveDriverId } from '@/lib/currentDriver'; import type { Notification } from '@core/notifications/application'; import ToastNotification from './ToastNotification'; import ModalNotification from './ModalNotification'; interface NotificationContextValue { notifications: Notification[]; unreadCount: number; toastNotifications: Notification[]; modalNotification: Notification | null; markAsRead: (notification: Notification) => Promise; dismissToast: (notification: Notification) => void; respondToModal: (notification: Notification, actionId?: string) => Promise; dismissModal: (notification: Notification) => Promise; } const NotificationContext = createContext(null); export function useNotifications() { const context = useContext(NotificationContext); if (!context) { throw new Error('useNotifications must be used within NotificationProvider'); } return context; } interface NotificationProviderProps { children: ReactNode; } export default function NotificationProvider({ children }: NotificationProviderProps) { const [notifications, setNotifications] = useState([]); const [toastNotifications, setToastNotifications] = useState([]); const [modalNotification, setModalNotification] = useState(null); const [seenNotificationIds, setSeenNotificationIds] = useState>(new Set()); const currentDriverId = useEffectiveDriverId(); // Poll for new notifications // TODO // useEffect(() => { // const loadNotifications = async () => { // try { // const repo = getNotificationRepository(); // const allNotifications = await repo.findByRecipientId(currentDriverId); // setNotifications(allNotifications); // // Check for new notifications that need toast/modal display // allNotifications.forEach((notification) => { // // Check both unread and action_required status for modals // const shouldDisplay = (notification.isUnread() || notification.isActionRequired()) && // !seenNotificationIds.has(notification.id); // if (shouldDisplay) { // // Mark as seen to prevent duplicate displays // setSeenNotificationIds((prev) => new Set([...prev, notification.id])); // // Handle based on urgency // if (notification.isModal()) { // // Modal takes priority - show immediately // setModalNotification(notification); // } else if (notification.isToast()) { // // Add to toast queue // setToastNotifications((prev) => [...prev, notification]); // } // // Silent notifications just appear in the notification center // } // }); // } catch (error) { // console.error('Failed to load notifications:', error); // } // }; // loadNotifications(); // // Poll every 2 seconds for responsiveness // const interval = setInterval(loadNotifications, 2000); // return () => clearInterval(interval); // }, [currentDriverId, seenNotificationIds]); // Prevent body scroll when modal is open useEffect(() => { if (modalNotification) { document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = ''; }; } }, [modalNotification]); const markAsRead = useCallback(async (notification: Notification) => { try { const markRead = getMarkNotificationReadUseCase(); await markRead.execute({ notificationId: notification.id, recipientId: currentDriverId, }); setNotifications((prev) => prev.map((n) => (n.id === notification.id ? n.markAsRead() : n)) ); } catch (error) { console.error('Failed to mark notification as read:', error); } }, [currentDriverId]); const dismissToast = useCallback((notification: Notification) => { setToastNotifications((prev) => prev.filter((n) => n.id !== notification.id)); }, []); const respondToModal = useCallback(async (notification: Notification, actionId?: string) => { try { // Mark as responded const repo = getNotificationRepository(); const updated = notification.markAsResponded(actionId); await repo.update(updated); // Update local state setNotifications((prev) => prev.map((n) => (n.id === notification.id ? updated : n)) ); // Clear modal setModalNotification(null); } catch (error) { console.error('Failed to respond to notification:', error); } }, []); const dismissModal = useCallback(async (notification: Notification) => { try { // Dismiss the notification const repo = getNotificationRepository(); const updated = notification.dismiss(); await repo.update(updated); // Update local state setNotifications((prev) => prev.map((n) => (n.id === notification.id ? updated : n)) ); // Clear modal setModalNotification(null); } catch (error) { console.error('Failed to dismiss notification:', error); } }, []); const unreadCount = notifications.filter((n) => n.isUnread() || n.isActionRequired()).length; const value: NotificationContextValue = { notifications, unreadCount, toastNotifications, modalNotification, markAsRead, dismissToast, respondToModal, dismissModal, }; return ( {children} {/* Toast notifications container */}
{toastNotifications.map((notification) => ( ))}
{/* Modal notification */} {modalNotification && ( )}
); }