Files
MultiRoombaRover/webui/src/barcodeGames/useBarcodeGameState.js
T
2026-06-18 20:13:07 -04:00

96 lines
2.5 KiB
JavaScript

// Barcode Game State Hook
// Purpose: Subscribes only interested pages/components to barcode game state.
// Scope: Keeps scanner-game traffic out of the global session tree so unrelated
// UI pages do not receive or retain barcode game data.
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSocket } from '../context/SocketContext.jsx';
const EMPTY_BARCODE_GAME_STATE = {
activeGameId: null,
games: [],
activeGame: null,
counters: {
objects: [],
rovers: [],
codes: [],
},
recentEvents: [],
};
function normalizeState(payload = {}) {
return {
...EMPTY_BARCODE_GAME_STATE,
...(payload && typeof payload === 'object' ? payload : {}),
games: Array.isArray(payload?.games) ? payload.games : [],
counters: {
...EMPTY_BARCODE_GAME_STATE.counters,
...(payload?.counters && typeof payload.counters === 'object' ? payload.counters : {}),
},
recentEvents: Array.isArray(payload?.recentEvents) ? payload.recentEvents : [],
};
}
export default function useBarcodeGameState() {
const socket = useSocket();
const [state, setState] = useState(EMPTY_BARCODE_GAME_STATE);
useEffect(() => {
function handleState(payload = {}) {
setState(normalizeState(payload));
}
socket.emit('barcodeGame:subscribe', {}, (response = {}) => {
if (response.state) {
handleState(response.state);
}
});
socket.on('barcodeGame:state', handleState);
return () => {
socket.off('barcodeGame:state', handleState);
};
}, [socket]);
const voteForGame = useCallback(
(gameId) =>
new Promise((resolve, reject) => {
socket.emit('barcodeGame:vote', { gameId }, (response = {}) => {
if (response.error) {
reject(new Error(response.error));
return;
}
if (response.state) {
setState(normalizeState(response.state));
}
resolve(response);
});
}),
[socket],
);
const resetActiveGame = useCallback(
() =>
new Promise((resolve, reject) => {
socket.emit('barcodeGame:resetActive', {}, (response = {}) => {
if (response.error) {
reject(new Error(response.error));
return;
}
if (response.state) {
setState(normalizeState(response.state));
}
resolve(response);
});
}),
[socket],
);
return useMemo(
() => ({
state,
resetActiveGame,
voteForGame,
}),
[resetActiveGame, state, voteForGame],
);
}