GameProposal: Cancel
- doPushing * JSON content type detection * unexpeted responce status: show warning instead of throwing exeption - useGamesApi: if already pushing - do not push another one wobling for Cancel button - middleware: GameProposal controller: handle Cancel requests
This commit is contained in:
parent
2522da6349
commit
3063146a76
@ -1,11 +1,14 @@
|
||||
package djmil.cordacheckers.api;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@ -33,11 +36,11 @@ public class GameProposalController {
|
||||
HoldingIdentityResolver holdingIdentityResolver;
|
||||
|
||||
@PostMapping()
|
||||
public ResponseEntity<GameView> createGameProposal(
|
||||
public ResponseEntity<GameView> create(
|
||||
@AuthenticationPrincipal User sender,
|
||||
@RequestBody ReqGameProposalCreate gpRequest,
|
||||
UriComponentsBuilder ucb) throws JsonMappingException, JsonProcessingException {
|
||||
|
||||
UriComponentsBuilder ucb) throws JsonMappingException, JsonProcessingException
|
||||
{
|
||||
final HoldingIdentity gpSender = sender.getHoldingIdentity();
|
||||
final HoldingIdentity gpReceiver = holdingIdentityResolver.getByUsername(gpRequest.opponentName());
|
||||
final Stone.Color gpReceiverColor = gpRequest.opponentColor();
|
||||
@ -47,7 +50,7 @@ public class GameProposalController {
|
||||
gpSender,
|
||||
gpReceiver,
|
||||
gpReceiverColor,
|
||||
// gpRequest.board() // GireaIssue #3: use provided board configuration
|
||||
// gpRequest.board() // GiteaIssue #3: use provided board configuration
|
||||
gpRequest.message());
|
||||
|
||||
URI locationOfNewGameProposal = ucb
|
||||
@ -60,4 +63,17 @@ public class GameProposalController {
|
||||
.body(gameStateView);
|
||||
}
|
||||
|
||||
@PutMapping("/{uuid}/cancel")
|
||||
public ResponseEntity<GameView> cancel(
|
||||
@AuthenticationPrincipal User issuer,
|
||||
@PathVariable UUID uuid
|
||||
) {
|
||||
final GameView canceledGameView = cordaClient.gameProposalCancel(
|
||||
issuer.getHoldingIdentity(),
|
||||
uuid
|
||||
);
|
||||
|
||||
return ResponseEntity
|
||||
.ok(canceledGameView);
|
||||
}
|
||||
}
|
@ -134,7 +134,7 @@ public class CordaClient {
|
||||
|
||||
public GameView gameProposalCancel(HoldingIdentity holdingIdentity, UUID gameUuid) {
|
||||
final RequestBody requestBody = new RequestBody(
|
||||
"gp.reject-" +UUID.randomUUID(),
|
||||
"gp.cancel-" +UUID.randomUUID(),
|
||||
"djmil.cordacheckers.gameproposal.CancelFlow",
|
||||
gameUuid);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { usePolling, doPushing } from '../hook/api';
|
||||
import { gamesInitialState } from '../reducer/games';
|
||||
|
||||
export default function useGamesApi(gamesReducer, config) {
|
||||
const [games, dispatchGames] = gamesReducer;
|
||||
@ -16,19 +17,23 @@ export default function useGamesApi(gamesReducer, config) {
|
||||
return games;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
pollGamesList: usePollingGamesList,
|
||||
|
||||
pushNewGame: (reqParams) => doPushing('/api/gameproposal', 'POST', reqParams, {
|
||||
pushNewGame: ({ opponentName, opponentColor, board, message }) => ifNot(games.isPushingNewGame) &&
|
||||
doPushing('/api/gameproposal', 'POST', { opponentName, opponentColor, board, message }, {
|
||||
onPushing: (isPushingNewGame) => dispatchGames({ type: 'next', isPushingNewGame }),
|
||||
onSuccess: (game) => dispatchGames({ type: 'next', gamesList: [game, ...games.gamesList], newGame: emptyNewGame })
|
||||
onSuccess: (game) => dispatchGames({ type: 'next', gamesList: [game, ...games.gamesList], newGame: gamesInitialState.newGame })
|
||||
}),
|
||||
|
||||
pushGameProposalCancel: ({ uuid }) => ifNot(games.isPushingGameProposalCancel) &&
|
||||
doPushing(`/api/gameproposal/${uuid}/cancel`, 'PUT', null, {
|
||||
onPushing: (isPushingGameProposalCancel) => dispatchGames({ type: 'next', isPushingGameProposalCancel }),
|
||||
onSuccess: (canceledGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(canceledGame), proposal: gamesInitialState.proposal })
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
const emptyNewGame = {
|
||||
whitePlayer: '',
|
||||
blackPlayer: '',
|
||||
message2opponent: ''
|
||||
function ifNot(expression) {
|
||||
return !expression;
|
||||
}
|
@ -93,10 +93,14 @@ function ActionPanel({ players, gamesApi }) {
|
||||
<Routes>
|
||||
<Route path='new' element={
|
||||
<Create isCurrentUser={players.isCurrentUser}
|
||||
pushNewGame={(reqParams) => gamesApi.pushNewGame(reqParams)}
|
||||
onClick={(reqParams) => gamesApi.pushNewGame(reqParams)}
|
||||
/>
|
||||
} />
|
||||
<Route path='proposal' element={[<Accept key={1} />, <Reject key={2} />, <Cancel key={3} />]} />
|
||||
<Route path='proposal' element={[
|
||||
<Accept key={1} />,
|
||||
<Reject key={2} />,
|
||||
<Cancel key={3} onClick={({uuid}) => gamesApi.pushGameProposalCancel({ uuid })} />
|
||||
]} />
|
||||
<Route path='active' element={[<DrawReq key={1} />, <DrawAcq key={2} />, <Surrender key={3} />]} />
|
||||
<Route path='archive' element={[<Backward key={1} />, <Forward key={2} />]} />
|
||||
</Routes>
|
||||
|
@ -1,15 +1,16 @@
|
||||
import React, { useContext } from 'react';
|
||||
import Wobler from '../../../components/Wobler';
|
||||
import { GamesContext } from '../../../context/games';
|
||||
|
||||
export default function Cancel() {
|
||||
export default function Cancel({ onClick }) {
|
||||
const games = useContext(GamesContext);
|
||||
|
||||
const selectedGame = games.findGame({ uuid: games.proposal.selectedUUID });
|
||||
|
||||
if (selectedGame?.status === 'GAME_PROPOSAL_WAIT_FOR_OPPONENT')
|
||||
return (
|
||||
<button className='Cancel'>
|
||||
Cancel
|
||||
<button className='Cancel' onClick={() => onClick({ uuid: games.proposal.selectedUUID })} >
|
||||
<Wobler text="Cancel" dance={games.isPushingGameProposalCancel} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import Wobler from '../../../components/Wobler';
|
||||
import { Color } from '../../../components/Checkers';
|
||||
|
||||
|
||||
export default function Create({ isCurrentUser, pushNewGame }) {
|
||||
export default function Create({ isCurrentUser, onClick }) {
|
||||
const games = useContext(GamesContext);
|
||||
|
||||
const hasPlayers = checkPlayers(games.newGame);
|
||||
@ -17,9 +17,6 @@ export default function Create({ isCurrentUser, pushNewGame }) {
|
||||
if (!hasCurrentUser)
|
||||
return alert("You must be one of the selected players");
|
||||
|
||||
if (games.isPushingNewGame)
|
||||
return; // current request is still being processed
|
||||
|
||||
/*
|
||||
* Prepare & send NewGame request
|
||||
*/
|
||||
@ -32,7 +29,7 @@ export default function Create({ isCurrentUser, pushNewGame }) {
|
||||
message: games.newGame.message2opponent
|
||||
}
|
||||
|
||||
pushNewGame(reqParams);
|
||||
onClick(reqParams);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -72,11 +72,18 @@ export async function doPushing(uri, method, data, { onSuccess, onPushing }) {
|
||||
body: JSON.stringify(data), // body data type must match "Content-Type" header
|
||||
});
|
||||
|
||||
if (!response.ok)
|
||||
throw new Error(`Error! status: ${response.status}`);
|
||||
if (!response.ok) {
|
||||
return console.warn(`Unexpected response status: ${response.status}`, response);
|
||||
}
|
||||
|
||||
if (onSuccess)
|
||||
onSuccess(await response.json());
|
||||
if (onSuccess) {
|
||||
var content = (response.headers.get('Content-Type') === "application/json")
|
||||
? await response.json()
|
||||
: null;
|
||||
|
||||
console.log("rsponce", response, "content", content);
|
||||
onSuccess(content);
|
||||
}
|
||||
// } catch (err) {
|
||||
} finally {
|
||||
if (onPushing)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useReducer } from 'react';
|
||||
import { nextState } from '../util/StateHelper';
|
||||
|
||||
const initialState = {
|
||||
export const gamesInitialState = {
|
||||
gamesList: null,
|
||||
|
||||
newGame: {
|
||||
@ -18,8 +18,10 @@ const initialState = {
|
||||
// Network
|
||||
isPollingGamesList: false,
|
||||
isPushingNewGame: false,
|
||||
isPushingGameProposalCancel: false,
|
||||
|
||||
findGame,
|
||||
nextGameList,
|
||||
};
|
||||
|
||||
function reducer(state, action) {
|
||||
@ -46,9 +48,15 @@ function reducer(state, action) {
|
||||
}
|
||||
|
||||
export default function useGamesReducer() {
|
||||
return useReducer(reducer, initialState);
|
||||
return useReducer(reducer, gamesInitialState);
|
||||
}
|
||||
|
||||
function findGame({ uuid }) {
|
||||
return this.gamesList?.find((game) => game.uuid === uuid);
|
||||
}
|
||||
|
||||
function nextGameList(nextGame) {
|
||||
return this.gamesList?.map(
|
||||
(game) => (nextGame?.uuid === game.uuid) ? nextGame : game
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user