155 lines
4.6 KiB
TypeScript
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>
|
|
);
|
|
}
|