mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
updated mobile page
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user