'use client'; import { useState, useEffect, useCallback } from 'react'; import { toast } from 'sonner'; import { useSignalRContext } from '@/contexts/signalrProvider'; import { fetchApi } from '@/lib/utils/client'; import { NotificationItem, NotePreview } from '@/types/notification'; import { NotificationReadRequest } from '@/types/request/notification/read'; export type { NotificationItem }; export function useNotification() { const { appConnection } = useSignalRContext(); const [unreadNotifCount, setUnreadNotifCount] = useState(0); const [unreadNoteCount, setUnreadNoteCount] = useState(0); const [latestNotification, setLatestNotification] = useState(null); const [latestNote, setLatestNote] = useState(null); useEffect(() => { if (!appConnection) { return; } // AppHub(/hubs/app)에서 알림/쪽지 이벤트 수신 appConnection.on('ReceiveNotification', (data: NotificationItem) => { setLatestNotification(data); setUnreadNotifCount(prev => prev + 1); // 하단 우측 toast (sonner) — 알림 ID 로 중복 방지 toast.info(data.title, { id: `notif-${data.id}`, description: data.message, ...(data.actionUrl ? { action: { label: '이동', onClick: () => { window.location.href = data.actionUrl!; } } } : {}) }); }); appConnection.on('ReceiveNote', (data: NotePreview) => { setLatestNote(data); setUnreadNoteCount(prev => prev + 1); toast.info('새 쪽지가 도착했어요', { id: `note-${data.id}`, description: `${data.senderName ?? '알 수 없음'}: ${data.title}`, action: { label: '쪽지함', onClick: () => { window.location.href = '/note/inbox'; } } }); }); appConnection.on('ReceiveUnreadCounts', (notifCount: number, noteCount: number) => { setUnreadNotifCount(notifCount); setUnreadNoteCount(noteCount); }); return () => { appConnection.off('ReceiveNotification'); appConnection.off('ReceiveNote'); appConnection.off('ReceiveUnreadCounts'); }; }, [appConnection]); // 마운트 시 미읽음 카운트 초기 fetch — SignalR ReceiveUnreadCounts 는 새 알림 발생 시에만 오므로 // 페이지 진입 시점의 기존 미읽음 수는 이 endpoint 로 보정. useEffect(() => { fetchApi<{ unreadNotif: number; unreadNote: number }>('/api/notification/unread-counts', { silent: true }).then(res => { if (res.success && res.data) { setUnreadNotifCount(res.data.unreadNotif); setUnreadNoteCount(res.data.unreadNote); } }).catch(() => {}); }, []); const markNotifRead = useCallback(async (notificationID?: number) => { await fetchApi('/api/notification/read', { method: 'POST', body: { notificationID } as NotificationReadRequest, silent: true }); if (notificationID) { setUnreadNotifCount(prev => Math.max(0, prev - 1)); } else { setUnreadNotifCount(0); } }, []); const clearLatestNotification = useCallback(() => setLatestNotification(null), []); const clearLatestNote = useCallback(() => setLatestNote(null), []); return { unreadNotifCount, unreadNoteCount, latestNotification, latestNote, markNotifRead, clearLatestNotification, clearLatestNote }; }