chg: dev: massive refactor on fetches - create centralized dataProvider #7
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import { useLobbyDataProvider } from '@mine-hooks';
|
||||||
|
|
||||||
const DIALOG_SX = {
|
const DIALOG_SX = {
|
||||||
'& .MuiDialog-paper': {
|
'& .MuiDialog-paper': {
|
||||||
@@ -39,7 +40,7 @@ const formatSince = isoStr => {
|
|||||||
return `${diff} min ago`;
|
return `${diff} min ago`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc, isEnvDev = false }) => {
|
||||||
const [players, setPlayers] = useState([]);
|
const [players, setPlayers] = useState([]);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -49,6 +50,7 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
|||||||
const [declinedMsg, setDeclinedMsg] = useState('');
|
const [declinedMsg, setDeclinedMsg] = useState('');
|
||||||
const [waitingCountdown, setWaitingCountdown] = useState(0);
|
const [waitingCountdown, setWaitingCountdown] = useState(0);
|
||||||
const declinedTimerRef = useRef(null);
|
const declinedTimerRef = useRef(null);
|
||||||
|
const { waitingPlayersQuery, challengeMutation } = useLobbyDataProvider();
|
||||||
|
|
||||||
const addPlayer = useCallback(entry => {
|
const addPlayer = useCallback(entry => {
|
||||||
setPlayers(prev =>
|
setPlayers(prev =>
|
||||||
@@ -66,20 +68,21 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
|||||||
if (!open) return;
|
if (!open) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setSnapshotLoaded(false);
|
setSnapshotLoaded(false);
|
||||||
fetch('/api/game/waiting')
|
|
||||||
.then(r => r.json())
|
waitingPlayersQuery.refetch().then(result => {
|
||||||
.then(data => {
|
if (result.data) {
|
||||||
// Filter out current user's game from the snapshot
|
// Filter out current user's game from the snapshot
|
||||||
const filtered = data.filter(p => p.gameAssoc !== currentGameAssoc);
|
const filtered = result.data.filter(p => p.gameAssoc !== currentGameAssoc);
|
||||||
setPlayers(filtered);
|
setPlayers(filtered);
|
||||||
|
}
|
||||||
setSnapshotLoaded(true);
|
setSnapshotLoaded(true);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
}).catch(() => {
|
||||||
.catch(() => {
|
|
||||||
setPlayers([]);
|
setPlayers([]);
|
||||||
setSnapshotLoaded(true);
|
setSnapshotLoaded(true);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [open, refreshKey, currentGameAssoc]);
|
}, [open, refreshKey, currentGameAssoc]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -107,6 +110,13 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
|||||||
return () => es.close();
|
return () => es.close();
|
||||||
}, [open, snapshotLoaded, addPlayer, removePlayer, currentGameAssoc]);
|
}, [open, snapshotLoaded, addPlayer, removePlayer, currentGameAssoc]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (challengeMutation.isError) {
|
||||||
|
setChallengingGameAssoc(null);
|
||||||
|
setWaitingCountdown(0);
|
||||||
|
}
|
||||||
|
}, [challengeMutation.isError]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handler = () => {
|
const handler = () => {
|
||||||
setChallengingGameAssoc(null);
|
setChallengingGameAssoc(null);
|
||||||
@@ -138,14 +148,10 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
|||||||
setChallengingGameAssoc(player.gameAssoc);
|
setChallengingGameAssoc(player.gameAssoc);
|
||||||
setDeclinedMsg('');
|
setDeclinedMsg('');
|
||||||
setWaitingCountdown(30);
|
setWaitingCountdown(30);
|
||||||
fetch('/api/game/challenge/' + player.gameAssoc, {
|
|
||||||
method: 'POST',
|
challengeMutation.mutate(
|
||||||
headers: { 'Content-Type': 'application/json' },
|
{ targetGameAssoc: player.gameAssoc, challengerGameAssoc: currentGameAssoc }
|
||||||
body: JSON.stringify({ challengerGameAssoc: currentGameAssoc }),
|
);
|
||||||
}).catch(() => {
|
|
||||||
setChallengingGameAssoc(null);
|
|
||||||
setWaitingCountdown(0);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const visible = players
|
const visible = players
|
||||||
@@ -156,8 +162,9 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
|||||||
const hasMore = 5 < visible.length;
|
const hasMore = 5 < visible.length;
|
||||||
|
|
||||||
// Debug: log if currentGameAssoc is undefined or if current user appears
|
// Debug: log if currentGameAssoc is undefined or if current user appears
|
||||||
if ('development' === process.env.NODE_ENV && 0 < players.length) {
|
if (isEnvDev && 0 < players.length) {
|
||||||
const userInList = players.find(p => p.gameAssoc === currentGameAssoc);
|
const userInList = players.find(p => p.gameAssoc === currentGameAssoc);
|
||||||
|
|
||||||
if (userInList) {
|
if (userInList) {
|
||||||
console.warn('[OnlinePlayersDialog] Current user appears in players list:', { currentGameAssoc, userInList });
|
console.warn('[OnlinePlayersDialog] Current user appears in players list:', { currentGameAssoc, userInList });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const GridControl = ({ gameAssoc, onClick, resign }) => {
|
|||||||
} = useGame();
|
} = useGame();
|
||||||
|
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
const shareUrl = gameAssoc ? `${window.location.origin}/battle/${gameAssoc}` : null;
|
const shareUrl = gameAssoc ? `${window.location.origin}/play/${gameAssoc}` : null;
|
||||||
const isAuthenticated = '1' === document.getElementById('mine-wrapper')?.dataset.isAuthenticated;
|
const isAuthenticated = '1' === document.getElementById('mine-wrapper')?.dataset.isAuthenticated;
|
||||||
|
|
||||||
const handleShare = () => {
|
const handleShare = () => {
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ export { default as useGameRefs } from './useGameRefs';
|
|||||||
export { default as useGameState } from './useGameState';
|
export { default as useGameState } from './useGameState';
|
||||||
export { default as useServerCommunication } from './useServerCommunication';
|
export { default as useServerCommunication } from './useServerCommunication';
|
||||||
export { default as useStepTimer } from './useStepTimer';
|
export { default as useStepTimer } from './useStepTimer';
|
||||||
|
export { default as useGameDataProvider, useLobbyDataProvider } from './useGameDataProvider';
|
||||||
|
|||||||
114
assets/js/mine-seeker/hooks/useGameDataProvider.js
Normal file
114
assets/js/mine-seeker/hooks/useGameDataProvider.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game Data Provider Hook
|
||||||
|
* Centralized API communication layer for game-related queries and mutations
|
||||||
|
*/
|
||||||
|
const useGameDataProvider = gameAssoc => {
|
||||||
|
// Queries
|
||||||
|
const connectQuery = useQuery({
|
||||||
|
queryKey: ['game-connect', gameAssoc],
|
||||||
|
queryFn: () => fetch(`/api/game/connect/${gameAssoc}`)
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(b64 => JSON.parse(window.atob(b64))),
|
||||||
|
enabled: false,
|
||||||
|
retry: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mutations
|
||||||
|
const startMutation = useMutation({
|
||||||
|
mutationFn: () => fetch('/api/game/start', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ gameAssoc }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const joinMutation = useMutation({
|
||||||
|
mutationFn: () => fetch(`/api/game/join/${gameAssoc}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const stepMutation = useMutation({
|
||||||
|
mutationFn: dataPack => fetch(`/api/game/step/${gameAssoc}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(dataPack),
|
||||||
|
}).then(r => r.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const heartbeatMutation = useMutation({
|
||||||
|
mutationFn: color => fetch(`/api/game/heartbeat/${gameAssoc}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ color }),
|
||||||
|
}).then(r => r.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const challengeRespondMutation = useMutation({
|
||||||
|
mutationFn: ({ challengerGameAssoc, accepted, targetGameAssoc }) => fetch('/api/game/challenge/respond/' + challengerGameAssoc, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ accepted, targetGameAssoc }),
|
||||||
|
}).then(r => r.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const leaveMutation = useMutation({
|
||||||
|
mutationFn: () => fetch(`/api/game/leave/${gameAssoc}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}).then(r => r.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Queries
|
||||||
|
connectQuery,
|
||||||
|
// Mutations
|
||||||
|
startMutation,
|
||||||
|
joinMutation,
|
||||||
|
stepMutation,
|
||||||
|
heartbeatMutation,
|
||||||
|
challengeRespondMutation,
|
||||||
|
leaveMutation,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lobby Data Provider Hook
|
||||||
|
* Centralized API communication layer for lobby-related queries and mutations
|
||||||
|
*/
|
||||||
|
export const useLobbyDataProvider = () => {
|
||||||
|
const waitingPlayersQuery = useQuery({
|
||||||
|
queryKey: ['game-waiting'],
|
||||||
|
queryFn: () => fetch('/api/game/waiting')
|
||||||
|
.then(r => r.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const challengeMutation = useMutation({
|
||||||
|
mutationFn: ({ targetGameAssoc, challengerGameAssoc }) => fetch(`/api/game/challenge/${targetGameAssoc}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ challengerGameAssoc }),
|
||||||
|
}).then(r => r.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Queries
|
||||||
|
waitingPlayersQuery,
|
||||||
|
// Mutations
|
||||||
|
challengeMutation,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useGameDataProvider;
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
|
||||||
import { useGame } from '@mine-contexts';
|
import { useGame } from '@mine-contexts';
|
||||||
import { DESC, IMAGES } from '@mine-utils';
|
import { DESC, IMAGES } from '@mine-utils';
|
||||||
import useStepTimer from './useStepTimer';
|
import useStepTimer from './useStepTimer';
|
||||||
|
import useGameDataProvider from './useGameDataProvider';
|
||||||
import { ChallengeCountdown, WaitingOverlayContent } from '@mine-components';
|
import { ChallengeCountdown, WaitingOverlayContent } from '@mine-components';
|
||||||
|
|
||||||
const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev) => {
|
const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev) => {
|
||||||
@@ -28,6 +28,17 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
cells,
|
cells,
|
||||||
} = useGame();
|
} = useGame();
|
||||||
|
|
||||||
|
/** Get all API queries and mutations from data provider */
|
||||||
|
const {
|
||||||
|
connectQuery,
|
||||||
|
startMutation,
|
||||||
|
joinMutation,
|
||||||
|
stepMutation,
|
||||||
|
heartbeatMutation,
|
||||||
|
challengeRespondMutation,
|
||||||
|
leaveMutation,
|
||||||
|
} = useGameDataProvider(gameAssoc);
|
||||||
|
|
||||||
const eventSourceRef = useRef(null);
|
const eventSourceRef = useRef(null);
|
||||||
const rpcUsersRef = useRef(null);
|
const rpcUsersRef = useRef(null);
|
||||||
const stepCacheRef = useRef([]);
|
const stepCacheRef = useRef([]);
|
||||||
@@ -42,40 +53,6 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
|
|
||||||
const HEARTBEAT_INTERVAL_MS = 1500;
|
const HEARTBEAT_INTERVAL_MS = 1500;
|
||||||
|
|
||||||
/** REST mutations / queries */
|
|
||||||
|
|
||||||
const connectQuery = useQuery({
|
|
||||||
queryKey: ['game-connect', gameAssoc],
|
|
||||||
queryFn: () => fetch('/api/game/connect/' + gameAssoc)
|
|
||||||
.then(r => r.text())
|
|
||||||
.then(b64 => JSON.parse(window.atob(b64))),
|
|
||||||
enabled: false,
|
|
||||||
retry: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const startMutation = useMutation({
|
|
||||||
mutationFn: () => fetch('/api/game/start', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ gameAssoc }),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const joinMutation = useMutation({
|
|
||||||
mutationFn: () => fetch('/api/game/join/' + gameAssoc, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
}).catch(e => isEnvDev && console.error('Join error', e)),
|
|
||||||
});
|
|
||||||
|
|
||||||
const stepMutation = useMutation({
|
|
||||||
mutationFn: dataPack => fetch('/api/game/step/' + gameAssoc, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(dataPack),
|
|
||||||
}).then(r => r.json()),
|
|
||||||
});
|
|
||||||
|
|
||||||
/** Game-start helpers (triggered by server events) */
|
/** Game-start helpers (triggered by server events) */
|
||||||
|
|
||||||
const wInit = (revealedCells = [], lastStep = {}, gameState = {}, isGameFinished = false) => {
|
const wInit = (revealedCells = [], lastStep = {}, gameState = {}, isGameFinished = false) => {
|
||||||
@@ -176,7 +153,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
overlayTitle = 'Choose an opponent!';
|
overlayTitle = 'Choose an opponent!';
|
||||||
overlaySubtitle = gameAssoc ? (
|
overlaySubtitle = gameAssoc ? (
|
||||||
<WaitingOverlayContent
|
<WaitingOverlayContent
|
||||||
shareUrl={`${window.location.origin}/battle/${gameAssoc}`}
|
shareUrl={`${window.location.origin}/play/${gameAssoc}`}
|
||||||
currentGameAssoc={gameAssoc}
|
currentGameAssoc={gameAssoc}
|
||||||
/>
|
/>
|
||||||
) : '';
|
) : '';
|
||||||
@@ -188,7 +165,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
Promise.resolve().then(() => setGridReady(true));
|
Promise.resolve().then(() => setGridReady(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeGameStart = (payload, lastStep = {}) => {
|
const makeGameStart = payload => {
|
||||||
/** Don't start a finished game */
|
/** Don't start a finished game */
|
||||||
if (isGameFinishedRef.current) {
|
if (isGameFinishedRef.current) {
|
||||||
return;
|
return;
|
||||||
@@ -242,11 +219,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
const publishHeartbeat = () => {
|
const publishHeartbeat = () => {
|
||||||
const me = webPlayerRef.current;
|
const me = webPlayerRef.current;
|
||||||
if (!me || endRef.current) return;
|
if (!me || endRef.current) return;
|
||||||
fetch('/api/game/heartbeat/' + gameAssoc, {
|
heartbeatMutation.mutate(me);
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ color: me }),
|
|
||||||
}).catch(e => isEnvDev && console.warn('Heartbeat publish failed', e));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const startHeartbeat = () => {
|
const startHeartbeat = () => {
|
||||||
@@ -264,7 +237,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
|
|
||||||
/** Mercure / SSE message handlers */
|
/** Mercure / SSE message handlers */
|
||||||
|
|
||||||
const wSubscribe = (payload, rpcUsers = null, lastStep = null) => {
|
const wSubscribe = (payload, rpcUsers = null) => {
|
||||||
isEnvDev && console.info((payload.user ?? 'user') + ' subscribed');
|
isEnvDev && console.info((payload.user ?? 'user') + ' subscribed');
|
||||||
const firstUser = !rpcUsers;
|
const firstUser = !rpcUsers;
|
||||||
|
|
||||||
@@ -279,7 +252,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
&& (!connectionLostRef.current
|
&& (!connectionLostRef.current
|
||||||
|| (connectionLostRef.current && false === activePlayerRef.current && !endRef.current))
|
|| (connectionLostRef.current && false === activePlayerRef.current && !endRef.current))
|
||||||
) {
|
) {
|
||||||
makeGameStart(payload, lastStep);
|
makeGameStart(payload);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -315,31 +288,31 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
|
|
||||||
const handleAccept = () => {
|
const handleAccept = () => {
|
||||||
clearTimeout(declineTimeout);
|
clearTimeout(declineTimeout);
|
||||||
fetch('/api/game/challenge/respond/' + challengerGameAssoc, {
|
challengeRespondMutation.mutate(
|
||||||
method: 'POST',
|
{ challengerGameAssoc, accepted: true, targetGameAssoc: gameAssoc },
|
||||||
headers: { 'Content-Type': 'application/json' },
|
{
|
||||||
body: JSON.stringify({ accepted: true, targetGameAssoc: gameAssoc }),
|
onSuccess: () => {
|
||||||
}).then(() => {
|
|
||||||
showOverlay('Challenge accepted!', 'Waiting for the challenger to join...');
|
showOverlay('Challenge accepted!', 'Waiting for the challenger to join...');
|
||||||
}).catch(() => {
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDecline = () => {
|
const handleDecline = () => {
|
||||||
clearTimeout(declineTimeout);
|
clearTimeout(declineTimeout);
|
||||||
fetch('/api/game/challenge/respond/' + challengerGameAssoc, {
|
challengeRespondMutation.mutate(
|
||||||
method: 'POST',
|
{ challengerGameAssoc, accepted: false, targetGameAssoc: gameAssoc },
|
||||||
headers: { 'Content-Type': 'application/json' },
|
{
|
||||||
body: JSON.stringify({ accepted: false, targetGameAssoc: gameAssoc }),
|
onSuccess: () => {
|
||||||
}).then(() => {
|
|
||||||
showOverlay('We are waiting for your opponent...', gameAssoc ? (
|
showOverlay('We are waiting for your opponent...', gameAssoc ? (
|
||||||
<WaitingOverlayContent
|
<WaitingOverlayContent
|
||||||
shareUrl={window.location.origin + '/play/' + gameAssoc}
|
shareUrl={`${window.location.origin}/play/${gameAssoc}`}
|
||||||
currentGameAssoc={gameAssoc}
|
currentGameAssoc={gameAssoc}
|
||||||
/>
|
/>
|
||||||
) : '');
|
) : '');
|
||||||
}).catch(() => {
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
declineTimeout = setTimeout(handleDecline, 30000);
|
declineTimeout = setTimeout(handleDecline, 30000);
|
||||||
@@ -404,7 +377,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
if (undefined !== payload.data) {
|
if (undefined !== payload.data) {
|
||||||
wTopic(payload);
|
wTopic(payload);
|
||||||
} else if (undefined === payload.msg) {
|
} else if (undefined === payload.msg) {
|
||||||
wSubscribe(payload, rpcUsersRef.current, lastStepRef.current);
|
wSubscribe(payload, rpcUsersRef.current);
|
||||||
} else {
|
} else {
|
||||||
wUnsubscribe(payload);
|
wUnsubscribe(payload);
|
||||||
}
|
}
|
||||||
@@ -423,7 +396,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
const subscriberJwt = wrapper.dataset.mercureSubscriberJwt;
|
const subscriberJwt = wrapper.dataset.mercureSubscriberJwt;
|
||||||
const url = new URL(hubUrl, window.location.origin);
|
const url = new URL(hubUrl, window.location.origin);
|
||||||
|
|
||||||
url.searchParams.append('topic', 'mineseeker/channel/' + gameAssoc);
|
url.searchParams.append('topic', `mineseeker/channel/${gameAssoc}`);
|
||||||
|
|
||||||
if (subscriberJwt) url.searchParams.append('authorization', subscriberJwt);
|
if (subscriberJwt) url.searchParams.append('authorization', subscriberJwt);
|
||||||
if (eventSourceRef.current) eventSourceRef.current.close();
|
if (eventSourceRef.current) eventSourceRef.current.close();
|
||||||
@@ -452,6 +425,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
openEventSource();
|
openEventSource();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (gameInherited) {
|
if (gameInherited) {
|
||||||
const serverData = await connectQuery.refetch().then(r => {
|
const serverData = await connectQuery.refetch().then(r => {
|
||||||
@@ -497,7 +471,9 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
window.addEventListener('pagehide', () => navigator.sendBeacon('/api/game/leave/' + gameAssoc));
|
window.addEventListener('pagehide', () => {
|
||||||
|
leaveMutation.mutate();
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
stopHeartbeat();
|
stopHeartbeat();
|
||||||
@@ -525,9 +501,11 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
try {
|
try {
|
||||||
const result = await stepMutation.mutateAsync(dataPack);
|
const result = await stepMutation.mutateAsync(dataPack);
|
||||||
applyStep(result);
|
applyStep(result);
|
||||||
|
|
||||||
if (result.uuid && !endRef.current) {
|
if (result.uuid && !endRef.current) {
|
||||||
setGameUuid(result.uuid);
|
setGameUuid(result.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
makeGameEndIfItEnds(result.bluePoints, result.redPoints, false, result.leftMines);
|
makeGameEndIfItEnds(result.bluePoints, result.redPoints, false, result.leftMines);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
isEnvDev && console.error('Step error', e);
|
isEnvDev && console.error('Step error', e);
|
||||||
@@ -537,6 +515,7 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
const clickResign = () => {
|
const clickResign = () => {
|
||||||
const color = activePlayerRef.current ? 'blue' : 'red';
|
const color = activePlayerRef.current ? 'blue' : 'red';
|
||||||
const stepElapsed = getStepElapsed(activePlayerRef.current, isGameRunningRef.current);
|
const stepElapsed = getStepElapsed(activePlayerRef.current, isGameRunningRef.current);
|
||||||
|
|
||||||
stepMutation.mutate(
|
stepMutation.mutate(
|
||||||
{ resign: color, stepElapsed },
|
{ resign: color, stepElapsed },
|
||||||
{
|
{
|
||||||
@@ -551,7 +530,9 @@ const useServerCommunication = (gameAssoc, gameInherited, opponentName, isEnvDev
|
|||||||
|
|
||||||
const resign = () => {
|
const resign = () => {
|
||||||
const activeColor = activePlayerRef.current ? 'blue' : 'red';
|
const activeColor = activePlayerRef.current ? 'blue' : 'red';
|
||||||
|
|
||||||
if (webPlayerRef.current !== activeColor) return;
|
if (webPlayerRef.current !== activeColor) return;
|
||||||
|
|
||||||
showOverlay('Are u sure u want to resign?!', (
|
showOverlay('Are u sure u want to resign?!', (
|
||||||
<div className="resign">
|
<div className="resign">
|
||||||
<a onClick={clickResign}>Yes</a>
|
<a onClick={clickResign}>Yes</a>
|
||||||
|
|||||||
@@ -120,9 +120,11 @@ class MercureController extends AbstractController
|
|||||||
{
|
{
|
||||||
$data = $request->toArray();
|
$data = $request->toArray();
|
||||||
$color = $data['color'] ?? '';
|
$color = $data['color'] ?? '';
|
||||||
|
|
||||||
if ('red' !== $color && 'blue' !== $color) {
|
if ('red' !== $color && 'blue' !== $color) {
|
||||||
return $this->json(['success' => false], Response::HTTP_BAD_REQUEST);
|
return $this->json(['success' => false], Response::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->topicManager->publishHeartbeat($gameAssoc, $color);
|
$this->topicManager->publishHeartbeat($gameAssoc, $color);
|
||||||
|
|
||||||
return $this->json(['success' => true]);
|
return $this->json(['success' => true]);
|
||||||
|
|||||||
Reference in New Issue
Block a user