232 lines
7.1 KiB
JavaScript
232 lines
7.1 KiB
JavaScript
import React, { useContext, useEffect } from 'react';
|
|
import { GamesStateContext, GamesGuideContext } from '../context/games';
|
|
import { Routes, Route, Link } from 'react-router-dom';
|
|
|
|
import NewGame from './games/NewGame';
|
|
import { GameProposalSelector, ActiveGameSelector, GameArchiveSelector } from './games/GameSelector';
|
|
import { Create, Accept, Reject, Cancel, DrawRequest, DrawAccept, DrawReject, Surrender, Backward, Forward } from './games/ActionPanel';
|
|
import GameBoard from './games/GameBoard';
|
|
import { nextStone } from '../components/Checkers';
|
|
import Message2Opponent from './games/Message2Opponent';
|
|
import Counter from '../components/Counter';
|
|
|
|
import './Games.css';
|
|
|
|
export default function Games({ games, players }) {
|
|
const gamesState = games.state;
|
|
const gamesDispatchGuide = games.dispatchGuide;
|
|
|
|
useEffect(() => {
|
|
gamesDispatchGuide({ type: 'sync', gamesState });
|
|
}, [gamesState, gamesDispatchGuide]);
|
|
|
|
return (
|
|
<GamesStateContext.Provider value={gamesState} >
|
|
<GamesGuideContext.Provider value={games.guide} >
|
|
<div className='Games'>
|
|
|
|
<div className='left-side'>
|
|
<ViewSelector />
|
|
<ViewProvider dispatchGuide={gamesDispatchGuide} players={players} />
|
|
</div>
|
|
|
|
<div className='right-side'>
|
|
<ActionPanel gamesApi={games.api} />
|
|
<GameBoardRoutes dispatchGuide={gamesDispatchGuide} gamesApi={games.api} username={players.user.name} />
|
|
<Message2OpponentRoutes dispatchGuide={gamesDispatchGuide} />
|
|
</div>
|
|
|
|
</div >
|
|
</GamesGuideContext.Provider>
|
|
</GamesStateContext.Provider>
|
|
)
|
|
};
|
|
|
|
function ViewSelector() {
|
|
const guide = useContext(GamesGuideContext);
|
|
|
|
return (
|
|
<nav className='ViewSelector'>
|
|
<div className='Container' >
|
|
<Link to='new'>New</Link>
|
|
<Link to='proposal'>Proposal<Counter number={guide.awaiting.proposal} /></Link>
|
|
<Link to='active' >Active<Counter number={guide.awaiting.active} /></Link>
|
|
<Link to='archive' >Archive</Link>
|
|
</div>
|
|
</nav>
|
|
)
|
|
}
|
|
|
|
function ViewProvider({ dispatchGuide, players }) {
|
|
|
|
return (
|
|
<div className='ViewProvider'>
|
|
<Routes>
|
|
|
|
<Route path='new' element={
|
|
<NewGame
|
|
players={players}
|
|
setPlayers={(opponentName, myColor) => dispatchGuide({ type: 'nextNewGame', opponentName, myColor })}
|
|
/>
|
|
} />
|
|
|
|
<Route path='proposal' element={
|
|
<GameProposalSelector onSelect={(uuid) => dispatchGuide({ type: 'selectedUUID', proposal: uuid })} />
|
|
} />
|
|
|
|
<Route path='active' element={
|
|
<ActiveGameSelector onSelect={(uuid) => dispatchGuide({ type: 'selectedUUID', active: uuid })} />
|
|
} />
|
|
|
|
<Route path='archive' element={
|
|
<GameArchiveSelector onSelect={(uuid) => dispatchGuide({ type: 'selectedUUID', archive: uuid })} />
|
|
} />
|
|
|
|
</Routes>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ActionPanel({ gamesApi }) {
|
|
const games = useContext(GamesStateContext);
|
|
const guide = useContext(GamesGuideContext);
|
|
|
|
const fromUUID = (uuid) => (!uuid) ? [{}, null] :
|
|
[
|
|
games.find((game) => game.uuid === uuid) || {}, // game
|
|
guide.UUIDpushing[uuid] // pushing
|
|
];
|
|
|
|
return (
|
|
<div className='ActionPanel'>
|
|
<Routes>
|
|
|
|
<Route path='new' element={
|
|
<Create
|
|
getGame={() => [guide.newGame, guide.newGame.isPushing]}
|
|
onClick={(req) => gamesApi.pushNewGame(req)}
|
|
/>
|
|
} />
|
|
|
|
<Route path='proposal' element={[
|
|
<Accept key={1}
|
|
getGame={() => fromUUID(guide.selectedUUID.proposal)}
|
|
onClick={(req) => gamesApi.pushGameProposalAccept(req)}
|
|
/>,
|
|
<Reject key={2}
|
|
getGame={() => fromUUID(guide.selectedUUID.proposal)}
|
|
onClick={(req) => gamesApi.pushGameProposalReject(req)}
|
|
/>,
|
|
<Cancel key={3}
|
|
getGame={() => fromUUID(guide.selectedUUID.proposal)}
|
|
onClick={(req) => gamesApi.pushGameProposalCancel(req)}
|
|
/>
|
|
]} />
|
|
|
|
<Route path='active' element={[
|
|
<DrawRequest key={1}
|
|
getGame={() => fromUUID(guide.selectedUUID.active)}
|
|
onClick={(req) => gamesApi.pushGameDrawRequest(req)}
|
|
/>,
|
|
<DrawAccept key={2}
|
|
getGame={() => fromUUID(guide.selectedUUID.active)}
|
|
onClick={(req) => gamesApi.pushGameDrawAccept(req)}
|
|
/>,
|
|
<DrawReject key={3}
|
|
getGame={() => fromUUID(guide.selectedUUID.active)}
|
|
onClick={(req) => gamesApi.pushGameDrawReject(req)}
|
|
/>,
|
|
<Surrender key={4}
|
|
getGame={() => fromUUID(guide.selectedUUID.active)}
|
|
onClick={(req) => gamesApi.pushGameSurrender(req)} />
|
|
]} />
|
|
|
|
<Route path='archive' element={[
|
|
<Backward key={1} />,
|
|
<Forward key={2} />
|
|
]} />
|
|
|
|
</Routes>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function GameBoardRoutes({ dispatchGuide, gamesApi, username }) {
|
|
const games = useContext(GamesStateContext);
|
|
const guide = useContext(GamesGuideContext);
|
|
|
|
const fromUUID = (uuid) => (!uuid) ? [{}, null, null] :
|
|
[
|
|
games.find((game) => game.uuid === uuid) || {}, // game
|
|
guide.UUIDpushing[uuid], // pushing
|
|
guide.UUIDerror[uuid] // error (aka bad move)
|
|
];
|
|
|
|
const onStoneClick = (uuid, cellId) => {
|
|
let board = { ...guide.newGame.board };
|
|
board[cellId] = nextStone(board[cellId]);
|
|
dispatchGuide({ type: 'nextNewGame', board });
|
|
}
|
|
|
|
return (
|
|
<Routes>
|
|
|
|
<Route path='new' element={
|
|
<GameBoard
|
|
username={username}
|
|
getGame={() => [guide.newGame, null]}
|
|
onStoneClick={onStoneClick}
|
|
/>
|
|
} />
|
|
|
|
<Route path='proposal' element={
|
|
<GameBoard
|
|
username={username}
|
|
getGame={() => fromUUID(guide.selectedUUID.proposal)}
|
|
/>
|
|
} />
|
|
|
|
<Route path='active' element={
|
|
<GameBoard
|
|
username={username}
|
|
getGame={() => fromUUID(guide.selectedUUID.active)}
|
|
onStoneMove={(uuid, move) => gamesApi.pushGameMove({ uuid, move, message: guide.UUIDmessage[uuid] })}
|
|
dispatchGuide={dispatchGuide}
|
|
/>
|
|
} />
|
|
|
|
<Route path='archive' element={
|
|
<GameBoard
|
|
username={username}
|
|
getGame={() => fromUUID(guide.selectedUUID.archive)}
|
|
/>
|
|
} />
|
|
|
|
</Routes>
|
|
)
|
|
}
|
|
|
|
function Message2OpponentRoutes({ dispatchGuide }) {
|
|
const guide = useContext(GamesGuideContext);
|
|
|
|
const getMessage = (uuid) => !uuid ? undefined : // <<-- appears as inactive message field
|
|
guide.UUIDmessage[uuid] || '';
|
|
|
|
return (
|
|
<Routes>
|
|
|
|
<Route path='new' element={
|
|
<Message2Opponent
|
|
getMessage={() => guide.newGame.message}
|
|
setMessage={(message) => dispatchGuide({ type: 'nextNewGame', message })} />
|
|
} />
|
|
|
|
<Route path='active' element={
|
|
<Message2Opponent
|
|
getMessage={() => getMessage(guide.selectedUUID.active)}
|
|
setMessage={(message) => dispatchGuide({ type: 'UUIDmessage', message, uuid: guide.selectedUUID.active })} />
|
|
} />
|
|
|
|
</Routes>
|
|
)
|
|
} |