import { useEffect, useState } from 'react' import { useQuery } from '@tanstack/react-query' import { fetchLiveState } from '../api' import type { LiveStreamData } from '../types' import { parseLiveStateEvent } from '../lib/live' import { SessionBanner } from '../components/live/SessionBanner' import { TimingTower } from '../components/live/TimingTower' import { RaceControlFeed } from '../components/live/RaceControlFeed' type StreamStatus = 'connecting' | 'connected' | 'disconnected' | 'error' export function LiveTimingPage() { const [snapshot, setSnapshot] = useState(null) const [isLive, setIsLive] = useState(false) const [streamStatus, setStreamStatus] = useState('connecting') const [now, setNow] = useState(Date.now()) const { data, isLoading, isError, error } = useQuery({ queryKey: ['live-state'], queryFn: fetchLiveState, staleTime: 5_000, }) useEffect(() => { if (!data) return setIsLive(data.is_live) setSnapshot(data.data) }, [data]) useEffect(() => { const timer = window.setInterval(() => setNow(Date.now()), 1000) return () => window.clearInterval(timer) }, []) useEffect(() => { if (!('EventSource' in window)) { setStreamStatus('error') return } let cancelled = false const events = new EventSource('/api/v1/live/stream') setStreamStatus('connecting') events.onopen = () => { if (!cancelled) setStreamStatus('connected') } events.addEventListener('snapshot', (event) => { const state = parseLiveStateEvent(event.data) if (!state || cancelled) return setIsLive(state.is_live) setSnapshot(state.data) setStreamStatus('connected') }) events.addEventListener('heartbeat', () => { if (!cancelled) setStreamStatus('connected') }) events.onerror = () => { if (!cancelled) setStreamStatus('disconnected') } return () => { cancelled = true events.close() } }, []) return (
{isError && (
{error instanceof Error ? error.message : 'Failed to load live timing state'}
)} {streamStatus === 'disconnected' && snapshot && (
Stream disconnected — showing last received snapshot
)} {isLoading && !snapshot && (
connecting to live timing…
)} {!isLoading && !snapshot && (
{streamStatus}
No live session active
No timing data in the current snapshot. The feed will update automatically when an F1 session goes live.
)} {snapshot && ( <>
Timing Tower
)}
) }