hku-class/frontend/src/hooks/use-notifications.tsx
2026-04-27 09:21:20 +08:00

101 lines
2.7 KiB
TypeScript

"use client";
import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from "react";
import { useAuth } from "@/hooks/use-auth";
import { fetchAPI, putAPI } from "@/lib/api";
import type { NotificationItem, PageResponse } from "@/lib/types";
interface NotificationContextType {
unreadCount: number;
notifications: NotificationItem[];
markRead: (id: number) => Promise<void>;
markAllRead: () => Promise<void>;
refresh: () => void;
}
const NotificationContext = createContext<NotificationContextType>({
unreadCount: 0,
notifications: [],
markRead: async () => {},
markAllRead: async () => {},
refresh: () => {},
});
export function NotificationProvider({ children }: { children: ReactNode }) {
const { user } = useAuth();
const [unreadCount, setUnreadCount] = useState(0);
const [notifications, setNotifications] = useState<NotificationItem[]>([]);
const fetchUnreadCount = useCallback(async () => {
if (!user) return;
try {
const res = await fetchAPI<{ count: number }>("/api/notifications/unread-count");
setUnreadCount(res.count);
} catch {
// ignore
}
}, [user]);
const fetchNotifications = useCallback(async () => {
if (!user) return;
try {
const res = await fetchAPI<PageResponse<NotificationItem>>("/api/notifications/", { page_size: "10" });
setNotifications(res.items || []);
} catch {
// ignore
}
}, [user]);
const refresh = useCallback(() => {
void fetchUnreadCount();
void fetchNotifications();
}, [fetchUnreadCount, fetchNotifications]);
useEffect(() => {
if (!user) return;
const initialRefresh = window.setTimeout(() => {
void fetchUnreadCount();
}, 0);
const interval = setInterval(fetchUnreadCount, 30000);
return () => {
window.clearTimeout(initialRefresh);
clearInterval(interval);
};
}, [user, fetchUnreadCount]);
const markRead = useCallback(async (id: number) => {
try {
await putAPI(`/api/notifications/${id}/read`);
setNotifications((prev) =>
prev.map((n) => (n.id === id ? { ...n, is_read: true } : n))
);
setUnreadCount((c) => Math.max(0, c - 1));
} catch {
// ignore
}
}, []);
const markAllRead = useCallback(async () => {
try {
await putAPI("/api/notifications/read-all");
setNotifications((prev) => prev.map((n) => ({ ...n, is_read: true })));
setUnreadCount(0);
} catch {
// ignore
}
}, []);
return (
<NotificationContext.Provider
value={{ unreadCount, notifications, markRead, markAllRead, refresh }}
>
{children}
</NotificationContext.Provider>
);
}
export function useNotifications() {
return useContext(NotificationContext);
}