// Mini Summary Content // Purpose: Defines the Mini Summary Content module and the local helpers/components used in this file. // Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit. import { useEffect, useMemo, useState } from 'react'; 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 useDefaultNickname from '../../hooks/useDefaultNickname.js'; import useUserIdentitySync from '../../hooks/useUserIdentitySync.js'; import RoverMediaPlayer from '../../components/RoverMediaPlayer/index.jsx'; import FitViewportFrame from './components/FitViewportFrame.jsx'; import InfoColumn from './components/InfoColumn.jsx'; import { ROTATE_MS } from './constants.js'; import { formatDriverLabel } from './utils.js'; export default function MiniSummaryContent() { const { session } = useSession(); const spectatorReady = useSpectatorMode(); useDefaultNickname(); // Mini renders outside App.jsx, so it must run the same persisted identity // heartbeat itself. This keeps cookie-backed identity and saved nickname // metadata current without changing the mini page's layout or controls. useUserIdentitySync(); const inLockdown = session?.mode === 'lockdown'; const canSpectateVideo = Boolean(session?.isLocalNetwork); 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 snapshotRoster = driverRoster.length ? driverRoster : roster; const snapshotFeeds = useRoverSnapshots( snapshotRoster.map((rover) => rover.id), { enabled: !inLockdown && !canSpectateVideo, version: session?.mode }, ); const videoEntries = useMemo( () => canSpectateVideo ? snapshotRoster.map((rover) => ({ type: 'rover', id: rover.id, key: rover.id })) : [], [canSpectateVideo, snapshotRoster], ); const videoSources = useVideoRequests(videoEntries, { enabled: !inLockdown && canSpectateVideo, 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 []; if (!canSpectateVideo) { const withSnapshot = driverRoster.filter((rover) => snapshotFeeds[rover.id]?.objectUrl); return withSnapshot.length ? withSnapshot : driverRoster; } const withVideo = driverRoster.filter((rover) => { const sessionInfo = videoSources[rover.id]; return sessionInfo?.url && !sessionInfo?.error; }); return withVideo.length ? withVideo : driverRoster; }, [driverRoster, snapshotFeeds, videoSources, canSpectateVideo]); 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 = !canSpectateVideo && activeRover ? snapshotFeeds[activeRover.id] || null : null; const activeVideo = canSpectateVideo && activeRover ? videoSources[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.