import { useEffect, useMemo, useState } from 'react'; import { SettingsProvider } from '../settings/index.js'; import { useSession } from '../context/SessionContext.jsx'; import { useTelemetryFrames } from '../context/TelemetryContext.jsx'; import { useVideoRequests } from '../hooks/useVideoRequests.js'; import { useRoverSnapshots } from '../hooks/useRoverSnapshots.js'; import { useSpectatorMode } from '../hooks/useSpectatorMode.js'; import VideoTile from '../components/VideoTile.jsx'; import ChatPanel from '../components/ChatPanel.jsx'; import AlertFeed from '../components/AlertFeed.jsx'; import useDefaultNickname from '../hooks/useDefaultNickname.js'; const ROTATE_MS = 20000; const HARD_REFRESH_MS = 3 * 60 * 60 * 1000; function formatDriverLabel({ roverId, session }) { const activeDriverId = session?.activeDrivers?.[roverId] || null; const user = (session?.users || []).find((entry) => entry.socketId === activeDriverId); const label = user?.nickname || (activeDriverId ? activeDriverId.slice(0, 6) : 'No driver'); const mode = session?.mode; const turnInfo = session?.turnQueues?.[roverId]; return mode === 'turns' && turnInfo?.current ? `${label} (turns)` : label; } function MiniSummaryContent() { const { session } = useSession(); const spectatorReady = useSpectatorMode(); useDefaultNickname(); const inLockdown = session?.mode === 'lockdown'; const frames = useTelemetryFrames(); const roster = session?.roster ?? []; const [index, setIndex] = useState(0); const activeDrivers = session?.activeDrivers || {}; const driverRoster = useMemo( () => roster.filter((rover) => activeDrivers[rover.id]), [roster, activeDrivers], ); const snapshotFeeds = useRoverSnapshots( driverRoster.map((rover) => rover.id), { enabled: !inLockdown, version: session?.mode }, ); const audioEntries = useMemo( () => driverRoster.flatMap((rover) => { if (!rover?.id || !rover.media?.audioPublishUrl) return []; const id = String(rover.id); return [{ type: 'rover', id: `${id}-audio`, key: `${id}-audio` }]; }), [driverRoster], ); const audioSources = useVideoRequests(audioEntries, { enabled: !inLockdown, version: session?.mode }); const roverPool = useMemo(() => { if (!driverRoster.length) return []; const withSnapshot = driverRoster.filter((rover) => snapshotFeeds[rover.id]?.objectUrl); return withSnapshot.length ? withSnapshot : driverRoster; }, [driverRoster, snapshotFeeds]); const rotationPool = useMemo(() => { return roverPool.map((rover) => ({ type: 'rover', rover })); }, [roverPool]); const rotationKey = useMemo( () => rotationPool .map((entry) => `r:${entry.rover.id}`) .join('|'), [rotationPool], ); useEffect(() => { setIndex(0); }, [rotationKey]); useEffect(() => { if (!rotationPool.length) return undefined; const timer = setInterval(() => { setIndex((prev) => (prev + 1) % rotationPool.length); }, ROTATE_MS); return () => clearInterval(timer); }, [rotationPool.length, rotationKey]); const activeEntry = rotationPool.length ? rotationPool[index % rotationPool.length] : null; const activeRover = activeEntry?.type === 'rover' ? activeEntry.rover : null; const activeSnapshot = activeRover ? snapshotFeeds[activeRover.id] || null : null; const activeAudio = activeRover ? audioSources[`${activeRover.id}-audio`] || null : null; const activeFrame = activeRover ? frames[activeRover.id] || null : null; const driverLabel = activeRover ? formatDriverLabel({ roverId: activeRover.id, session }) : null; if (inLockdown) { return (
Mini spectator is disabled in lockdown.
It will automatically resume once lockdown ends.