| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- '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<NotificationItem|null>(null);
- const [latestNote, setLatestNote] = useState<NotePreview|null>(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
- };
- }
|