updated mobile page

This commit is contained in:
legop3
2026-01-08 20:32:47 -05:00
parent 008cf96c7e
commit d3f2f15370
5 changed files with 51 additions and 41 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -11,7 +11,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" /> <meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
<title>Multi Roomba Rover</title> <title>Multi Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-IngetVG6.js"></script> <script type="module" crossorigin src="/assets/index-DcfJLiso.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DCrU2sZz.css"> <link rel="stylesheet" crossorigin href="/assets/index-DCrU2sZz.css">
</head> </head>
<body> <body>
+6 -4
View File
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import TelemetryPanel from './components/TelemetryPanel.jsx'; import TelemetryPanel from './components/TelemetryPanel.jsx';
import ControlSummary from './components/ControlSummary.jsx'; import ControlSummary, { RoverRosterPanel } from './components/ControlSummary.jsx';
import AlertFeed from './components/AlertFeed.jsx'; import AlertFeed from './components/AlertFeed.jsx';
import MobileControls, { import MobileControls, {
MobileLandscapeAuxColumn, MobileLandscapeAuxColumn,
@@ -125,8 +125,9 @@ function MobileFeatureTabs({
function MobilePortraitLayout({ onOpenHelpOverlay }) { function MobilePortraitLayout({ onOpenHelpOverlay }) {
return ( return (
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<DriverVideoPanel layoutFormat='mobile'/> <DriverVideoPanel layoutFormat="mobile-portrait" />
<MobileControls /> <MobileControls />
<RoverRosterPanel />
{/* <ControlSummary /> */} {/* <ControlSummary /> */}
<MobileFeatureTabs <MobileFeatureTabs
layout="mobile-portrait" layout="mobile-portrait"
@@ -143,8 +144,9 @@ function MobileLandscapeLayout({ onOpenHelpOverlay }) {
<section className="grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] gap-0.5"> <section className="grid min-h-screen grid-cols-[minmax(0,0.7fr)_minmax(0,2.1fr)_minmax(0,0.7fr)] gap-0.5">
<MobileLandscapeAuxColumn /> <MobileLandscapeAuxColumn />
<div> <div>
<DriverVideoPanel layoutFormat='mobile'/> <DriverVideoPanel layoutFormat="mobile-landscape" />
<TelemetryPanel /> <RoverRosterPanel />
{/* <TelemetryPanel /> */}
</div> </div>
<MobileLandscapeControlColumn /> <MobileLandscapeControlColumn />
</section> </section>
+31 -26
View File
@@ -7,21 +7,12 @@ import TopDownMap from './TopDownMap.jsx';
import RoverRoster from './RoverRoster.jsx'; import RoverRoster from './RoverRoster.jsx';
import DriveDockAction, { useDriveDockState } from './DriveDockAction.jsx'; import DriveDockAction, { useDriveDockState } from './DriveDockAction.jsx';
export default function ControlSummary() { export function RoverRosterPanel({ title = 'Rovers' }) {
const { session, requestControl } = useSession(); 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 [pending, setPending] = useState({});
const canRequest = useMemo(() => session?.role && session.role !== 'spectator', [session?.role]); const canRequest = useMemo(() => session?.role && session.role !== 'spectator', [session?.role]);
const hideInlineControls = driveDockState.docked && !driveDockState.driving;
async function handleRequest(targetRoverId) { async function handleRequest(targetRoverId) {
if (!targetRoverId) return; if (!targetRoverId) return;
setPending((prev) => ({ ...prev, [targetRoverId]: true })); setPending((prev) => ({ ...prev, [targetRoverId]: true }));
@@ -34,6 +25,35 @@ export default function ControlSummary() {
} }
} }
return (
<RoverRoster
title={title}
roster={session?.roster ?? []}
renderActions={(rover) =>
canRequest ? (
<button
type="button"
onClick={() => handleRequest(rover.id)}
disabled={pending[rover.id]}
className="button-dark disabled:opacity-40"
>
{pending[rover.id] ? '...' : 'request'}
</button>
) : null
}
/>
);
}
export default function ControlSummary({ showRoster = true }) {
const {
state: { roverId, keymap },
} = useControlSystem();
const frame = useTelemetryFrame(roverId);
const sensors = frame?.sensors || {};
const driveDockState = useDriveDockState(roverId);
const hideInlineControls = driveDockState.docked && !driveDockState.driving;
return ( return (
<section className="panel-section"> <section className="panel-section">
<div className="grid items-stretch gap-1 md:grid-cols-[minmax(0,2fr)_minmax(0,1fr)] md:min-h-[22rem]"> <div className="grid items-stretch gap-1 md:grid-cols-[minmax(0,2fr)_minmax(0,1fr)] md:min-h-[22rem]">
@@ -49,22 +69,7 @@ export default function ControlSummary() {
{!hideInlineControls ? <InlineCameraTilt keymap={keymap} /> : null} {!hideInlineControls ? <InlineCameraTilt keymap={keymap} /> : null}
</div> </div>
</div> </div>
<RoverRoster {showRoster ? <RoverRosterPanel /> : null}
title="Rovers"
roster={roster}
renderActions={(rover) =>
canRequest ? (
<button
type="button"
onClick={() => handleRequest(rover.id)}
disabled={pending[rover.id]}
className="button-dark disabled:opacity-40"
>
{pending[rover.id] ? '...' : 'request'}
</button>
) : null
}
/>
</section> </section>
); );
} }
+5 -2
View File
@@ -368,6 +368,7 @@ export default function VideoTile({
status={renderedStatus} status={renderedStatus}
audioStatus={renderedAudioStatus} audioStatus={renderedAudioStatus}
desktopLayout={desktopLayout} desktopLayout={desktopLayout}
layoutFormat={layoutFormat}
variant={hudVariant} variant={hudVariant}
driverLabel={driverLabel} driverLabel={driverLabel}
battery={batteryVisual} battery={batteryVisual}
@@ -484,6 +485,7 @@ function HudOverlay({
status, status,
audioStatus, audioStatus,
desktopLayout = true, desktopLayout = true,
layoutFormat = 'desktop',
variant = 'default', variant = 'default',
driverLabel = null, driverLabel = null,
battery, battery,
@@ -502,12 +504,13 @@ function HudOverlay({
const pulse = frame?.receivedAt ? now - frame.receivedAt < 200 : false; const pulse = frame?.receivedAt ? now - frame.receivedAt < 200 : false;
const isMobile = mobileHud; const isMobile = mobileHud;
const portraitMobile = layoutFormat === 'mobile-portrait';
const statusTextClass = isMobile ? 'text-[0.55rem]' : 'text-[0.65rem]'; const statusTextClass = isMobile ? 'text-[0.55rem]' : 'text-[0.65rem]';
const statusPadClass = isMobile ? 'px-0.5 py-0.25' : 'px-1 py-0.5'; const statusPadClass = isMobile ? 'px-0.5 py-0.25' : 'px-1 py-0.5';
const labelPadClass = isMobile ? 'px-0.5 py-0.25' : 'px-0.5 py-0.5'; const labelPadClass = isMobile ? 'px-0.5 py-0.25' : 'px-0.5 py-0.5';
const labelTextClass = isMobile ? 'text-[0.7rem]' : 'text-[0.8rem]'; const labelTextClass = isMobile ? 'text-[0.7rem]' : 'text-[0.8rem]';
const mapSize = isMobile ? '240px' : '240px'; const mapSize = '240px';
const mapScale = isMobile ? 0.45 : 0.7; const mapScale = portraitMobile ? 0.36 : isMobile ? 0.45 : 0.7;
const mapOpacity = isMobile ? 0.85 : 0.7; const mapOpacity = isMobile ? 0.85 : 0.7;
const mapStyle = { const mapStyle = {
width: mapSize, width: mapSize,