mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
pass 1 (broken)
This commit is contained in:
@@ -2,14 +2,14 @@
|
||||
// Purpose: Defines the Global Objective Banner 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, useRef, useState } from 'react';
|
||||
import { useSession } from '../../context/SessionContext.jsx';
|
||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
|
||||
const MOBILE_DISMISS_MS = 10000;
|
||||
const MAX_FONT_PX = 28;
|
||||
const MIN_FONT_PX = 14;
|
||||
|
||||
export default function GlobalObjectiveBanner({ layout = 'desktop', className = '', dismissable = true }) {
|
||||
const { session } = useSession();
|
||||
const session = useSessionSelector((state) => state.session);
|
||||
const goalText = session?.globalObjective?.text ? String(session.globalObjective.text).trim() : '';
|
||||
const isMobile = layout === 'mobile-portrait' || layout === 'mobile-landscape' || layout === 'mobile';
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// Purpose: Defines the Log Panel 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 { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export default function LogPanel() {
|
||||
const logs = useSessionSelector((state) => state.logs);
|
||||
const rendered = useMemo(() => logs.slice().reverse(), [logs]);
|
||||
return (
|
||||
<div className="panel-section space-y-0.5 text-base">
|
||||
<div className="flex items-center justify-between text-sm text-slate-400">
|
||||
@@ -15,10 +17,7 @@ export default function LogPanel() {
|
||||
{logs.length === 0 ? (
|
||||
<p>No logs yet.</p>
|
||||
) : (
|
||||
logs
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((entry) => (
|
||||
rendered.map((entry) => (
|
||||
<div key={entry.id} className="surface">
|
||||
<span className="text-amber-400">{entry.timestamp}</span>{' '}
|
||||
<span className="text-lime-400">[{entry.level}]</span>{' '}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Purpose: Defines the Raw User Pile Panel 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 { useMemo } from 'react';
|
||||
import { useSession } from '../../context/SessionContext.jsx';
|
||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
import NicknameForm from '../NicknameForm/index.jsx';
|
||||
import SocialButtonsGrid from '../SocialButtonsGrid/index.jsx';
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function RawUserPilePanel({
|
||||
fillHeight = false,
|
||||
compact = false,
|
||||
}) {
|
||||
const { session } = useSession();
|
||||
const session = useSessionSelector((state) => state.session);
|
||||
const canSetNickname = session?.role !== 'spectator';
|
||||
const users = session?.users ?? [];
|
||||
const selfId = session?.socketId || null;
|
||||
|
||||
@@ -23,6 +23,7 @@ import VipPanel from '../VipPanel/index.jsx';
|
||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
import { useSettingsNamespace } from '../../settings/index.js';
|
||||
import ButtonBoxPanel from '../ButtonBoxPanel/index.jsx';
|
||||
import { useState } from 'react';
|
||||
|
||||
function TopDownMapPanel() {
|
||||
const {
|
||||
@@ -115,6 +116,7 @@ function DriveDockPanel() {
|
||||
}
|
||||
|
||||
export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||
const [activeTab, setActiveTab] = useState('telemetry');
|
||||
const session = useSessionSelector((state) => state.session);
|
||||
const { state: controlState } = useControlSystem();
|
||||
const { value: vipAudio } = useSettingsNamespace('vipAudio', { openMicEnabled: false, pttMode: 'live' });
|
||||
@@ -138,7 +140,7 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||
);
|
||||
return (
|
||||
<section className="panel text-base">
|
||||
<Tabs defaultTab="telemetry">
|
||||
<Tabs defaultTab="telemetry" currentTab={activeTab} onTabChange={setActiveTab}>
|
||||
<TabList>
|
||||
<Tab id="telemetry">Controls</Tab>
|
||||
<Tab id="vip" highlight={vipClipPlaying ? 'green' : vipMicActive ? 'pink' : 'none'}>
|
||||
@@ -179,7 +181,7 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel id="vip" keepMounted>
|
||||
<VipPanel />
|
||||
<VipPanel isActive={activeTab === 'vip'} />
|
||||
</TabPanel>
|
||||
<TabPanel id="help">
|
||||
<HelpPanel layout={layout} onOpenOverlay={onOpenHelpOverlay} />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Purpose: Defines the Room Camera Panel 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, useState } from 'react';
|
||||
import { useSession } from '../../context/SessionContext.jsx';
|
||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
import { useSettingsNamespace } from '../../settings/index.js';
|
||||
import { useRoomCameraSnapshots } from '../../hooks/useRoomCameraSnapshots.js';
|
||||
import RoomCameraFeed from '../RoomCameraFeed/index.jsx';
|
||||
@@ -32,7 +32,7 @@ export default function RoomCameraPanel({
|
||||
hideHeader = false,
|
||||
panelId = null,
|
||||
}) {
|
||||
const { session } = useSession();
|
||||
const session = useSessionSelector((state) => state.session);
|
||||
const cameras = session?.roomCameras || [];
|
||||
const feedMap = useRoomCameraSnapshots(cameras.map((camera) => ({ id: camera.id })));
|
||||
const { value: orientationSettings, save: saveOrientationSettings } = useSettingsNamespace('roomCameraPanels', {});
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
cliffColor,
|
||||
} from './visuals.jsx';
|
||||
|
||||
export default function TopDownMapContent({ sensors = {}, variant = 'full', size: overrideSize, overlay = false }) {
|
||||
function TopDownMapContent({ sensors = {}, variant = 'full', size: overrideSize, overlay = false }) {
|
||||
const size = overrideSize || (variant === 'mini' ? 190 : 260);
|
||||
const center = size / 2;
|
||||
const offsetY = size * 0.07;
|
||||
@@ -147,3 +147,5 @@ export default function TopDownMapContent({ sensors = {}, variant = 'full', size
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(TopDownMapContent);
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
import TopDownMap from '../TopDownMap/index.jsx';
|
||||
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
||||
|
||||
export default function HudOverlay({
|
||||
function HudOverlay({
|
||||
sensors,
|
||||
label,
|
||||
roverColor = null,
|
||||
@@ -174,3 +174,5 @@ export default function HudOverlay({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(HudOverlay);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||
import React from 'react';
|
||||
|
||||
export default function LightBumpBars({ sensors }) {
|
||||
function LightBumpBars({ sensors }) {
|
||||
const values = [
|
||||
sensors?.lightBumpLeftSignal,
|
||||
sensors?.lightBumpFrontLeftSignal,
|
||||
@@ -47,3 +47,5 @@ export default function LightBumpBars({ sensors }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(LightBumpBars);
|
||||
|
||||
@@ -100,10 +100,18 @@ export default function VideoTile({
|
||||
.map(([key]) => key);
|
||||
const limiterCaps = overcurrentLimiter?.caps || null;
|
||||
const limiterGroups = overcurrentLimiter?.overcurrent?.groups || null;
|
||||
const debugAudio =
|
||||
typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('debugAudio');
|
||||
const debugHud =
|
||||
typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('debugHud');
|
||||
const debugFlags = useMemo(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return { debugAudio: false, debugHud: false };
|
||||
}
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return {
|
||||
debugAudio: params.has('debugAudio'),
|
||||
debugHud: params.has('debugHud'),
|
||||
};
|
||||
}, []);
|
||||
const debugAudio = debugFlags.debugAudio;
|
||||
const debugHud = debugFlags.debugHud;
|
||||
const limiterFill = useMemo(() => {
|
||||
if (!limiterCaps) return null;
|
||||
const driveCap = Number.isFinite(limiterCaps?.drive?.cap) ? limiterCaps.drive.cap : 1;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Purpose: Defines the Vip Panel 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 { useMemo, useState } from 'react';
|
||||
import { useSession } from '../../context/SessionContext.jsx';
|
||||
import { useSessionActions, useSessionSelector } from '../../context/SessionContext.jsx';
|
||||
import { useSettingsNamespace } from '../../settings/index.js';
|
||||
import { COOKIE_KEY_REGEX, flowWrapClass } from '../vip/constants.js';
|
||||
import VipAudioUploadCard from '../vip/VipAudioUploadCard/index.jsx';
|
||||
@@ -12,10 +12,10 @@ import VipPrivateRoverAccessCard from '../vip/VipPrivateRoverAccessCard.jsx';
|
||||
import VipNeatoCard from '../vip/VipNeatoCard.jsx';
|
||||
import VipLiftCard from '../vip/VipLiftCard.jsx';
|
||||
|
||||
export default function VipPanel() {
|
||||
export default function VipPanel({ isActive = true }) {
|
||||
const session = useSessionSelector((state) => state.session);
|
||||
const neatoLidar = useSessionSelector((state) => state.neatoLidar);
|
||||
const {
|
||||
session,
|
||||
neatoLidar,
|
||||
identifySession,
|
||||
requestVerification,
|
||||
requestPrivateRoverAccess,
|
||||
@@ -31,7 +31,7 @@ export default function VipPanel() {
|
||||
neatoPowerCycle,
|
||||
liftUp,
|
||||
liftDown,
|
||||
} = useSession();
|
||||
} = useSessionActions();
|
||||
const { value: identity, save: saveIdentity } = useSettingsNamespace('identity', { cookieUserId: '' });
|
||||
const { value: profile } = useSettingsNamespace('profile', { nickname: '' });
|
||||
|
||||
@@ -80,6 +80,7 @@ export default function VipPanel() {
|
||||
<VipNeatoCard
|
||||
neato={session?.neato || null}
|
||||
lidar={neatoLidar}
|
||||
lidarActive={isActive}
|
||||
onStart={neatoStart}
|
||||
onSendHome={neatoSendHome}
|
||||
onLocate={neatoLocate}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Vip Neato Card
|
||||
// Purpose: Defines the Vip Neato Card 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, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
function normalizeState(value) {
|
||||
return String(value || '').trim();
|
||||
@@ -114,6 +114,7 @@ function buildLidarRenderPoints(points = []) {
|
||||
export default function VipNeatoCard({
|
||||
neato,
|
||||
lidar,
|
||||
lidarActive = true,
|
||||
onStart,
|
||||
onSendHome,
|
||||
onLocate,
|
||||
@@ -140,7 +141,10 @@ export default function VipNeatoCard({
|
||||
const robotError = normalizeState(neato?.telemetry?.robotError) || '--';
|
||||
const robotAlert = normalizeState(neato?.telemetry?.robotAlert) || '--';
|
||||
const lidarPoints = Array.isArray(lidar?.points) ? lidar.points : [];
|
||||
const lidarRenderPoints = buildLidarRenderPoints(lidarPoints);
|
||||
const lidarRenderPoints = useMemo(
|
||||
() => (lidarActive ? buildLidarRenderPoints(lidarPoints) : []),
|
||||
[lidarActive, lidarPoints],
|
||||
);
|
||||
const lidarStatus = normalizeState(lidar?.status) || '--';
|
||||
const lidarDebug = lidar?.debug && typeof lidar.debug === 'object' ? lidar.debug : null;
|
||||
const lidarReason = normalizeState(lidarDebug?.reason) || '--';
|
||||
@@ -180,13 +184,14 @@ export default function VipNeatoCard({
|
||||
const primaryState = docked ? 'Docked' : uiStateLabel !== '--' ? uiStateLabel : 'Away from dock';
|
||||
|
||||
useEffect(() => {
|
||||
if (!lidarActive) return undefined;
|
||||
if (!lidar || !Array.isArray(lidar.points)) return undefined;
|
||||
setLidarFlash(true);
|
||||
const timer = setTimeout(() => {
|
||||
setLidarFlash(false);
|
||||
}, 120);
|
||||
return () => clearTimeout(timer);
|
||||
}, [lidar]);
|
||||
}, [lidar, lidarActive]);
|
||||
|
||||
return (
|
||||
<section className={`surface text-sm text-slate-200 ${wrapClass}`}>
|
||||
|
||||
Reference in New Issue
Block a user