'use client'; import { useEffect, useRef, useState, useCallback } from 'react'; import * as signalR from '@microsoft/signalr'; import { GoalProgress, CrewRankItem } from '@/types/donation'; /** * DonationHub 공통 Hook — 목표/순위/크루 위젯에서 공유 */ export function useDonationHub(widgetToken: string, hubUrl: string) { const connectionRef = useRef(null); const [connected, setConnected] = useState(false); // 목표 const [goalProgress, setGoalProgress] = useState(null); // 순위 const [ranking, setRanking] = useState<{ rank: number; sponsorMemberID: number; sponsorName: string; totalAmount: number; donationCount: number }[]>([]); // 크루 const [crewRanking, setCrewRanking] = useState([]); const [crewTotalAmount, setCrewTotalAmount] = useState(0); useEffect(() => { const conn = new signalR.HubConnectionBuilder() .withUrl(hubUrl) .withAutomaticReconnect() .build(); conn.on('ReceiveGoalUpdate', (data: GoalProgress) => { setGoalProgress(data); }); conn.on('ReceiveRankUpdate', (data: { list: typeof ranking }) => { setRanking(data.list); }); conn.on('ReceiveCrewUpdate', (data: { list: CrewRankItem[]; totalAmount: number }) => { setCrewRanking(data.list); setCrewTotalAmount(data.totalAmount); }); conn.start().then(() => { conn.invoke('JoinChannel', widgetToken); setConnected(true); }).catch(err => console.error('[DonationHub]', err)); conn.onreconnected(() => { conn.invoke('JoinChannel', widgetToken); setConnected(true); }); conn.onclose(() => setConnected(false)); connectionRef.current = conn; return () => { conn.stop(); }; }, [widgetToken, hubUrl]); return { connected, goalProgress, setGoalProgress, ranking, setRanking, crewRanking, crewTotalAmount }; }