diff --git a/src/components/Game/AutoActionManager.tsx b/src/components/Game/AutoActionManager.tsx index bd717da..e4889b6 100644 --- a/src/components/Game/AutoActionManager.tsx +++ b/src/components/Game/AutoActionManager.tsx @@ -30,13 +30,11 @@ const AutoActionManager = () => { const isMyGo = useAppSelector(getIsMyGo) const isInBunker = useAppSelector(getIsInBunker) - const playCard = (id: string, card: string, suppressError = false) => + const playCard = (id: string, card: string) => dispatch(GameService.playCard(id, card)).catch(e => { - if (!suppressError) - enqueueSnackbar(parseError(e), { - variant: "error", - }) - else console.error(e) + enqueueSnackbar(parseError(e), { + variant: "error", + }) }) const call = (id: string, callAmount: number) => @@ -59,17 +57,17 @@ const AutoActionManager = () => { round?.suit && round.status === RoundStatus.PLAYING ) { - if (autoPlayCard) playCard(gameId, autoPlayCard, true) + if (autoPlayCard) playCard(gameId, autoPlayCard) else if (bestCardLead(round)) { const cardToPlay = getWorstCard(cards, round.suit) - if (cardToPlay) playCard(gameId, cardToPlay.name, true) + if (cardToPlay) playCard(gameId, cardToPlay.name) } } }, [gameId, round, isMyGo, cards, autoPlayCard]) // Buy cards in if you are the goer useEffect(() => { - if (gameId && canBuyCards) buyCards(gameId, cards) + if (gameId && canBuyCards && cards.length <= 5) buyCards(gameId, cards) }, [gameId, cards, canBuyCards]) return null diff --git a/src/components/Game/WebsocketManager.tsx b/src/components/Game/WebsocketManager.tsx index 23eb59a..bd553ef 100644 --- a/src/components/Game/WebsocketManager.tsx +++ b/src/components/Game/WebsocketManager.tsx @@ -6,6 +6,7 @@ import { disableActions, getGameId, getIsMyGo, + getIamSpectator, updateGame, updatePlayedCards, } from "../../caches/GameSlice" @@ -60,6 +61,7 @@ const WebsocketHandler = () => { const [autoActionEnabled, setAutoActionEnabled] = useState(false) const isMyGo = useAppSelector(getIsMyGo) + const iamSpectator = useAppSelector(getIamSpectator) const playerProfiles = useAppSelector(getPlayerProfiles) const { enqueueSnackbar } = useSnackbar() @@ -146,6 +148,31 @@ const WebsocketHandler = () => { dispatch(updateMyCards(game.cards)) } + // On game completion we need to display the last round to the user + const processGameCompleted = async ( + game: GameState, + previousRound: Round, + ) => { + // Disable actions by setting isMyGo to false + dispatch(disableActions()) + + // Show the last card of the penultimate round being played + playCardSound() + const penultimateHand = previousRound.completedHands.pop() + if (!penultimateHand) throw Error("Failed to get the penultimate round") + dispatch(updatePlayedCards(penultimateHand.playedCards)) + await new Promise(r => setTimeout(r, 4000)) + + // Next show the final round being played + playCardSound() + dispatch(updatePlayedCards(previousRound.currentHand.playedCards)) + dispatch(updateMyCards([])) + await new Promise(r => setTimeout(r, 6000)) + + // Finally update the game with the latest state + dispatch(updateGame(game)) + } + const processAction = useCallback( async (action: ActionEvent) => { console.log(action.type) @@ -165,32 +192,39 @@ const WebsocketHandler = () => { case "BUY_CARDS": const buyCardsEvt = action.transitionData as BuyCardsEvent sendCardsBoughtNotification(buyCardsEvt) - reloadCards(action.gameState.cards, isMyGo) + if (!iamSpectator) + reloadCards(action.gameState.cards, isMyGo) dispatch(updateGame(action.gameState)) break case "CHOOSE_FROM_DUMMY": case "CARD_PLAYED": playCardSound() - reloadCards(action.gameState.cards, isMyGo) + if (!iamSpectator) + reloadCards(action.gameState.cards, isMyGo) dispatch(updateGame(action.gameState)) break case "CALL": callSound() - reloadCards(action.gameState.cards, true) + if (!iamSpectator) reloadCards(action.gameState.cards, true) dispatch(updateGame(action.gameState)) break case "PASS": passSound() - reloadCards(action.gameState.cards, true) + if (!iamSpectator) reloadCards(action.gameState.cards, true) dispatch(updateGame(action.gameState)) break case "REPLAY": - case "GAME_OVER": dispatch(updateGame(action.gameState)) + break + case "GAME_OVER": + await processGameCompleted( + action.gameState, + action.transitionData as Round, + ) } }, - [playerProfiles, isMyGo], + [playerProfiles, iamSpectator, isMyGo], ) useSubscription(["/game", "/user/game"], message => diff --git a/src/pages/Game/Game.tsx b/src/pages/Game/Game.tsx index 8538d7c..13795a0 100644 --- a/src/pages/Game/Game.tsx +++ b/src/pages/Game/Game.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react" +import React, { useCallback, useEffect } from "react" import GameWrapper from "../../components/Game/GameWrapper" import GameOver from "../../components/Game/GameOver" import GameService from "../../services/GameService" @@ -10,7 +10,11 @@ import { useParams } from "react-router-dom" import { useAppDispatch, useAppSelector } from "../../caches/hooks" import { useSnackbar } from "notistack" -import { getIsGameActive, resetGame } from "../../caches/GameSlice" +import { + getIamSpectator, + getIsGameActive, + resetGame, +} from "../../caches/GameSlice" import { clearAutoPlay } from "../../caches/AutoPlaySlice" import { clearMyCards } from "../../caches/MyCardsSlice" import RefreshingData from "../../components/icons/RefreshingData" @@ -20,18 +24,21 @@ const Game = () => { const dispatch = useAppDispatch() let { id } = useParams() const { enqueueSnackbar } = useSnackbar() + const iamSpectator = useAppSelector(getIamSpectator) const isGameActive = useAppSelector(getIsGameActive) - const fetchData = async () => { + const fetchData = useCallback(async () => { if (id) - await dispatch(GameService.refreshGameState(id)).catch((e: Error) => + await dispatch( + GameService.refreshGameState(id, iamSpectator), + ).catch((e: Error) => enqueueSnackbar(parseError(e), { variant: "error" }), ) await dispatch(GameService.getAllPlayers()).catch((e: Error) => enqueueSnackbar(parseError(e), { variant: "error" }), ) - } + }, [iamSpectator]) useEffect(() => { fetchData() @@ -42,7 +49,7 @@ const Game = () => { dispatch(clearMyCards()) dispatch(clearAutoPlay()) } - }, [id]) + }, [id, iamSpectator]) return ( > => + (gameId: string, iamSpectator: boolean): AppThunk> => async (dispatch, getState) => { const accessToken = getAccessToken(getState()) const response = await axios.get( @@ -37,8 +37,10 @@ const refreshGameState = getDefaultConfig(accessToken), ) dispatch(updateGame(response.data)) - dispatch(updateMyCards(response.data.cards)) - dispatch(clearAutoPlay()) + if (!iamSpectator) { + dispatch(updateMyCards(response.data.cards)) + dispatch(clearAutoPlay()) + } } const getAll = (): AppThunk> => async (dispatch, getState) => { @@ -178,14 +180,14 @@ const chooseFromDummy = const playCard = (gameId: string, card: string): AppThunk> => async (dispatch, getState) => { - dispatch(removeCard(card)) - dispatch(clearAutoPlay()) const accessToken = getAccessToken(getState()) await axios.put( `${process.env.REACT_APP_API_URL}/api/v1/playCard?gameId=${gameId}&card=${card}`, null, getDefaultConfig(accessToken), ) + dispatch(removeCard(card)) + dispatch(clearAutoPlay()) } export default {