chg: dev: more, massive refactor for front-end #4
This commit is contained in:
26
assets/js/mine-seeker/hooks/useGameRefs.jsx
Normal file
26
assets/js/mine-seeker/hooks/useGameRefs.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useRef } from 'react';
|
||||
import { PLAYER_DEF } from '../utils/constants';
|
||||
|
||||
const useGameRefs = () => {
|
||||
const webPlayerRef = useRef(null);
|
||||
const activePlayerRef = useRef(false);
|
||||
const bombSelectedRef = useRef(false);
|
||||
const connectionLostRef = useRef(false);
|
||||
const redRef = useRef({ ...PLAYER_DEF });
|
||||
const blueRef = useRef({ ...PLAYER_DEF });
|
||||
const lastClickedRef = useRef({ red: null, blue: null });
|
||||
const endRef = useRef(false);
|
||||
|
||||
return {
|
||||
webPlayerRef,
|
||||
activePlayerRef,
|
||||
bombSelectedRef,
|
||||
connectionLostRef,
|
||||
redRef,
|
||||
blueRef,
|
||||
lastClickedRef,
|
||||
endRef,
|
||||
};
|
||||
};
|
||||
|
||||
export default useGameRefs;
|
||||
36
assets/js/mine-seeker/hooks/useGameState.jsx
Normal file
36
assets/js/mine-seeker/hooks/useGameState.jsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useState } from 'react';
|
||||
import { initCells, PLAYER_DEF } from '../utils/constants';
|
||||
|
||||
const useGameState = () => {
|
||||
const [webPlayer, setWebPlayer] = useState(null);
|
||||
const [activePlayer, setActivePlayer] = useState(false);
|
||||
const [overlay, setOverlay] = useState(true);
|
||||
const [overlayTitle, setOverlayTitle] = useState('');
|
||||
const [overlaySubTitle, setOverlaySubTitle] = useState('');
|
||||
const [mines, setMines] = useState(51);
|
||||
const [bombSelected, setBombSelected] = useState(false);
|
||||
const [foundMines, setFoundMines] = useState(false);
|
||||
const [red, setRed] = useState({ ...PLAYER_DEF });
|
||||
const [blue, setBlue] = useState({ ...PLAYER_DEF });
|
||||
const [cells, setCells] = useState(initCells);
|
||||
const [gridReady, setGridReady] = useState(false);
|
||||
const [connectionLost, setConnectionLost] = useState(false);
|
||||
|
||||
return {
|
||||
webPlayer, setWebPlayer,
|
||||
activePlayer, setActivePlayer,
|
||||
overlay, setOverlay,
|
||||
overlayTitle, setOverlayTitle,
|
||||
overlaySubTitle, setOverlaySubTitle,
|
||||
mines, setMines,
|
||||
bombSelected, setBombSelected,
|
||||
foundMines, setFoundMines,
|
||||
red, setRed,
|
||||
blue, setBlue,
|
||||
cells, setCells,
|
||||
gridReady, setGridReady,
|
||||
connectionLost, setConnectionLost,
|
||||
};
|
||||
};
|
||||
|
||||
export default useGameState;
|
||||
@@ -1,26 +1,22 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { useGame } from '../contexts/GameContext';
|
||||
import { DESC } from '../constants';
|
||||
import { DESC } from '../utils/constants';
|
||||
|
||||
/**
|
||||
* Handles all server communication: SSE (Mercure), REST calls, and the
|
||||
* initialization lifecycle. Exposes only the UI-facing callbacks the
|
||||
* component needs: onClick, resign.
|
||||
*/
|
||||
const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
/** Handles all server communication: SSE (Mercure), REST calls, and the initialization lifecycle. */
|
||||
const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
const {
|
||||
// Async-safe refs
|
||||
/** Async-safe refs */
|
||||
webPlayerRef, activePlayerRef, bombSelectedRef, connectionLostRef, endRef,
|
||||
// State setters
|
||||
/** State setters */
|
||||
setGridReady,
|
||||
// Sync helpers
|
||||
/** Sync helpers */
|
||||
syncWebPlayer, syncActivePlayer, syncBombSelected, syncConnLost, syncRed, syncBlue,
|
||||
// Game logic
|
||||
/** Game logic */
|
||||
showOverlay, hideOverlay,
|
||||
applyRevealedCell, applyStep,
|
||||
makeGameEndIfItEnds, resignProcess,
|
||||
// Current cells snapshot (for active-check in onClick)
|
||||
/** Current cells snapshot (for active-check in onClick) */
|
||||
cells,
|
||||
} = useGame();
|
||||
|
||||
@@ -28,7 +24,7 @@ const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
const rpcUsersRef = useRef(null);
|
||||
const stepCacheRef = useRef([]);
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
/** HELPERS */
|
||||
|
||||
const correctGridSize = () => {
|
||||
let $f = $('#mine-wrapper .grid');
|
||||
@@ -38,7 +34,7 @@ const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
$('#mine-wrapper .grid .field-wrapper .field').height($f.width()).css('line-height', ($f.width() - 2) + 'px');
|
||||
};
|
||||
|
||||
// ── REST mutations / queries ──────────────────────────────────────────────
|
||||
/** REST mutations / queries */
|
||||
|
||||
const connectQuery = useQuery({
|
||||
queryKey: ['game-connect', gameAssoc],
|
||||
@@ -72,7 +68,7 @@ const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
}).then(r => r.json()),
|
||||
});
|
||||
|
||||
// ── Game-start helpers (triggered by server events) ───────────────────────
|
||||
/** Game-start helpers (triggered by server events) */
|
||||
|
||||
const wInit = (revealedCells = []) => {
|
||||
setGridReady(true);
|
||||
@@ -91,17 +87,20 @@ const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
|
||||
const makeGameStart = payload => {
|
||||
syncActivePlayer(1);
|
||||
syncRed(p => ({ ...p, name: payload.users.red || payload.users.redAnon || p.name }));
|
||||
syncRed(p => ({
|
||||
...p,
|
||||
name: payload.users.red || payload.users.redAnon || p.name,
|
||||
}));
|
||||
syncBlue(p => ({
|
||||
...p,
|
||||
name: payload.users.blue || payload.users.blueAnon || p.name,
|
||||
desc: 'blue' === webPlayerRef.current ? DESC.you : DESC.buddy,
|
||||
name: payload.users.blue || payload.users.blueAnon || p.name,
|
||||
desc: 'blue' === webPlayerRef.current ? DESC.you : DESC.buddy,
|
||||
active: true,
|
||||
}));
|
||||
hideOverlay();
|
||||
};
|
||||
|
||||
// ── Mercure / SSE message handlers ───────────────────────────────────────
|
||||
/** Mercure / SSE message handlers */
|
||||
|
||||
const wSubscribe = (payload, rpcUsers = null) => {
|
||||
isEnvDev && console.info((payload.user ?? 'user') + ' subscribed');
|
||||
@@ -174,17 +173,26 @@ const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
es.onmessage = e => handleMercureMessage(JSON.parse(e.data));
|
||||
es.onopen = () => {
|
||||
isEnvDev && console.info('SSE opened');
|
||||
if (connectionLostRef.current) { isEnvDev && console.info('SSE reconnected'); joinMutation.mutate(); }
|
||||
if (connectionLostRef.current) {
|
||||
isEnvDev && console.info('SSE reconnected');
|
||||
joinMutation.mutate();
|
||||
}
|
||||
};
|
||||
es.onerror = () => {
|
||||
isEnvDev && console.error('SSE error');
|
||||
syncConnLost(true);
|
||||
};
|
||||
es.onerror = () => { isEnvDev && console.error('SSE error'); syncConnLost(true); };
|
||||
eventSourceRef.current = es;
|
||||
};
|
||||
|
||||
// ── Initialization ────────────────────────────────────────────────────────
|
||||
/** Initialization */
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (connectionLostRef.current) { openEventSource(); return; }
|
||||
if (connectionLostRef.current) {
|
||||
openEventSource();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (gameInherited) {
|
||||
const serverData = await connectQuery.refetch().then(r => {
|
||||
@@ -219,7 +227,7 @@ const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// ── UI-facing callbacks ───────────────────────────────────────────────────
|
||||
/** UI-facing callbacks */
|
||||
|
||||
const onClick = async coords => {
|
||||
const activeColor = activePlayerRef.current ? 'blue' : 'red';
|
||||
@@ -230,7 +238,10 @@ const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
|
||||
const dataPack = { coords, player: activeColor, bomb: bombSelectedRef.current, resign: null };
|
||||
|
||||
if (connectionLostRef.current) { stepCacheRef.current.push(dataPack); return; }
|
||||
if (connectionLostRef.current) {
|
||||
stepCacheRef.current.push(dataPack);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await stepMutation.mutateAsync(dataPack);
|
||||
@@ -261,4 +272,4 @@ const useServerComm = (gameAssoc, gameInherited, isEnvDev) => {
|
||||
return { onClick, resign };
|
||||
};
|
||||
|
||||
export default useServerComm;
|
||||
export default useServerCommunication;
|
||||
Reference in New Issue
Block a user