mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
spectator tweakings
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-Sryc1K8v.js"></script>
|
<script type="module" crossorigin src="/assets/index-C2Pt63lU.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-CwxAi21V.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DsiEQJXa.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const MOBILE_DISMISS_MS = 10000;
|
|||||||
const MAX_FONT_PX = 28;
|
const MAX_FONT_PX = 28;
|
||||||
const MIN_FONT_PX = 14;
|
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 { session } = useSession();
|
||||||
const goalText = session?.communityGoal?.text ? String(session.communityGoal.text).trim() : '';
|
const goalText = session?.communityGoal?.text ? String(session.communityGoal.text).trim() : '';
|
||||||
const isMobile = layout === 'mobile-portrait' || layout === 'mobile-landscape' || layout === 'mobile';
|
const isMobile = layout === 'mobile-portrait' || layout === 'mobile-landscape' || layout === 'mobile';
|
||||||
@@ -22,7 +22,7 @@ export default function CommunityGoalBanner({ layout = 'desktop', className = ''
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
if (!isMobile) return undefined;
|
if (!isMobile || !dismissable) return undefined;
|
||||||
const startedAt = Date.now();
|
const startedAt = Date.now();
|
||||||
setRemainingMs(MOBILE_DISMISS_MS);
|
setRemainingMs(MOBILE_DISMISS_MS);
|
||||||
const timer = setTimeout(() => setVisible(false), 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 remainingSeconds = isMobile ? Math.ceil(remainingMs / 1000) : null;
|
||||||
|
|
||||||
|
const dismissProps = dismissable
|
||||||
|
? { onClick: () => setVisible(false), role: 'button', tabIndex: 0 }
|
||||||
|
: {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className={containerClass}
|
className={containerClass}
|
||||||
style={{ fontSize: `${fontSize}px`, lineHeight: 1.1 }}
|
style={{ fontSize: `${fontSize}px`, lineHeight: 1.1 }}
|
||||||
onClick={() => setVisible(false)}
|
{...dismissProps}
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
>
|
>
|
||||||
<span className="flex w-full items-stretch gap-0.5 whitespace-nowrap rounded-md">
|
<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">
|
<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">
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useEffect, useState } 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';
|
||||||
@@ -14,6 +15,33 @@ import CommunityGoalBanner from '../components/CommunityGoalBanner.jsx';
|
|||||||
import RoverQueuesPanel from '../components/RoverQueuesPanel.jsx';
|
import RoverQueuesPanel from '../components/RoverQueuesPanel.jsx';
|
||||||
import RawUserPilePanel from '../components/RawUserPilePanel.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 }) {
|
function formatDriverLabel({ roverId, session }) {
|
||||||
const activeDriverId = session?.activeDrivers?.[roverId] || null;
|
const activeDriverId = session?.activeDrivers?.[roverId] || null;
|
||||||
const user = (session?.users || []).find((entry) => entry.socketId === activeDriverId);
|
const user = (session?.users || []).find((entry) => entry.socketId === activeDriverId);
|
||||||
@@ -98,6 +126,7 @@ function SpectatorContent() {
|
|||||||
const inLockdown = session?.mode === 'lockdown';
|
const inLockdown = session?.mode === 'lockdown';
|
||||||
useDefaultNickname();
|
useDefaultNickname();
|
||||||
useSpectatorMode();
|
useSpectatorMode();
|
||||||
|
const isPortraitLayout = usePortraitLayout();
|
||||||
const frames = useTelemetryFrames();
|
const frames = useTelemetryFrames();
|
||||||
const roster = session?.roster ?? [];
|
const roster = session?.roster ?? [];
|
||||||
const snapshotFeeds = useRoverSnapshots(
|
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 (
|
return (
|
||||||
<div className="min-h-screen bg-black text-slate-100 md:h-screen md:overflow-hidden">
|
<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]">
|
<main className={mainClass}>
|
||||||
<section className="flex min-h-0 min-w-0 flex-col gap-0.5 md:overflow-y-auto">
|
<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
|
<RoverRow
|
||||||
roster={roster}
|
roster={roster}
|
||||||
frames={frames}
|
frames={frames}
|
||||||
@@ -135,21 +200,6 @@ function SpectatorContent() {
|
|||||||
/>
|
/>
|
||||||
<SecondaryRow />
|
<SecondaryRow />
|
||||||
</section>
|
</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>
|
</main>
|
||||||
<AlertFeed />
|
<AlertFeed />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user