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, {
 | 
			
		||||
            onPushing: (isPushingNewGame) => dispatchGames({ type: 'next', isPushingNewGame }),
 | 
			
		||||
            onSuccess: (game) => dispatchGames({ type: 'next', gamesList: [game, ...games.gamesList], newGame: emptyNewGame })
 | 
			
		||||
        }),
 | 
			
		||||
 | 
			
		||||
        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: 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}) {
 | 
			
		||||
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