mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 01:50:47 -04:00
spectator updates
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@
|
|||||||
<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-Cx2yYVpk.js"></script>
|
<script type="module" crossorigin src="/assets/index-Bu0ya-aM.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-sMfSpjY4.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-B8zLJlAS.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -39,11 +39,12 @@ function buildWhepUrlForSource(source) {
|
|||||||
|
|
||||||
function passesMode(socket) {
|
function passesMode(socket) {
|
||||||
const mode = getMode();
|
const mode = getMode();
|
||||||
if (mode === MODES.LOCKDOWN && !isLockdownAdmin(socket)) {
|
if (mode === MODES.LOCKDOWN) {
|
||||||
return false;
|
return isLockdownAdmin(socket);
|
||||||
}
|
}
|
||||||
if (mode === MODES.ADMIN && !isAdmin(socket)) {
|
if (mode === MODES.ADMIN) {
|
||||||
return false;
|
const role = getRole(socket);
|
||||||
|
return role === 'spectator' || isAdmin(socket);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
import { SettingsProvider } from '../settings/index.js';
|
import { SettingsProvider } from '../settings/index.js';
|
||||||
import { useSession } from '../context/SessionContext.jsx';
|
import { useSession } from '../context/SessionContext.jsx';
|
||||||
import { useSpectatorMode } from '../hooks/useSpectatorMode.js';
|
import { useSpectatorMode } from '../hooks/useSpectatorMode.js';
|
||||||
@@ -10,22 +9,6 @@ import UserListPanel from '../components/UserListPanel.jsx';
|
|||||||
import ChatPanel from '../components/ChatPanel.jsx';
|
import ChatPanel from '../components/ChatPanel.jsx';
|
||||||
import LogPanel from '../components/LogPanel.jsx';
|
import LogPanel from '../components/LogPanel.jsx';
|
||||||
|
|
||||||
function SpectatorStatus({ connected, ready, rosterCount, mode, blocked }) {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-wrap items-center gap-0.5 text-sm">
|
|
||||||
<span className={`px-0.5 py-0.5 font-semibold ${connected ? 'bg-emerald-500/20 text-emerald-200' : 'bg-red-500/20 text-red-200'}`}>
|
|
||||||
{connected ? 'Connected' : 'Disconnected'}
|
|
||||||
</span>
|
|
||||||
<span className={`px-0.5 py-0.5 ${ready ? 'bg-blue-600/40 text-blue-100' : 'bg-slate-700 text-slate-200'}`}>
|
|
||||||
Spectator mode {ready ? 'active' : 'pending…'}
|
|
||||||
</span>
|
|
||||||
<span className="bg-slate-800 px-0.5 py-0.5 text-slate-200">Rovers: {rosterCount}</span>
|
|
||||||
<span className="bg-slate-800 px-0.5 py-0.5 text-slate-200">Mode: {mode || 'unknown'}</span>
|
|
||||||
{blocked && <span className="bg-red-700/60 px-0.5 py-0.5 text-red-100">Spectate blocked</span>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function TelemetrySummary({ frame }) {
|
function TelemetrySummary({ frame }) {
|
||||||
const sensors = frame?.sensors || {};
|
const sensors = frame?.sensors || {};
|
||||||
const updated = frame?.receivedAt ? new Date(frame.receivedAt).toLocaleTimeString() : null;
|
const updated = frame?.receivedAt ? new Date(frame.receivedAt).toLocaleTimeString() : null;
|
||||||
@@ -87,7 +70,7 @@ function RoverRow({ roster, frames, videoSources, session }) {
|
|||||||
return <p className="col-span-full text-slate-400">No rovers registered.</p>;
|
return <p className="col-span-full text-slate-400">No rovers registered.</p>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<section className="grid grid-cols-1 gap-0.5 md:grid-cols-2 xl:grid-cols-3">
|
<section className="grid grid-cols-1 gap-0.5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
{roster.map((rover) => (
|
{roster.map((rover) => (
|
||||||
<RoverSpectatorCard
|
<RoverSpectatorCard
|
||||||
key={rover.id}
|
key={rover.id}
|
||||||
@@ -120,30 +103,16 @@ function LogsRow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function SpectatorApp() {
|
export default function SpectatorApp() {
|
||||||
const { connected, session } = useSession();
|
const { session } = useSession();
|
||||||
const spectatorReady = useSpectatorMode();
|
useSpectatorMode();
|
||||||
const frames = useTelemetryFrames();
|
const frames = useTelemetryFrames();
|
||||||
const roster = session?.roster ?? [];
|
const roster = session?.roster ?? [];
|
||||||
const videoSources = useVideoRequests(roster.map((rover) => rover.id));
|
const videoSources = useVideoRequests(roster.map((rover) => rover.id));
|
||||||
const blocked = session?.mode === 'lockdown';
|
|
||||||
|
|
||||||
const status = useMemo(
|
|
||||||
() => ({ connected, ready: spectatorReady, rosterCount: roster.length, mode: session?.mode, blocked }),
|
|
||||||
[blocked, connected, roster.length, session?.mode, spectatorReady],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsProvider>
|
<SettingsProvider>
|
||||||
<div className="min-h-screen bg-black text-slate-100">
|
<div className="min-h-screen bg-black text-slate-100">
|
||||||
<main className="mx-auto flex max-w-7xl flex-col gap-0.5 px-0.5 py-0.5">
|
<main className="flex flex-col gap-0.5 p-0.5">
|
||||||
<header className="space-y-0.5">
|
|
||||||
<h1 className="text-4xl font-semibold text-white">Spectator Console</h1>
|
|
||||||
<p className="text-slate-400">Live rover telemetry & logs. This view is read-only.</p>
|
|
||||||
<SpectatorStatus {...status} />
|
|
||||||
{blocked && (
|
|
||||||
<p className="text-sm text-amber-300">Lockdown active. Spectator view will resume automatically when access returns.</p>
|
|
||||||
)}
|
|
||||||
</header>
|
|
||||||
<RoverRow roster={roster} frames={frames} videoSources={videoSources} session={session} />
|
<RoverRow roster={roster} frames={frames} videoSources={videoSources} session={session} />
|
||||||
<SecondaryRow />
|
<SecondaryRow />
|
||||||
<LogsRow />
|
<LogsRow />
|
||||||
|
|||||||
Reference in New Issue
Block a user