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