'use client'; import { useEffect, useRef, useState, useCallback } from 'react'; import * as signalR from '@microsoft/signalr'; import { DonationAlertData, DonationRemoteState } from '@/types/donation'; type AlertQueueItem = DonationAlertData & { status: 'queued'|'playing'|'done' }; export function useDonationAlert(widgetToken: string, hubUrl: string) { const connectionRef = useRef(null); const [connected, setConnected] = useState(false); const [queue, setQueue] = useState([]); const [current, setCurrent] = useState(null); const [remoteState, setRemoteState] = useState({ isPaused: false, isAccepting: true, isAudioOnly: false, isVideoOnly: false }); const [skipSignal, setSkipSignal] = useState(0); // SignalR 연결 useEffect(() => { const conn = new signalR.HubConnectionBuilder() .withUrl(hubUrl) .withAutomaticReconnect() .build(); conn.on('ReceiveAlert', (data: DonationAlertData) => { setQueue(prev => [...prev, { ...data, status: 'queued' }]); }); conn.on('ReceiveSkip', () => { setSkipSignal(prev => prev + 1); }); conn.on('ReceivePause', (isPaused: boolean) => { setRemoteState(prev => ({ ...prev, isPaused })); }); conn.on('ReceiveState', (state: DonationRemoteState) => { setRemoteState(state); }); conn.start().then(() => { conn.invoke('JoinChannel', widgetToken); setConnected(true); }).catch(err => { console.error('[DonationHub] Connect failed:', err); }); conn.onreconnected(() => { conn.invoke('JoinChannel', widgetToken); setConnected(true); }); conn.onclose(() => setConnected(false)); connectionRef.current = conn; return () => { conn.stop(); }; }, [widgetToken, hubUrl]); // 큐 처리 — 일시정지가 아닐 때 다음 알림 꺼내기 useEffect(() => { if (current || remoteState.isPaused || queue.length === 0) { return; } const next = queue[0]; setQueue(prev => prev.slice(1)); setCurrent({ ...next, status: 'playing' }); // 알림 재생 시작 보고 connectionRef.current?.invoke('AlertDelivered', next.alertID).catch(() => {}); }, [queue, current, remoteState.isPaused]); // 스킵 시그널 처리 useEffect(() => { if (skipSignal > 0 && current) { setCurrent(null); } }, [skipSignal]); // 알림 완료 콜백 const onAlertComplete = useCallback(() => { setCurrent(null); }, []); return { connected, current, queue, remoteState, skipSignal, onAlertComplete }; }