diff --git a/webapp/src/api/games.js b/webapp/src/api/games.js
index 6f3cb01..1cb0d3b 100644
--- a/webapp/src/api/games.js
+++ b/webapp/src/api/games.js
@@ -73,11 +73,12 @@ export default function useGamesApi() {
const pushGameMove = ({ uuid, move, message }) => {
doPushing(`/api/game/${uuid}/move`, 'PUT', { move, message }, {
- onPushing: (isPushing) => dispatchGuide({ type: 'UUIDpushing', uuid, what: isPushing && {moveFrom: move[0], moveTo: move[1]} }),
+ onPushing: (isPushing) => dispatchGuide({ type: 'UUIDpushing', uuid, what: isPushing && { moveFrom: move[0], moveTo: move[1] } }),
+ onBadReq: (message) => dispatchGuide({ type: 'UUIDerror', uuid, error: { message, moveFrom: move[0], moveTo: move[1] } }),
onSuccess: (game) => {
dispatchState({ type: 'update', game });
- dispatchGuide({ type: 'UUIDmessage', uuid, message: ''});
- }
+ dispatchGuide({ type: 'UUIDmessage', uuid, message: '' });
+ },
})
}
diff --git a/webapp/src/container/Games.css b/webapp/src/container/Games.css
index 5b5dca7..27ce429 100644
--- a/webapp/src/container/Games.css
+++ b/webapp/src/container/Games.css
@@ -97,10 +97,25 @@
.GameBoard {
padding-left: 30px;
+ width: 275px;
}
.Message2Opponent {
margin: 10px;
margin-left: 30px;
- width: 270px;
+ width: 275px;
+}
+
+.BadMove {
+ position: fixed;
+ width: 275px;
+ top: 250px;
+ cursor: default; /* disable 'I beam' cursor change */
+ text-align: center;
+ border-radius: 3px;
+ background-color: rgba(255, 80, 80, 0.3);
+}
+
+.BadMove:hover {
+ background-color: rgba(255, 80, 80, 0.7);
}
\ No newline at end of file
diff --git a/webapp/src/container/Games.jsx b/webapp/src/container/Games.jsx
index 7a3e880..44d5f4f 100644
--- a/webapp/src/container/Games.jsx
+++ b/webapp/src/container/Games.jsx
@@ -34,7 +34,6 @@ export default function Games({ games, players }) {
- {/* */}
@@ -156,10 +155,11 @@ function GameBoardRoutes({ dispatchGuide, gamesApi, username }) {
const games = useContext(GamesStateContext);
const guide = useContext(GamesGuideContext);
- const fromUUID = (uuid) => (!uuid) ? [{}, null] :
+ const fromUUID = (uuid) => (!uuid) ? [{}, null, null] :
[
games.find((game) => game.uuid === uuid) || {}, // game
- guide.UUIDpushing[uuid] // pushing
+ guide.UUIDpushing[uuid], // pushing
+ guide.UUIDerror[uuid] // error (aka bad move)
];
const onStoneClick = (uuid, cellId) => {
@@ -191,6 +191,7 @@ function GameBoardRoutes({ dispatchGuide, gamesApi, username }) {
username={username}
getGame={() => fromUUID(guide.selectedUUID.active)}
onStoneMove={(uuid, move) => gamesApi.pushGameMove({ uuid, move, message: guide.UUIDmessage[uuid] })}
+ dispatchGuide={dispatchGuide}
/>
} />
diff --git a/webapp/src/container/games/BadMove.jsx b/webapp/src/container/games/BadMove.jsx
new file mode 100644
index 0000000..16dac2d
--- /dev/null
+++ b/webapp/src/container/games/BadMove.jsx
@@ -0,0 +1,14 @@
+import React from 'react';
+
+export default function BadMove({ dispatchGuide, uuid, message }) {
+ if (!dispatchGuide || !uuid || !message)
+ return;
+
+ return (
+
dispatchGuide({ type: 'UUIDerror', uuid, error: {} })}
+ >
+ {message}
+
+ )
+}
\ No newline at end of file
diff --git a/webapp/src/container/games/GameBoard.jsx b/webapp/src/container/games/GameBoard.jsx
index 437048e..afd8303 100644
--- a/webapp/src/container/games/GameBoard.jsx
+++ b/webapp/src/container/games/GameBoard.jsx
@@ -1,10 +1,10 @@
import React from 'react';
-
import { Color, Player, Board } from '../../components/Checkers';
import './GameBoard.css';
+import BadMove from './BadMove';
-export default function GameBoard({ username, getGame, onStoneClick, onStoneMove }) {
- const [game, isPushing] = getGame();
+export default function GameBoard({ dispatchGuide, username, getGame, onStoneClick, onStoneMove }) {
+ const [game, isPushing, error] = getGame();
const myName = game.opponentName ? username : '';
const opponentColor = Color.opposite(game.myColor);
@@ -14,11 +14,17 @@ export default function GameBoard({ username, getGame, onStoneClick, onStoneMove
(cellId) => onStoneClick(game.uuid, cellId);
const optionalOnStoneMove = (typeof onStoneMove !== 'function' || isPushing) ? null :
- (move) => {
+ (move) => {
if (move[0] !== move[1] && game.board[move[1]] === undefined)
onStoneMove(game.uuid, move)
};
+ const currMove = (() => {
+ if (isPushing) return [isPushing.moveFrom, isPushing.moveTo];
+ if (error?.moveFrom && error?.moveTo) return [error.moveFrom, error.moveTo];
+ return [];
+ })(); // <<-- execute
+
return (
@@ -37,6 +43,7 @@ export default function GameBoard({ username, getGame, onStoneClick, onStoneMove
color={game.myColor || Color.white}
name={myName}
/>
+
)
}
\ No newline at end of file
diff --git a/webapp/src/hook/api.js b/webapp/src/hook/api.js
index 2e5b71b..2fc88a3 100644
--- a/webapp/src/hook/api.js
+++ b/webapp/src/hook/api.js
@@ -21,7 +21,7 @@ export function usePolling(uri, { onSuccess, onPolling }, mode = null) {
setPolling(true);
if (onPolling)
onPolling(true);
-
+
initialPollRef.current = false;
fetch(uri)
@@ -62,7 +62,7 @@ export function usePolling(uri, { onSuccess, onPolling }, mode = null) {
}, [initialPoll, mode, intervalTimerId, isPolling, pollData, stopPollInterval]);
}
-export async function doPushing(uri, method, data, { onSuccess, onPushing }) {
+export async function doPushing(uri, method, data, { onSuccess, onBadReq, onPushing }) {
if (onPushing)
onPushing(true);
@@ -75,14 +75,22 @@ export async function doPushing(uri, method, data, { onSuccess, onPushing }) {
body: JSON.stringify(data), // body data type must match "Content-Type" header
});
+ if (response.status === 400 && onBadReq) {
+ const content = (response.headers.get('Content-Type') === "application/json")
+ ? await response.json()
+ : {};
+
+ return onBadReq(content.message);
+ }
+
if (!response.ok) {
return console.warn(`Unexpected response status: ${response.status}`, response);
}
if (onSuccess) {
- var content = (response.headers.get('Content-Type') === "application/json")
+ const content = (response.headers.get('Content-Type') === "application/json")
? await response.json()
- : null;
+ : {};
onSuccess(content);
}
diff --git a/webapp/src/reducer/games.js b/webapp/src/reducer/games.js
index 14aa963..a7092d0 100644
--- a/webapp/src/reducer/games.js
+++ b/webapp/src/reducer/games.js
@@ -76,6 +76,9 @@ export const gamesGuideTemplate = {
UUIDpushing: { // UUIDpushing[uuid]
},
+ UUIDerror: { // UUIDerror[uuid]
+ },
+
isPolling: false,
};
@@ -116,6 +119,12 @@ function gamesGuideReducer(state, action) {
return next;
}
+ case 'UUIDerror': {
+ const next = { ...state };
+ next.UUIDerror[action.uuid] = action.error;
+ return next;
+ }
+
default:
throw Error('GamesGuide: unknown action.type: ' + action.type);
}