more queue related convergence for the future.

This commit is contained in:
legop3
2026-09-27 17:19:56 -04:00
parent 7231809140
commit 34936ab05c
54 changed files with 219 additions and 368 deletions
+1 -19
View File
@@ -1,25 +1,7 @@
// Rover Control Availability
// Purpose: Mirrors the server turnService.canDrive rule for immediate, consistent UI disabling.
import { useSessionSelector } from '../context/SessionContext.jsx';
export function selectCanControlRover(state, roverId) {
const session = state.session;
if (!roverId || session?.role === 'spectator') return false;
if (session?.mode !== 'turns') return true;
const turnInfo = session?.turnQueues?.[roverId] || null;
const queue = Array.isArray(turnInfo?.queue) ? turnInfo.queue : [];
/* The server permits control when a turns queue is absent or contains at most
one driver. Only a genuinely shared queue requires the active socket id.
Keeping this exact ordering prevents initial session loading from disabling
an otherwise uncontested rover. The server remains the final authority. */
if (queue.length <= 1) return true;
/* activeDrivers is the server's direct ownership map and can arrive before
the richer queue snapshot during an initial load or reconnect. The queue
field remains a compatibility fallback for intermediate payloads. */
const activeDriverId = session?.activeDrivers?.[roverId] || turnInfo?.current || null;
return Boolean(session?.socketId && activeDriverId === session.socketId);
return Boolean(state.session?.roster?.find((rover) => String(rover.id) === String(roverId))?.permissions?.canControl);
}
export default function useCanControlRover(roverId) {
+3 -13
View File
@@ -1,12 +1,9 @@
import { useMemo } from 'react';
import { useSessionSelector } from '../context/SessionContext.jsx';
import { useSharedClock } from './useSharedClock.js';
export function useDriverVideoModePolicy(roverId) {
const mode = useSessionSelector((state) => state.session?.mode || null);
const turnQueues = useSessionSelector((state) => state.session?.turnQueues ?? {});
const socketId = useSessionSelector((state) => state.session?.socketId || null);
const activeDrivers = useSessionSelector((state) => state.session?.activeDrivers ?? {});
const nonTurnSnapshotsActive = useSessionSelector(
(state) => Boolean(state.session?.bandwidthSavings?.nonTurnVideo?.snapshotsActive),
);
@@ -18,16 +15,9 @@ export function useDriverVideoModePolicy(roverId) {
*/
const now = useSharedClock(1000, isTurnsMode);
const turnInfo = roverId ? turnQueues?.[roverId] || null : null;
const activeDriverId = roverId ? activeDrivers?.[roverId] || null : null;
const isActiveDriver = Boolean(socketId && activeDriverId === socketId);
const nextDriverId = useMemo(() => {
const queue = turnInfo?.queue || [];
if (!queue.length || !turnInfo?.current || queue.length <= 1) return null;
const idx = queue.findIndex((id) => id === turnInfo.current);
if (idx === -1) return queue[0] || null;
return queue[(idx + 1) % queue.length] || null;
}, [turnInfo]);
const turnInfo = useSessionSelector((state) => state.session?.roster?.find((rover) => String(rover.id) === String(roverId))?.turn);
const isActiveDriver = Boolean(turnInfo?.ownsControl);
const nextDriverId = turnInfo?.currentId ? turnInfo.nextId : null;
const isNextDriver = Boolean(socketId && nextDriverId === socketId);
const deadline = turnInfo?.deadline || null;
const msUntilTurn = deadline ? deadline - now : null;
-10
View File
@@ -1,10 +0,0 @@
import { useCallback } from 'react';
import { useSessionSelector } from '../context/SessionContext.jsx';
export default function useQueueUserLookup(turn, usersOverride) {
const localUsers = useSessionSelector((state) => state.session?.users);
const users = usersOverride || localUsers;
return useCallback((socketId) => users?.find((user) => user.socketId === socketId)
|| { socketId, nickname: turn?.userLabels?.[socketId] || null, role: null },
[users, turn?.userLabels]);
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { RESTART_DELAY_MS } from '../components/RoverMediaPlayer/constants.js';
import { RESTART_DELAY_MS } from '../lib/whepPlayback.js';
export default function useWhepRestart(enabled = true) {
const timer = useRef(null);