Files
projet_gestion_commande/mobile/src/context/NotificationContext.tsx
T

155 lines
4.6 KiB
TypeScript

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<void>;
refreshNotifications: () => Promise<void>;
toast: ToastData | null;
clearToast: () => void;
navigateToOrder: number | null;
clearNavigateToOrder: () => void;
}
const NotificationContext = createContext<NotificationContextType>({
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<ClientNotification[]>([]);
const [unreadCount, setUnreadCount] = useState(0);
const [toast, setToast] = useState<ToastData | null>(null);
const [navigateToOrder, setNavigateToOrder] = useState<number | null>(null);
const seenIdsRef = useRef<Set<string>>(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 (
<NotificationContext.Provider
value={{
notifications,
unreadCount,
markAllRead,
refreshNotifications: fetchNotifications,
toast,
clearToast,
navigateToOrder,
clearNavigateToOrder,
}}
>
{children}
</NotificationContext.Provider>
);
}