Game Draw: req/acq/rej

This commit is contained in:
djmil 2023-11-16 11:49:12 +01:00
parent 576556afe7
commit 671e13a41d
11 changed files with 186 additions and 30 deletions

View File

@ -50,4 +50,46 @@ public class GameController {
return ResponseEntity return ResponseEntity
.ok(finishedGame); .ok(finishedGame);
} }
@PutMapping("/{uuid}/drawreq")
public ResponseEntity<GameView> drawRequest(
@AuthenticationPrincipal User issuer,
@PathVariable UUID uuid
) {
final GameView drawReqGame = cordaClient.gameDrawRequest(
issuer.getHoldingIdentity(),
uuid
);
return ResponseEntity
.ok(drawReqGame);
}
@PutMapping("/{uuid}/drawacc")
public ResponseEntity<GameView> drawAccept(
@AuthenticationPrincipal User issuer,
@PathVariable UUID uuid
) {
final GameView drawAccGame = cordaClient.gameDrawAccept(
issuer.getHoldingIdentity(),
uuid
);
return ResponseEntity
.ok(drawAccGame);
}
@PutMapping("/{uuid}/drawrej")
public ResponseEntity<GameView> drawReject(
@AuthenticationPrincipal User issuer,
@PathVariable UUID uuid
) {
final GameView drawRejGame = cordaClient.gameDrawReject(
issuer.getHoldingIdentity(),
uuid
);
return ResponseEntity
.ok(drawRejGame);
}
} }

View File

@ -44,11 +44,29 @@ export default function useGamesApi(gamesReducer, config) {
onSuccess: (acceptedGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(acceptedGame), proposal: gamesInitialState.proposal }) onSuccess: (acceptedGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(acceptedGame), proposal: gamesInitialState.proposal })
}), }),
pushGameSurrender: ({ uuid }) => ifNot(games.isPushingActiveGameSurrender) && pushGameSurrender: ({ uuid }) => ifNot(games.isPushingGameSurrender) &&
doPushing(`/api/game/${uuid}/surrender`, 'PUT', null, { doPushing(`/api/game/${uuid}/surrender`, 'PUT', null, {
onPushing: (isPushingActiveGameSurrender) => dispatchGames({ type: 'next', isPushingActiveGameSurrender }), onPushing: (isPushingGameSurrender) => dispatchGames({ type: 'next', isPushingGameSurrender }),
onSuccess: (finishedGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(finishedGame), proposal: gamesInitialState.active }) onSuccess: (finishedGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(finishedGame), proposal: gamesInitialState.active })
}), }),
pushGameDrawRequest: ({ uuid }) => ifNot(games.isPushingGameDrawRequest) &&
doPushing(`/api/game/${uuid}/drawreq`, 'PUT', null, {
onPushing: (isPushingGameDrawRequest) => dispatchGames({ type: 'next', isPushingGameDrawRequest }),
onSuccess: (drawReqGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(drawReqGame), proposal: gamesInitialState.active })
}),
pushGameDrawAccept: ({ uuid }) => ifNot(games.isPushingGameDrawAccept) &&
doPushing(`/api/game/${uuid}/drawacc`, 'PUT', null, {
onPushing: (isPushingGameDrawAccept) => dispatchGames({ type: 'next', isPushingGameDrawAccept }),
onSuccess: (drawAccGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(drawAccGame), proposal: gamesInitialState.active })
}),
pushGameDrawReject: ({ uuid }) => ifNot(games.isPushingGameDrawReject) &&
doPushing(`/api/game/${uuid}/drawrej`, 'PUT', null, {
onPushing: (isPushingGameDrawReject) => dispatchGames({ type: 'next', isPushingGameDrawReject }),
onSuccess: (drawRejGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(drawRejGame), proposal: gamesInitialState.active })
}),
} }
} }

View File

@ -145,4 +145,37 @@
.ActionPanel .Accept.ready:active { .ActionPanel .Accept.ready:active {
background-color:#00af00a0; background-color:#00af00a0;
}
/*
* Draw
*/
.ActionPanel .Draw.ready,
.ActionPanel .Draw.accept,
.ActionPanel .Draw.reject {
background-color: #ffff0018;
}
.ActionPanel .Draw.ready:hover {
background-color:#ffff0040;
}
.ActionPanel .Draw.ready:active {
background-color:#ffff00a0;
}
.ActionPanel .Draw.accept:hover {
background-color:#a4ff4a50;
}
.ActionPanel .Draw.accept:active {
background-color:#10ff0080;
}
.ActionPanel .Draw.reject:hover {
background-color:#ffaaaa60;
}
.ActionPanel .Draw.reject:active {
background-color:#ff000090;
} }

View File

@ -9,9 +9,8 @@ import Create from './games/action/Create';
import Reject from './games/action/Reject'; import Reject from './games/action/Reject';
import Cancel from './games/action/Cancel'; import Cancel from './games/action/Cancel';
import Accept from './games/action/Accept'; import Accept from './games/action/Accept';
import DrawReq from './games/action/DrawReq';
import DrawAcq from './games/action/DrawAcq';
import Surrender from './games/action/Surrender'; import Surrender from './games/action/Surrender';
import { DrawRequest, DrawAccept, DrawReject } from './games/action/Draw';
import Backward from './games/action/Backward'; import Backward from './games/action/Backward';
import Forward from './games/action/Forward'; import Forward from './games/action/Forward';
@ -27,18 +26,19 @@ export default function Games({ context: { gamesReducer, gamesApi }, players })
return ( return (
<GamesContext.Provider value={games} > <GamesContext.Provider value={games} >
<div className='Games'> <div className='Games'>
<div className='left-side'> <div className='left-side'>
<ViewSelector games={games} /> <ViewSelector games={games} />
<ViewProvider gamesReducer={gamesReducer} players={players} /> <ViewProvider gamesReducer={gamesReducer} players={players} />
</div> </div>
<div className='right-side'> <div className='right-side'>
<ActionPanel gamesApi={gamesApi} players={players} /> <ActionPanel gamesApi={gamesApi} players={players} />
{/* <GameMessage /> */}
<GameBoard /> <GameBoard />
<Message2Opponent dispatchGames={dispatchGames} /> <Message2Opponent dispatchGames={dispatchGames} />
{/*
<GameMessage />
*/}
</div> </div>
</div > </div >
</GamesContext.Provider> </GamesContext.Provider>
) )
@ -81,6 +81,7 @@ function ViewProvider({ gamesReducer, players }) {
<Route path='archive' element={ <Route path='archive' element={
<GameArchiveSelector onSelect={(selectedUUID) => dispatchGames({ type: 'nextArchive', selectedUUID })} /> <GameArchiveSelector onSelect={(selectedUUID) => dispatchGames({ type: 'nextArchive', selectedUUID })} />
} /> } />
</Routes> </Routes>
</div> </div>
) )
@ -90,22 +91,31 @@ function ActionPanel({ players, gamesApi }) {
return ( return (
<div className='ActionPanel'> <div className='ActionPanel'>
<Routes> <Routes>
<Route path='new' element={ <Route path='new' element={
<Create isCurrentUser={players.isCurrentUser} <Create isCurrentUser={players.isCurrentUser}
onClick={(reqParams) => gamesApi.pushNewGame(reqParams)} onClick={(reqParams) => gamesApi.pushNewGame(reqParams)}
/> />
} /> } />
<Route path='proposal' element={[ <Route path='proposal' element={[
<Accept key={1} onClick={(uuid) => gamesApi.pushGameProposalAccept({ uuid })} />, <Accept key={1} onClick={(uuid) => gamesApi.pushGameProposalAccept({ uuid })} />,
<Reject key={2} onClick={(uuid) => gamesApi.pushGameProposalReject({ uuid })} />, <Reject key={2} onClick={(uuid) => gamesApi.pushGameProposalReject({ uuid })} />,
<Cancel key={3} onClick={(uuid) => gamesApi.pushGameProposalCancel({ uuid })} /> <Cancel key={3} onClick={(uuid) => gamesApi.pushGameProposalCancel({ uuid })} />
]} /> ]} />
<Route path='active' element={[ <Route path='active' element={[
<DrawReq key={1} />, <DrawRequest key={1} onClick={(uuid) => gamesApi.pushGameDrawRequest({ uuid })} />,
<DrawAcq key={2} />, <DrawAccept key={2} onClick={(uuid) => gamesApi.pushGameDrawAccept({ uuid })} />,
<Surrender key={3} onClick={(uuid) => gamesApi.pushGameSurrender({ uuid })}/> <DrawReject key={3} onClick={(uuid) => gamesApi.pushGameDrawReject({ uuid })} />,
<Surrender key={4} onClick={(uuid) => gamesApi.pushGameSurrender({ uuid })} />
]} /> ]} />
<Route path='archive' element={[<Backward key={1} />, <Forward key={2} />]} />
<Route path='archive' element={[
<Backward key={1} />,
<Forward key={2} />
]} />
</Routes> </Routes>
</div> </div>
) )
@ -127,7 +137,7 @@ function countGames(gamesList) {
awaiting.proposals++; awaiting.proposals++;
break; break;
case 'GAME_BOARD_WAIT_FOR_YOU': case 'GAME_BOARD_WAIT_FOR_YOU':
case 'DRAW_REQUEST_WAIT_FOR_YO': case 'DRAW_REQUEST_WAIT_FOR_YOU':
awaiting.active++; awaiting.active++;
break; break;
default: default:

View File

@ -56,7 +56,7 @@ export default function Message2Opponent({ dispatchGames }) {
return ( return (
<input className='Message2Opponent' <input className='Message2Opponent'
placeholder='Message to opponent' placeholder='Message'
value={value} value={value}
maxLength={150} maxLength={150}
onChange={e => update(e.target.value)} onChange={e => update(e.target.value)}

View File

@ -0,0 +1,60 @@
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 DrawAcq() {
return <button className='DrawAcq'>Draw accept</button>
}

View File

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

View File

@ -14,9 +14,9 @@ export default function Surrender({ onClick }) {
return ( return (
<button className={'Surrender' + (isReady && ' ready')} <button className={'Surrender' + (isReady && ' ready')}
onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select some game')} onClick={() => isReady ? onClick(selectedGame.uuid) : alert('You have to select a game')}
> >
<Wobler text="Surrender" dance={games.isPushingActiveGameSurrender} /> <Wobler text="Surrender" dance={games.isPushingGameSurrender} />
</button> </button>
) )
} }

View File

@ -49,10 +49,10 @@ export function ActiveGameSelector({ onSelect }) {
onSelect(uuid); onSelect(uuid);
} }
const yoursList = games.gamesList.filter(game => game.status === 'GAME_BOARD_WAIT_FOR_YOU') const yoursList = games.gamesList.filter(game => (game.status === 'GAME_BOARD_WAIT_FOR_YOU' || game.status === 'DRAW_REQUEST_WAIT_FOR_YOU'))
.map(game => <Selectable game={game} key={game.uuid} selected={isSelected(game.uuid)} onClick={onClick} />) .map(game => <Selectable game={game} key={game.uuid} selected={isSelected(game.uuid)} onClick={onClick} />)
const opponentsList = games.gamesList.filter(game => game.status === 'GAME_BOARD_WAIT_FOR_OPPONENT') const opponentsList = games.gamesList.filter(game => (game.status === 'GAME_BOARD_WAIT_FOR_OPPONENT' || game.status === 'DRAW_REQUEST_WAIT_FOR_OPPONENT'))
.map(game => <Selectable game={game} key={game.uuid} selected={isSelected(game.uuid)} onClick={onClick} />) .map(game => <Selectable game={game} key={game.uuid} selected={isSelected(game.uuid)} onClick={onClick} />)
return ( return (

View File

@ -27,10 +27,15 @@ export const gamesInitialState = {
// Network // Network
isPollingGamesList: false, isPollingGamesList: false,
isPushingNewGame: false, isPushingNewGame: false,
isPushingGameProposalCancel: false, isPushingGameProposalCancel: false,
isPushingGameProposalReject: false, isPushingGameProposalReject: false,
isPushingGameProposalAccept: false, isPushingGameProposalAccept: false,
isPushingActiveGameSurrender: false,
isPushingGameSurrender: false,
isPushingGameDrawRequest: false,
isPushingGameDrawAccept: false,
isPushingGameDrawReject: false,
findGame, findGame,
nextGameList, nextGameList,