diff --git a/apps/web/src/hooks/useNotificationSSE.ts b/apps/web/src/hooks/useNotificationSSE.ts index b769c81d..62071d5e 100644 --- a/apps/web/src/hooks/useNotificationSSE.ts +++ b/apps/web/src/hooks/useNotificationSSE.ts @@ -2,12 +2,11 @@ import { fetchEventSource } from '@microsoft/fetch-event-source'; import { WEBBRIDGE_MESSAGE_TYPE } from '@piki/core'; import type { Query } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query'; -import { useRouter } from 'next/navigation'; +import { usePathname } from 'next/navigation'; import { useEffect, useRef } from 'react'; import { toast } from 'sonner'; import { ENDPOINTS } from '@/consts/api'; -import { QUERY_ACTION } from '@/consts/queryAction'; import { QUERY_KEYS } from '@/consts/queryKeys'; import { ROUTES } from '@/consts/route'; import { CLIENT_TYPE } from '@/consts/webBridge'; @@ -27,39 +26,23 @@ const isWishQueryOfItem = (query: Query, itemId: number) => query.queryKey[0] === 'wish' && (query.state.data as { item?: { id: number } } | undefined)?.item?.id === itemId; -const SCROLL_TO_LAST_QUERY = `${QUERY_ACTION.KEY}=${QUERY_ACTION.VALUE.SCROLL_TO_LAST}`; - const buildToastMessage = (payload: NotificationSsePayloadT) => payload.body ? `${payload.title} ${payload.body}` : payload.title; -const resolveDeepLink = (payload: NotificationSsePayloadT): string | null => { - const { type, refId, kind, tournamentId } = payload; - - switch (type) { - case 'TOURNAMENT_STARTED': - return ROUTES.TOURNAMENT_CREATE(refId); - case 'TOURNAMENT_JOINED': - case 'TOURNAMENT_ITEM_ADDED': - return `${ROUTES.TOURNAMENT_CREATE(refId)}?${SCROLL_TO_LAST_QUERY}`; - case 'ITEM_PARSING_COMPLETED': - case 'ITEM_PARSING_FAILED': - if (kind === 'TOURNAMENT' && tournamentId != null) { - return `${ROUTES.TOURNAMENT_CREATE(tournamentId)}?${SCROLL_TO_LAST_QUERY}`; - } - return ROUTES.WISHLIST; - default: - return null; - } -}; - export const useNotificationSSE = (enabled: boolean) => { - const router = useRouter(); + const pathname = usePathname(); const queryClient = useQueryClient(); const retryDelayRef = useRef(1_000); const abortRef = useRef(null); const hasConnectedRef = useRef(false); const authFailCountRef = useRef(0); + // 주최자 알림 토스트를 담기 화면에서만 노출하기 위해 최신 경로를 ref로 관리 + const pathnameRef = useRef(pathname); + useEffect(() => { + pathnameRef.current = pathname; + }, [pathname]); + useEffect(() => { if (!enabled) return; @@ -169,10 +152,6 @@ export const useNotificationSSE = (enabled: boolean) => { type: 'all', }); const message = buildToastMessage(payload); - const deepLink = resolveDeepLink(payload); - const action = deepLink - ? { label: '바로가기', onClick: () => router.push(deepLink) } - : void 0; switch (payload.type) { case 'ITEM_PARSING_COMPLETED': @@ -199,17 +178,23 @@ export const useNotificationSSE = (enabled: boolean) => { predicate: query => isWishQueryOfItem(query, payload.refId), }); } - toast.error(message, { action, duration: 5000 }); + toast.error(message, { duration: 5000 }); break; case 'TOURNAMENT_STARTED': + queryClient.invalidateQueries({ queryKey: ['tournament', payload.refId] }); + toast.info(message, { duration: 5000 }); + break; case 'TOURNAMENT_JOINED': case 'TOURNAMENT_ITEM_ADDED': case 'TOURNAMENT_ITEM_DELETED': queryClient.invalidateQueries({ queryKey: ['tournament', payload.refId] }); - toast.info(message, { action, duration: 5000 }); + + if (pathnameRef.current === ROUTES.TOURNAMENT_CREATE(payload.refId)) { + toast.info(message, { duration: 5000 }); + } break; default: - toast.info(message, { action, duration: 5000 }); + toast.info(message, { duration: 5000 }); } } catch { // malformed JSON — 무시 @@ -241,5 +226,5 @@ export const useNotificationSSE = (enabled: boolean) => { cancelled = true; abortRef.current?.abort(); }; - }, [enabled, router, queryClient]); + }, [enabled, queryClient]); };