import { useState, useEffect, useCallback, useRef } from 'react'; import { useSession } from 'next-auth/react'; import { Notification, NotificationCount } from '@/lib/types/notification'; import { useUnifiedRefresh } from './use-unified-refresh'; import { REFRESH_INTERVALS } from '@/lib/constants/refresh-intervals'; import { requestDeduplicator } from '@/lib/utils/request-deduplication'; // Default empty notification count const defaultNotificationCount: NotificationCount = { total: 0, unread: 0, sources: {} }; export function useNotifications() { const { data: session, status } = useSession(); const [notifications, setNotifications] = useState([]); const [notificationCount, setNotificationCount] = useState(defaultNotificationCount); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const isMountedRef = useRef(false); // Fetch notification count with request deduplication const fetchNotificationCount = useCallback(async (force = false) => { if (!session?.user || !isMountedRef.current) return; try { setError(null); console.log('[useNotifications] Fetching notification count', { force }); // Use request deduplication to prevent duplicate calls const requestKey = `notifications-count-${session.user.id}`; const url = force ? `/api/notifications/count?_t=${Date.now()}` : '/api/notifications/count'; const data = await requestDeduplicator.execute( requestKey, async () => { const response = await fetch(url, { credentials: 'include', cache: force ? 'no-store' : 'default', }); if (!response.ok) { const errorText = await response.text(); console.error('Failed to fetch notification count:', { status: response.status, body: errorText }); throw new Error(errorText || 'Failed to fetch notification count'); } return response.json(); }, 2000 // 2 second deduplication window ); if (isMountedRef.current) { console.log('[useNotifications] Received notification count:', data); setNotificationCount(data); } } catch (err: any) { console.error('Error fetching notification count:', err); if (isMountedRef.current) { setError(err.message || 'Failed to fetch notification count'); } } }, [session?.user]); // Fetch notifications with request deduplication const fetchNotifications = useCallback(async (page = 1, limit = 20) => { if (!session?.user || !isMountedRef.current) return; setLoading(true); setError(null); try { console.log('[useNotifications] Fetching notifications', { page, limit }); // Use request deduplication to prevent duplicate calls const requestKey = `notifications-${session.user.id}-${page}-${limit}`; const data = await requestDeduplicator.execute( requestKey, async () => { const response = await fetch(`/api/notifications?page=${page}&limit=${limit}`, { credentials: 'include' }); if (!response.ok) { const errorText = await response.text(); console.error('Failed to fetch notifications:', { status: response.status, body: errorText }); throw new Error(errorText || 'Failed to fetch notifications'); } return response.json(); }, 2000 // 2 second deduplication window ); if (isMountedRef.current) { setNotifications(data.notifications); } } catch (err: any) { console.error('Error fetching notifications:', err); if (isMountedRef.current) { setError(err.message || 'Failed to fetch notifications'); } } finally { if (isMountedRef.current) { setLoading(false); } } }, [session?.user]); // Use unified refresh system for notification count const { refresh: refreshCount } = useUnifiedRefresh({ resource: 'notifications-count', interval: REFRESH_INTERVALS.NOTIFICATIONS_COUNT, enabled: status === 'authenticated', onRefresh: async () => { await fetchNotificationCount(true); // Force refresh to bypass cache }, priority: 'high', }); // Listen for custom events to trigger immediate refresh useEffect(() => { if (status !== 'authenticated') return; const handleNotificationTrigger = () => { console.log('[useNotifications] Received notification trigger event'); fetchNotificationCount(true); }; // Listen for custom event from widgets window.addEventListener('trigger-notification-refresh', handleNotificationTrigger); return () => { window.removeEventListener('trigger-notification-refresh', handleNotificationTrigger); }; }, [status, fetchNotificationCount]); // Initialize fetching on component mount and cleanup on unmount useEffect(() => { isMountedRef.current = true; if (status === 'authenticated' && session?.user) { // Initial fetches fetchNotificationCount(true); fetchNotifications(); } return () => { isMountedRef.current = false; }; }, [status, session?.user, fetchNotificationCount, fetchNotifications]); return { notifications, notificationCount, loading, error, fetchNotifications, fetchNotificationCount: () => fetchNotificationCount(true), }; }