import React, { createContext, useContext, useState, useEffect, useRef, useCallback, type ReactNode, } from "react"; import { getClientNotifications, markNotificationsRead } from "../api/api"; import type { ClientNotification } from "../api/api"; import { getToken } from "../auth/tokenStorage"; interface ToastData { message: string; type: "success" | "error" | "warning" | "info"; } interface NotificationContextType { notifications: ClientNotification[]; unreadCount: number; markAllRead: () => Promise; refreshNotifications: () => Promise; toast: ToastData | null; clearToast: () => void; navigateToOrder: number | null; clearNavigateToOrder: () => void; } const NotificationContext = createContext({ notifications: [], unreadCount: 0, markAllRead: async () => {}, refreshNotifications: async () => {}, toast: null, clearToast: () => {}, navigateToOrder: null, clearNavigateToOrder: () => {}, }); export function useNotifications() { return useContext(NotificationContext); } function getToastType( notifType: string, ): "success" | "error" | "warning" | "info" { switch (notifType) { case "assigned": return "success"; case "en_route": return "info"; case "livre": return "success"; case "failed": return "error"; default: return "info"; } } export function NotificationProvider({ children }: { children: ReactNode }) { const [notifications, setNotifications] = useState([]); const [unreadCount, setUnreadCount] = useState(0); const [toast, setToast] = useState(null); const [navigateToOrder, setNavigateToOrder] = useState(null); const seenIdsRef = useRef>(new Set()); const isFirstLoadRef = useRef(true); const clearToast = useCallback(() => setToast(null), []); const clearNavigateToOrder = useCallback(() => setNavigateToOrder(null), []); const showToast = useCallback( (message: string, type: ToastData["type"]) => { setToast({ message, type }); setTimeout(() => setToast(null), 5000); }, [], ); const fetchNotifications = useCallback(async () => { const token = await getToken(); if (!token) return; try { const response = await getClientNotifications(); if (response.success) { const newNotifs = response.notifications; setNotifications(newNotifs); setUnreadCount(response.unread_count); if (!isFirstLoadRef.current) { for (const notif of newNotifs) { if (notif.read) continue; const key = `${notif.command_id}-${notif.type}-${notif.created_at}`; if (!seenIdsRef.current.has(key)) { seenIdsRef.current.add(key); showToast(notif.message, getToastType(notif.type)); } } } else { for (const notif of newNotifs) { const key = `${notif.command_id}-${notif.type}-${notif.created_at}`; seenIdsRef.current.add(key); } isFirstLoadRef.current = false; } } } catch { // Silencieux } }, [showToast]); const markAllRead = useCallback(async () => { const token = await getToken(); if (!token) return; try { const response = await markNotificationsRead(); if (response.success) { setUnreadCount(0); setNotifications((prev) => prev.map((n) => ({ ...n, read: true })), ); } } catch { // Silencieux } }, []); // Polling toutes les 15 secondes useEffect(() => { fetchNotifications(); const interval = setInterval(fetchNotifications, 15000); return () => clearInterval(interval); }, [fetchNotifications]); return ( {children} ); }