GameBoard to react on selected player

- some refactoring
This commit is contained in:
djmil 2023-11-16 18:19:40 +01:00
parent 671e13a41d
commit 8611ede4b4
17 changed files with 247 additions and 279 deletions

View File

@ -26,6 +26,7 @@ export default function App() {
const players = {
leaderboard,
currentUser: user.username,
isCurrentUser: (playerName) => user?.isCurrentUser(playerName) === true ? true : null
};

View File

@ -26,6 +26,9 @@ export function Stone({ color }) {
case Color.black:
return BlackStone();
case '':
return; // no color :)
default:
console.warn("Unknown color: ", color)
}

View File

@ -4,16 +4,7 @@ import { NavLink, Routes, Route } from 'react-router-dom';
import NewGame from './games/view/NewGame';
import { GameProposalSelector, ActiveGameSelector, GameArchiveSelector } from './games/view/GameSelector';
import Create from './games/action/Create';
import Reject from './games/action/Reject';
import Cancel from './games/action/Cancel';
import Accept from './games/action/Accept';
import Surrender from './games/action/Surrender';
import { DrawRequest, DrawAccept, DrawReject } from './games/action/Draw';
import Backward from './games/action/Backward';
import Forward from './games/action/Forward';
import { Create, Accept, Reject, Cancel, DrawRequest, DrawAccept, DrawReject, Surrender, Backward, Forward } from './games/ActionPanel';
import GameBoard from './games/GameBoard';
import Message2Opponent from './games/Message2Opponent';
import Counter from '../components/Counter';
@ -29,11 +20,11 @@ export default function Games({ context: { gamesReducer, gamesApi }, players })
<div className='left-side'>
<ViewSelector games={games} />
<ViewProvider gamesReducer={gamesReducer} players={players} />
<ViewProvider dispatchGames={dispatchGames} players={players} />
</div>
<div className='right-side'>
<ActionPanel gamesApi={gamesApi} players={players} />
<ActionPanel gamesApi={gamesApi} />
{/* <GameMessage /> */}
<GameBoard />
<Message2Opponent dispatchGames={dispatchGames} />
@ -57,15 +48,14 @@ function ViewSelector({ games }) {
)
}
function ViewProvider({ gamesReducer, players }) {
const [/*games*/, dispatchGames] = gamesReducer;
function ViewProvider({ dispatchGames, players }) {
return (
<div className='ViewProvider'>
<Routes>
<Route path='new' element={
<NewGame onSelectPlayer={(whitePlayer, blackPlayer) => dispatchGames({ type: 'nextNewGame', whitePlayer, blackPlayer })}
<NewGame setOpponent={(opponentName, opponentColor) => dispatchGames({ type: 'nextNewGame', opponentName, opponentColor })}
players={players}
/>
} />
@ -87,15 +77,13 @@ function ViewProvider({ gamesReducer, players }) {
)
}
function ActionPanel({ players, gamesApi }) {
function ActionPanel({ gamesApi }) {
return (
<div className='ActionPanel'>
<Routes>
<Route path='new' element={
<Create isCurrentUser={players.isCurrentUser}
onClick={(reqParams) => gamesApi.pushNewGame(reqParams)}
/>
<Create onClick={(reqParams) => gamesApi.pushNewGame(reqParams)} />
} />
<Route path='proposal' element={[
@ -146,4 +134,4 @@ function countGames(gamesList) {
}
return awaiting;
}
}

View File

@ -0,0 +1,184 @@
import React, { useContext } from 'react';
import { GamesContext } from '../../context/games';
import Wobler from '../../components/Wobler';
/*
* NewGame actoins
*/
export function Create({ onClick }) {
const games = useContext(GamesContext);
const hasOpponent = games.newGame.opponentName && games.newGame.opponentColor;
const prepareNewGameRequest = () => {
if (!hasOpponent)
return alert("You have to select an opponent");
const reqParams = {
opponentName: games.newGame.opponentName,
opponentColor: games.newGame.opponentColor,
board: null, // default board configuration
message: games.newGame.message
}
onClick(reqParams);
}
return (
<button className={'Create' + (hasOpponent ? ' ready' : '')}
onClick={prepareNewGameRequest}
>
<Wobler text="Create" dance={games.isPushingNewGame} />
</button>
)
}
/*
* GameProposal actions
*/
export function Accept({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.proposal.selectedUUID });
const isReady = selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_YOU' ? true : '';
if (selectedGame?.status !== 'GAME_PROPOSAL_WAIT_FOR_OPPONENT')
return (
<button className={'Accept' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select some GameProposal')}
>
<Wobler text="Accept" dance={games.isPushingGameProposalAccept} />
</button>
)
}
export function Reject({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.proposal.selectedUUID });
const isReady = selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_YOU' ? true : '';
if (selectedGame?.status !== 'GAME_PROPOSAL_WAIT_FOR_OPPONENT')
return (
<button className={'Reject' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select some GameProposal')}
>
<Wobler text="Reject" dance={games.isPushingGameProposalReject} />
</button>
)
}
export function Cancel({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.proposal.selectedUUID });
const isReady = selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_OPPONENT' ? true : '';
if (selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_OPPONENT')
return (
<button className={'Cancel' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select pending GameProposal')}
>
<Wobler text="Cancel" dance={games.isPushingGameProposalCancel} />
</button>
)
}
/*
* Game actions
*/
export function Surrender({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.active.selectedUUID });
const gameStatus = selectedGame?.status;
const isReady = (gameStatus === 'GAME_BOARD_WAIT_FOR_OPPONENT' || gameStatus === 'GAME_BOARD_WAIT_FOR_YOU') ? true : '';
if (gameStatus === 'DRAW_REQUEST_WAIT_FOR_YOU' || gameStatus === 'DRAW_REQUEST_WAIT_FOR_OPPONENT')
return; // You shall not surrender if there is an active tie negotiations
return (
<button className={'Surrender' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select a game')}
>
<Wobler text="Surrender" dance={games.isPushingGameSurrender} />
</button>
)
}
/*
* Game actions: Draw
*/
export function DrawRequest({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.active.selectedUUID });
const gameStatus = selectedGame?.status;
const isReady = gameStatus === 'GAME_BOARD_WAIT_FOR_YOU' ? true : '';
const checkStatus = () => {
if (!selectedGame)
return alert('You have to select a game');
if (gameStatus === 'DRAW_REQUEST_WAIT_FOR_OPPONENT')
return alert('A draw was alredy offered to the opponent');
if (!isReady)
return alert('You can ask for a draw only during your turn');
onClick(selectedGame.uuid);
}
if (gameStatus === 'DRAW_REQUEST_WAIT_FOR_YOU')
return; // You can not send counter draw request
return (
<button className={'Draw' + (isReady && ' ready')} onClick={() => checkStatus()} >
<Wobler text="Draw?" dance={games.isPushingGameDrawRequest} />
</button >
)
}
export function DrawAccept({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.active.selectedUUID });
const gameStatus = selectedGame?.status;
if (gameStatus === 'DRAW_REQUEST_WAIT_FOR_YOU')
return (
<button className='Draw accept' onClick={() => onClick(selectedGame.uuid)} >
<Wobler text="Draw accept" dance={games.isPushingGameDrawAccept} />
</button>
)
}
export function DrawReject({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.active.selectedUUID });
if (selectedGame?.status === 'DRAW_REQUEST_WAIT_FOR_YOU')
return (
<button className='Draw reject' onClick={() => onClick(selectedGame.uuid)} >
<Wobler text="Reject" dance={games.isPushingGameDrawReject} />
</button>
)
}
/*
* GameArchive actions
*/
export function Backward() {
return <button className='Backward' disabled>Backward</button>
}
export function Forward() {
return <button className='Forward' disabled>Forward</button>
}

View File

@ -9,19 +9,30 @@ export default function GameBoard() {
const games = useContext(GamesContext);
const {pathname} = useLocation();
var whiteName = '';
var blackName = '';
let opponentName = '';
let opponentColor = Color.white; // defaut color
if (matchPath('/games/new', pathname)) {
whiteName = games.newGame.whitePlayer;
blackName = games.newGame.blackPlayer;
opponentName = games.newGame.opponentName;
opponentColor = games.newGame.opponentColor;
}
// if (matchPath('/games/proposal', pathname)) {
// const selectedGame = games.findGame({ uuid: games.proposal.selectedUUID });
// const opponentColor = selectedGame
// whiteName = selectedGame.newGame.whitePlayer;
// blackName = games.newGame.blackPlayer;
// }
const myColor = Color.opposite(opponentColor);
return (
<div className='GameBoard'>
<Player color={Color.white} name={whiteName} />
<Player color={opponentColor} name={opponentName} />
<Board />
<Player color={Color.black} name={blackName} />
<Player color={myColor} name='MyName' />
</div>
)
}

View File

@ -18,11 +18,11 @@ export default function Message2Opponent({ dispatchGames }) {
var externalValue = '';
if (matchPath('/games/new', pathname))
externalValue = games.newGame.message2opponent;
externalValue = games.newGame.message;
else if (matchPath('/games/proposal', pathname))
externalValue = games.proposal.message2opponent;
externalValue = games.proposal.message;
else if (matchPath('/games/active', pathname))
externalValue = games.active.message2opponent;
externalValue = games.active.message;
if (value !== externalValue)
setValue(externalValue);
@ -30,17 +30,17 @@ export default function Message2Opponent({ dispatchGames }) {
/* --- */
const sync = (message2opponent) => {
const sync = (message) => {
syncTimeoutRef.current = null;
if (matchPath('/games/new', pathname))
return dispatchGames({ type: 'nextNewGame', message2opponent });
return dispatchGames({ type: 'nextNewGame', message });
if (matchPath('/games/proposal', pathname))
return dispatchGames({ type: 'nextProposal', message2opponent });
return dispatchGames({ type: 'nextProposal', message });
if (matchPath('/games/active', pathname))
return dispatchGames({ type: 'nextActive', message2opponent });
return dispatchGames({ type: 'nextActive', message });
console.warn('unknown path');
}

View File

@ -1,19 +0,0 @@
import React, { useContext } from 'react';
import Wobler from '../../../components/Wobler';
import { GamesContext } from '../../../context/games';
export default function Accept({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.proposal.selectedUUID });
const isReady = selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_YOU' ? true : '';
if (selectedGame?.status !== 'GAME_PROPOSAL_WAIT_FOR_OPPONENT')
return (
<button className={'Accept' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select some GameProposal')}
>
<Wobler text="Accept" dance={games.isPushingGameProposalAccept} />
</button>
)
}

View File

@ -1,6 +0,0 @@
import React from 'react';
export default function Backward() {
return <button className='Backward' disabled>Backward</button>
}

View File

@ -1,19 +0,0 @@
import React, { useContext } from 'react';
import Wobler from '../../../components/Wobler';
import { GamesContext } from '../../../context/games';
export default function Cancel({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.proposal.selectedUUID });
const isReady = selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_OPPONENT' ? true : '';
if (selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_OPPONENT')
return (
<button className={'Cancel' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select pending GameProposal')}
>
<Wobler text="Cancel" dance={games.isPushingGameProposalCancel} />
</button>
)
}

View File

@ -1,63 +0,0 @@
import React, { useContext } from 'react';
import { GamesContext } from '../../../context/games';
import Wobler from '../../../components/Wobler';
import { Color } from '../../../components/Checkers';
export default function Create({ isCurrentUser, onClick }) {
const games = useContext(GamesContext);
const hasPlayers = checkPlayers(games.newGame);
const hasCurrentUser = checkCurrentUser(games.newGame, isCurrentUser);
const prepareNewGameRequest = () => {
if (!hasPlayers)
return alert("Black and White players must be selected for the game");
if (!hasCurrentUser)
return alert("You must be one of the selected players");
/*
* Prepare & send NewGame request
*/
const [opponentName, opponentColor] = getOpponent(games.newGame, isCurrentUser);
const reqParams = {
opponentName,
opponentColor,
board: null, // default board configuration
message: games.newGame.message2opponent
}
onClick(reqParams);
}
return (
<button className={'Create' + (hasPlayers && hasCurrentUser ? ' ready' : '')}
onClick={prepareNewGameRequest}
>
<Wobler text="Create" dance={games.isPushingNewGame} />
</button>
)
}
function checkPlayers({ whitePlayer, blackPlayer }) {
return whitePlayer && blackPlayer
&& whitePlayer !== blackPlayer;
}
function checkCurrentUser({ whitePlayer, blackPlayer }, isCurrentUser) {
return isCurrentUser(whitePlayer) || isCurrentUser(blackPlayer);
}
function getOpponent({whitePlayer, blackPlayer}, isCurrentUser) {
if (isCurrentUser(whitePlayer)) {
return [blackPlayer, Color.black];
}
if (isCurrentUser(blackPlayer)) {
return [whitePlayer, Color.white];
}
return ['', ''];
}

View File

@ -1,60 +0,0 @@
import React, { useContext } from 'react';
import Wobler from '../../../components/Wobler';
import { GamesContext } from '../../../context/games';
export function DrawRequest({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.active.selectedUUID });
const gameStatus = selectedGame?.status;
const isReady = gameStatus === 'GAME_BOARD_WAIT_FOR_YOU' ? true : '';
const checkStatus = () => {
if (!selectedGame)
return alert('You have to select a game');
if (gameStatus === 'DRAW_REQUEST_WAIT_FOR_OPPONENT')
return alert('A draw was alredy offered to the opponent');
if (!isReady)
return alert('You can ask for a draw only during your turn');
onClick(selectedGame.uuid);
}
if (gameStatus === 'DRAW_REQUEST_WAIT_FOR_YOU')
return; // You can not send counter draw request
return (
<button className={'Draw' + (isReady && ' ready')} onClick={() => checkStatus()} >
<Wobler text="Draw?" dance={games.isPushingGameDrawRequest} />
</button >
)
}
export function DrawAccept({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.active.selectedUUID });
const gameStatus = selectedGame?.status;
if (gameStatus === 'DRAW_REQUEST_WAIT_FOR_YOU')
return (
<button className='Draw accept' onClick={() => onClick(selectedGame.uuid)} >
<Wobler text="Draw accept" dance={games.isPushingGameDrawAccept} />
</button>
)
}
export function DrawReject({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.active.selectedUUID });
if (selectedGame?.status === 'DRAW_REQUEST_WAIT_FOR_YOU')
return (
<button className='Draw reject' onClick={() => onClick(selectedGame.uuid)} >
<Wobler text="Reject" dance={games.isPushingGameDrawReject} />
</button>
)
}

View File

@ -1,6 +0,0 @@
import React from 'react';
export default function Forward() {
return <button className='Forward' disabled>Forward</button>
}

View File

@ -1,19 +0,0 @@
import React, { useContext } from 'react';
import Wobler from '../../../components/Wobler';
import { GamesContext } from '../../../context/games';
export default function Reject({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.proposal.selectedUUID });
const isReady = selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_YOU' ? true : '';
if (selectedGame?.status !== 'GAME_PROPOSAL_WAIT_FOR_OPPONENT')
return (
<button className={'Reject' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select some GameProposal')}
>
<Wobler text="Reject" dance={games.isPushingGameProposalReject} />
</button>
)
}

View File

@ -1,22 +0,0 @@
import React, { useContext } from 'react';
import Wobler from '../../../components/Wobler';
import { GamesContext } from '../../../context/games';
export default function Surrender({ onClick }) {
const games = useContext(GamesContext);
const selectedGame = games.findGame({ uuid: games.active.selectedUUID });
const gameStatus = selectedGame?.status;
const isReady = (gameStatus === 'GAME_BOARD_WAIT_FOR_OPPONENT' || gameStatus === 'GAME_BOARD_WAIT_FOR_YOU') ? true : '';
if (gameStatus === 'DRAW_REQUEST_WAIT_FOR_YOU' || gameStatus === 'DRAW_REQUEST_WAIT_FOR_OPPONENT')
return; // You shall not surrender if there is an active tie negotiations
return (
<button className={'Surrender' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select a game')}
>
<Wobler text="Surrender" dance={games.isPushingGameSurrender} />
</button>
)
}

View File

@ -3,10 +3,20 @@ import React, { useContext } from 'react';
import { GamesContext } from '../../../context/games';
import DropdownList from '../../../components/DropdownList';
import { WhiteStone, BlackStone } from '../../../components/Checkers';
import { Color, WhiteStone, BlackStone } from '../../../components/Checkers';
export default function NewGame({ players, onSelectPlayer }) {
export default function NewGame({ players, setOpponent }) {
const games = useContext(GamesContext);
const [whitePlayer, blackPlayer] = (() => {
if (games.newGame.opponentColor === Color.white)
return [games.newGame.opponentName, players.currentUser];
if (games.newGame.opponentColor === Color.black)
return [players.currentUser, games.newGame.opponentName];
return ['', ''];
})(); // <<-- Execute!
/*
* Name options
@ -24,37 +34,22 @@ export default function NewGame({ players, onSelectPlayer }) {
const blackOptions = Array(nameOptions)
blackOptions.push(<option key='default' value=''>{'black player …'}</option>)
/*
* Radiobutton
*/
const radioButton = (whitePlayer, blackPlayer) => {
if (whitePlayer !== '' && whitePlayer === games.newGame.blackPlayer) {
blackPlayer = '';
}
if (blackPlayer !== '' && blackPlayer === games.newGame.whitePlayer) {
whitePlayer = '';
}
onSelectPlayer(whitePlayer, blackPlayer);
}
const setWhitePlayer = (name) => {
radioButton(name, games.newGame.blackPlayer);
}
const setBlackPlayer = (name) => {
radioButton(games.newGame.whitePlayer, name);
}
/*
* The Component
*/
const onSelect = (name, color) => {
if (players.isCurrentUser(name))
setOpponent(games.newGame.opponentName, Color.opposite(color));
else
setOpponent(name, color);
}
return (
<div className='NewGame'>
<WhiteStone />
<DropdownList selected={games.newGame.whitePlayer} onSelect={setWhitePlayer} optionsList={whiteOptions} />
<DropdownList selected={whitePlayer} onSelect={(name) => onSelect(name, Color.white)} optionsList={whiteOptions} />
<i>- vs -</i>
<DropdownList selected={games.newGame.blackPlayer} onSelect={setBlackPlayer} optionsList={blackOptions} />
<DropdownList selected={blackPlayer} onSelect={(name) => onSelect(name, Color.black)} optionsList={blackOptions} />
<BlackStone />
</div>
)

View File

@ -5,19 +5,19 @@ export const gamesInitialState = {
gamesList: null,
newGame: {
whitePlayer: '',
blackPlayer: '',
message2opponent: '',
opponentName: '',
opponentColor: '',
message: '',
},
proposal: {
selectedUUID: null,
message2opponent: '',
message: '',
},
active: {
selectedUUID: null,
message2opponent: '',
message: '',
},
archive: {

View File

@ -15,7 +15,7 @@ function reducer(state, action) {
case 'parse':
return {
...state,
username: action.userJson.username
username: action.userJson.holdingIdentity.name
};
default: