import { useEffect, useMemo, useRef, useState } from 'react';
import { useSession } from '../context/SessionContext.jsx';
import { useTelemetryFrame } from '../context/TelemetryContext.jsx';
import { useControlSystem } from '../controls/index.js';
import { formatKeyLabel } from '../controls/keymapUtils.js';
import TopDownMap from './TopDownMap.jsx';
import RoverRoster from './RoverRoster.jsx';
import DriveDockAction, { useDriveDockState } from './DriveDockAction.jsx';
export default function ControlSummary() {
const { session, requestControl } = useSession();
const {
state: { roverId, keymap },
} = useControlSystem();
const frame = useTelemetryFrame(roverId);
const sensors = frame?.sensors || {};
const driveDockState = useDriveDockState(roverId);
const roster = session?.roster ?? [];
const [pending, setPending] = useState({});
const canRequest = useMemo(() => session?.role && session.role !== 'spectator', [session?.role]);
const hideInlineControls = driveDockState.docked && !driveDockState.driving;
async function handleRequest(targetRoverId) {
if (!targetRoverId) return;
setPending((prev) => ({ ...prev, [targetRoverId]: true }));
try {
await requestControl(targetRoverId);
} catch (err) {
alert(err.message);
} finally {
setPending((prev) => ({ ...prev, [targetRoverId]: false }));
}
}
return (
{!hideInlineControls ?
: null}
canRequest ? (
) : null
}
/>
);
}
function InlineCameraTilt({ keymap }) {
const {
state: { roverId, camera },
actions: { setServoAngle },
} = useControlSystem();
const config = camera?.config;
const enabled = Boolean(roverId && camera?.enabled && config);
const min = typeof config?.minAngle === 'number' ? config.minAngle : -30;
const max = typeof config?.maxAngle === 'number' ? config.maxAngle : 30;
const value =
typeof camera?.angle === 'number'
? camera.angle
: typeof config?.homeAngle === 'number'
? config.homeAngle
: (min + max) / 2;
const [pendingAngle, setPendingAngle] = useState(value);
const draggingRef = useRef(false);
useEffect(() => {
if (!draggingRef.current) {
setPendingAngle(value);
}
}, [value]);
const handleSlider = (event) => {
const next = Number.parseFloat(event.target.value);
if (Number.isNaN(next)) return;
draggingRef.current = true;
setPendingAngle(next);
setServoAngle(next);
};
const commitSlider = () => {
draggingRef.current = false;
};
if (!enabled) return null;
const upLabel = formatKeyLabel(keymap?.cameraUp?.[0]);
const downLabel = formatKeyLabel(keymap?.cameraDown?.[0]);
return (
Camera tilt
{formatDegrees(value)}
);
}
function KeyPill({ label }) {
return {label};
}
function formatDegrees(value) {
if (typeof value !== 'number' || Number.isNaN(value)) return '—';
return `${value > 0 ? '+' : ''}${value.toFixed(1)}°`;
}