Files
2026FIFAWorldCup/platform/web/hooks/useLiveMatchData.ts
QuantBot aa7e3bba76
Some checks failed
2026 World Cup Quant Platform - Production Deployment / Code Quality & Testing (push) Failing after 1m49s
2026 World Cup Quant Platform - Production Deployment / Deploy to Production VM via Rsync (push) Has been skipped
chore: migrate deployment to Gitea Actions with zero-trust rsync
2026-06-16 19:06:50 +08:00

86 lines
1.9 KiB
TypeScript

'use client';
import { useEffect, useMemo, useState } from 'react';
type OddsPayload = {
matchId: string;
market: string;
bookmaker: string;
odds: number;
};
type MatchEventPayload = {
matchId: string;
type: 'goal' | 'yellow' | 'red' | 'substitution';
minute: number;
detail: string;
};
type WSMessage =
| ({ eventType: 'odds'; payload: OddsPayload })
| ({ eventType: 'event'; payload: MatchEventPayload })
| Record<string, unknown>;
export function useLiveMatchData(matchId: string | null) {
const [odds, setOdds] = useState<OddsPayload[]>([]);
const [events, setEvents] = useState<MatchEventPayload[]>([]);
const wsBase = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:8000/ws/matches';
useEffect(() => {
if (!matchId) {
return undefined;
}
let isMounted = true;
const ws = new WebSocket(`${wsBase}/${matchId}`);
ws.onopen = () => {
if (!isMounted) return;
ws.send(JSON.stringify({ action: 'subscribe', matchId }));
};
ws.onmessage = (event) => {
let message: WSMessage;
try {
message = JSON.parse(event.data);
} catch {
return;
}
if (message.eventType === 'odds') {
const payload = message.payload as OddsPayload;
setOdds((prev) => {
const next = [...prev.filter((item) => item.market !== payload.market), payload];
return next;
});
return;
}
if (message.eventType === 'event') {
const payload = message.payload as MatchEventPayload;
setEvents((prev) => [payload, ...prev].slice(0, 80));
}
};
ws.onerror = () => {
console.error('WebSocket 錯誤', matchId);
};
return () => {
isMounted = false;
ws.close(1000);
};
}, [matchId, wsBase]);
const liveState = useMemo(
() => ({
odds,
events,
}),
[odds, events],
);
return liveState;
}