spectator tweakings

This commit is contained in:
legop3
2026-01-25 02:59:16 -05:00
parent 536e4fe32e
commit d7833dbba9
6 changed files with 83 additions and 31 deletions
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
+2 -2
View File
@@ -11,8 +11,8 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
<title>Multi Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-Sryc1K8v.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CwxAi21V.css">
<script type="module" crossorigin src="/assets/index-C2Pt63lU.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DsiEQJXa.css">
</head>
<body>
<div id="root"></div>
+7 -5
View File
@@ -5,7 +5,7 @@ const MOBILE_DISMISS_MS = 10000;
const MAX_FONT_PX = 28;
const MIN_FONT_PX = 14;
export default function CommunityGoalBanner({ layout = 'desktop', className = '' }) {
export default function CommunityGoalBanner({ layout = 'desktop', className = '', dismissable = true }) {
const { session } = useSession();
const goalText = session?.communityGoal?.text ? String(session.communityGoal.text).trim() : '';
const isMobile = layout === 'mobile-portrait' || layout === 'mobile-landscape' || layout === 'mobile';
@@ -22,7 +22,7 @@ export default function CommunityGoalBanner({ layout = 'desktop', className = ''
return undefined;
}
setVisible(true);
if (!isMobile) return undefined;
if (!isMobile || !dismissable) return undefined;
const startedAt = Date.now();
setRemainingMs(MOBILE_DISMISS_MS);
const timer = setTimeout(() => setVisible(false), MOBILE_DISMISS_MS);
@@ -81,14 +81,16 @@ export default function CommunityGoalBanner({ layout = 'desktop', className = ''
const remainingSeconds = isMobile ? Math.ceil(remainingMs / 1000) : null;
const dismissProps = dismissable
? { onClick: () => setVisible(false), role: 'button', tabIndex: 0 }
: {};
return (
<div
ref={containerRef}
className={containerClass}
style={{ fontSize: `${fontSize}px`, lineHeight: 1.1 }}
onClick={() => setVisible(false)}
role="button"
tabIndex={0}
{...dismissProps}
>
<span className="flex w-full items-stretch gap-0.5 whitespace-nowrap rounded-md">
<span className="flex flex-col justify-center border-r border-slate-700/60 px-0.5 text-[0.55em] font-semibold leading-tight text-slate-400">
+67 -17
View File
@@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { SettingsProvider } from '../settings/index.js';
import { useSession } from '../context/SessionContext.jsx';
import { useSpectatorMode } from '../hooks/useSpectatorMode.js';
@@ -14,6 +15,33 @@ import CommunityGoalBanner from '../components/CommunityGoalBanner.jsx';
import RoverQueuesPanel from '../components/RoverQueuesPanel.jsx';
import RawUserPilePanel from '../components/RawUserPilePanel.jsx';
function usePortraitLayout() {
const [isPortrait, setIsPortrait] = useState(() => {
if (typeof window === 'undefined') return false;
return window.matchMedia('(max-aspect-ratio: 4/3)').matches;
});
useEffect(() => {
if (typeof window === 'undefined') return undefined;
const media = window.matchMedia('(max-aspect-ratio: 4/3)');
const handleChange = (event) => setIsPortrait(event.matches);
if (media.addEventListener) {
media.addEventListener('change', handleChange);
} else {
media.addListener(handleChange);
}
return () => {
if (media.removeEventListener) {
media.removeEventListener('change', handleChange);
} else {
media.removeListener(handleChange);
}
};
}, []);
return isPortrait;
}
function formatDriverLabel({ roverId, session }) {
const activeDriverId = session?.activeDrivers?.[roverId] || null;
const user = (session?.users || []).find((entry) => entry.socketId === activeDriverId);
@@ -98,6 +126,7 @@ function SpectatorContent() {
const inLockdown = session?.mode === 'lockdown';
useDefaultNickname();
useSpectatorMode();
const isPortraitLayout = usePortraitLayout();
const frames = useTelemetryFrames();
const roster = session?.roster ?? [];
const snapshotFeeds = useRoverSnapshots(
@@ -122,10 +151,46 @@ function SpectatorContent() {
);
}
const mainClass = isPortraitLayout
? 'flex min-h-screen flex-col bg-black text-slate-100 md:h-screen md:overflow-hidden'
: 'grid min-h-screen grid-cols-1 gap-0.5 bg-black text-slate-100 md:h-full md:min-h-0 md:grid-cols-[minmax(0,1fr)_18rem] lg:grid-cols-[minmax(0,1fr)_20rem]';
const contentClass = isPortraitLayout
? 'order-2 flex min-h-0 min-w-0 flex-1 flex-col gap-0.5 overflow-y-auto'
: 'order-1 flex min-h-0 min-w-0 flex-col gap-0.5 md:overflow-y-auto';
const sidebarClass = isPortraitLayout
? 'order-1 flex min-h-0 w-full items-stretch gap-0.5 overflow-x-auto border-b border-slate-800/60 bg-slate-950/90 p-0.5'
: 'order-2 flex min-h-0 min-w-0 flex-col gap-0.5 border-l border-slate-800/60 bg-slate-950/90 md:h-full md:overflow-y-auto';
const topBarItemClass = isPortraitLayout ? 'min-w-[16rem] max-w-[22rem] flex-1' : '';
return (
<div className="min-h-screen bg-black text-slate-100 md:h-screen md:overflow-hidden">
<main className="grid min-h-screen grid-cols-1 gap-0.5 p-0 md:h-full md:min-h-0 md:grid-cols-[minmax(0,1fr)_18rem] lg:grid-cols-[minmax(0,1fr)_20rem]">
<section className="flex min-h-0 min-w-0 flex-col gap-0.5 md:overflow-y-auto">
<main className={mainClass}>
<section className={sidebarClass}>
<div className={topBarItemClass}>
<CommunityGoalBanner layout="desktop" dismissable={false} className="text-sm" />
</div>
<div className={`${topBarItemClass} ${isPortraitLayout ? 'h-32 overflow-y-auto' : ''}`}>
<div className="panel h-full">
<RoverQueuesPanel title="Rovers" />
</div>
</div>
<div className={`${topBarItemClass} ${isPortraitLayout ? 'h-32' : 'flex-1 min-h-0'}`}>
<RawUserPilePanel
hideNicknameForm
hideHeader
compact
fillHeight
className="h-full"
/>
</div>
<div className={`${topBarItemClass} ${isPortraitLayout ? 'h-32' : 'flex-[1.1] min-h-0'}`}>
<ChatPanel hideInput hideSpectatorNotice fillHeight />
</div>
<div className={`${topBarItemClass} ${isPortraitLayout ? 'h-32' : ''}`}>
<LogsRow className={`${isPortraitLayout ? 'h-full' : 'h-40'} overflow-hidden`} />
</div>
</section>
<section className={contentClass}>
<RoverRow
roster={roster}
frames={frames}
@@ -135,21 +200,6 @@ function SpectatorContent() {
/>
<SecondaryRow />
</section>
<section className="flex min-h-0 min-w-0 flex-col gap-0.5 md:h-full">
<CommunityGoalBanner layout="desktop" />
<div className="panel">
<RoverQueuesPanel title="Rovers" />
</div>
<div className="min-h-0 min-w-0 flex-1 overflow-hidden">
<RawUserPilePanel hideNicknameForm hideHeader fillHeight className="h-full" />
</div>
<div className="min-h-0 min-w-0 flex-[1.1] overflow-hidden">
<ChatPanel hideInput hideSpectatorNotice fillHeight />
</div>
<div className="min-h-0 min-w-0">
<LogsRow className="h-40 overflow-hidden" />
</div>
</section>
</main>
<AlertFeed />
</div>