Selaa lähdekoodia

Making the whole application use gameId and playerName stores

Kirk Trombley 5 vuotta sitten
vanhempi
commit
6eea1ba9c8

+ 11 - 14
client/src/components/Game/Game.jsx

@@ -13,11 +13,10 @@ import PreRound from '../screens/PreRound';
 import GamePanel from "../screens/GamePanel";
 import RoundSummary from '../screens/RoundSummary';
 import PlayerScores from "../screens/PlayerScores";
+import { gameIdStore } from "../../domain/store";
 
 const initialState = {
   gameState: PRE_GAME,
-  gameId: null,
-  playerName: null,
   lastRound: null,
   joined: false,
 }
@@ -32,18 +31,21 @@ const extractAndRemoveSearchParam = param => {
 
 const Game = () => {
   const [ state, rawSetState ] = useState(initialState);
+
   const setGameState = gameState => rawSetState({ ...state, gameState });
   const setGameStateAnd = (gameState, updates) => rawSetState({ ...state, gameState, ...updates });
-  const onGameJoined = ({ gameId, playerName }) => setGameStateAnd(PRE_ROUND, { gameId, playerName, joined: true });
+  const onGameJoined = () => setGameStateAnd(PRE_ROUND, { joined: true });
   
   const joinCode = extractAndRemoveSearchParam("join");
   if (joinCode) {
-    setGameStateAnd(PRE_ROUND, { gameId: joinCode, joined: false });
+    gameIdStore.set(joinCode);
+    setGameState(PRE_ROUND);
   }
 
   const summaryCode = extractAndRemoveSearchParam("summary");
   if (summaryCode) {
-    setGameStateAnd(POST_GAME, { gameId: summaryCode });
+    gameIdStore.set(summaryCode);
+    setGameState(POST_GAME);
   }
 
   switch (state.gameState) {
@@ -51,7 +53,6 @@ const Game = () => {
       return (
         <HeaderAndFooter>
           <PreGame
-            initPlayerName={state.playerName}
             onGameJoined={onGameJoined}
           />
         </HeaderAndFooter>
@@ -60,8 +61,6 @@ const Game = () => {
       return (
         <HeaderAndFooter>
           <PreRound
-            gameId={state.gameId}
-            playerName={state.playerName}
             joined={state.joined}
             onGameJoined={onGameJoined}
             onStart={() => setGameState(IN_ROUND)}
@@ -70,15 +69,11 @@ const Game = () => {
       );
     case IN_ROUND:
       return <GamePanel
-        gameId={state.gameId}
-        playerName={state.playerName}
         onRoundEnd={lastRound => setGameStateAnd(POST_ROUND, { lastRound })}
         onGameEnd={() => setGameState(POST_GAME)}
       />
     case POST_ROUND:
       return <RoundSummary
-        gameId={state.gameId}
-        playerName={state.playerName}
         round={state.lastRound}
         onNext={() => setGameState(IN_ROUND)}
       />
@@ -86,8 +81,10 @@ const Game = () => {
       return (
         <HeaderAndFooter>
           <PlayerScores
-            gameId={state.gameId}
-            onReturnToStart={() => setGameStateAnd(PRE_GAME, { gameId: null, joined: false })}
+            onReturnToStart={() => {
+              gameIdStore.set(null);
+              setGameStateAnd(PRE_GAME, { joined: false });
+            }}
           />
         </HeaderAndFooter>
       );

+ 3 - 3
client/src/components/screens/GamePanel/GamePanel.jsx

@@ -29,10 +29,10 @@ const StreetViewWrapper = styled.div`
   flex: 3;
 `
 
-const GamePanelContainer = ({ gameId, playerName, onRoundEnd, onGameEnd }) => {
+const GamePanelContainer = ({ onRoundEnd, onGameEnd }) => {
   const [submitDisabled, setSubmitDisabled] = useState(false);
   const [selectedPoint, setSelectedPoint] = useState(null);
-  const [finished, roundInfo] = useRoundInfo(gameId, playerName);
+  const [finished, roundInfo] = useRoundInfo();
 
   if (finished) {
     onGameEnd();
@@ -46,7 +46,7 @@ const GamePanelContainer = ({ gameId, playerName, onRoundEnd, onGameEnd }) => {
 
   const handleSubmitGuess = async () => {
     setSubmitDisabled(true);
-    const { score, totalScore } = await sendGuess(gameId, playerName, currentRound, selectedPoint || { timeout: true });
+    const { score, totalScore } = await sendGuess(currentRound, selectedPoint || { timeout: true });
     onRoundEnd({
       roundNum: currentRound,
       selectedPoint,

+ 5 - 2
client/src/components/screens/PlayerScores/PlayerScores.jsx

@@ -5,6 +5,8 @@ import Button from "../../util/Button";
 import PlayerScoreTile from "./PlayerScoreTile";
 import usePlayerScores from '../../../hooks/usePlayerScores';
 import ClickToCopy from '../../util/ClickToCopy';
+import useObservable from '../../../hooks/useObservable';
+import { gameIdStore } from '../../../domain/store';
 
 const Container = styled.div`
   display: flex;
@@ -47,8 +49,9 @@ const getUrl = gameId => {
   return u.href;
 }
 
-export default ({ gameId, onReturnToStart }) => {
-  const scores = usePlayerScores(gameId);
+export default ({ onReturnToStart }) => {
+  const gameId = useObservable(gameIdStore);
+  const scores = usePlayerScores();
 
   if (!scores) {
     return <Loading/>

+ 9 - 6
client/src/components/screens/PreGame/PreGame.jsx

@@ -3,7 +3,9 @@ import styled from "styled-components";
 import Loading from "../../util/Loading";
 import PlayerNameInput from "./../../util/PlayerNameInput";
 import { createGame } from "../../../domain/GGSHService";
-import NewGameInput from "./NewGameInput"
+import NewGameInput from "./NewGameInput";
+import useObservable from "../../../hooks/useObservable";
+import { playerNameStore, gameIdStore } from "../../../domain/store";
 
 const Container = styled.div`
   display: flex;
@@ -29,9 +31,9 @@ const Divider = styled.hr`
   }
 `
 
-export default ({ initPlayerName, onGameJoined }) => {
+export default ({ onGameJoined }) => {
   const [loading, setLoading] = useState(false);
-  const [playerName, setPlayerName] = useState(initPlayerName);
+  const playerName = useObservable(playerNameStore);
   const [timer, setTimer] = useState(300);
 
   if (loading) {
@@ -40,14 +42,15 @@ export default ({ initPlayerName, onGameJoined }) => {
 
   const onCreateGame = async () => {
     setLoading(true);
-    const gameId = await createGame(playerName, timer);
-    onGameJoined({ gameId, playerName });
+    const gameId = await createGame(timer);
+    gameIdStore.set(gameId);
+    onGameJoined();
   };
   const cannotCreateGame = !playerName || playerName.length === 0;
 
   return (
     <Container>
-      <PlayerNameInput playerName={playerName} onChangePlayerName={setPlayerName} />
+      <PlayerNameInput/>
       <Divider/>
       <NewGameInput {...{ onCreateGame, cannotCreateGame, timer, setTimer }} />
     </Container>

+ 2 - 2
client/src/components/screens/PreRound/LobbyPlayerList.jsx

@@ -25,12 +25,12 @@ const PlayerList = styled.ul`
 const PlayerName = styled.li`
 `
 
-const LobbyPlayerList = ({ gameId }) => (
+const LobbyPlayerList = () => (
   <PlayerListContainer>
     <Label>Players</Label>
     <PlayerList>
       {
-        usePlayerScores(gameId)?.map(({ name }) => (
+        usePlayerScores()?.map(({ name }) => (
           <PlayerName key={name}>{name}</PlayerName>
         ))
       }

+ 31 - 22
client/src/components/screens/PreRound/PreRound.jsx

@@ -7,6 +7,8 @@ import PlayerNameInput from "../../util/PlayerNameInput";
 import LobbyPlayerList from "./LobbyPlayerList";
 import { joinGame } from "../../../domain/GGSHService";
 import DelayedButton from "../../util/DelayedButton";
+import useObservable from "../../../hooks/useObservable";
+import { playerNameStore, gameIdStore } from "../../../domain/store";
 
 const InfoContainer = styled.div`
   flex: 3;
@@ -48,24 +50,30 @@ const getUrl = gameId => {
   return u.href;
 }
 
-const LobbyInfo = ({ gameId, playerName, onStart }) => (
-  <InfoContainer>
-    <Label>Playing as {playerName}</Label>
-    <Label>Use this link to invite other players:</Label>
-    <Label><ClickToCopy text={getUrl(gameId)} /></Label>
-    <StyledDelayButton 
-      onEnd={onStart} 
-      countDownFormatter={rem => `Click to cancel, ${rem}s...`}
-    >
-      Start Game
-    </StyledDelayButton>
-  </InfoContainer>
-);
+const LobbyInfo = ({ onStart }) => {
+  const gameId = useObservable(gameIdStore);
+  const playerName = useObservable(playerNameStore);
+
+  return (
+    <InfoContainer>
+      <Label>Playing as {playerName}</Label>
+      <Label>Use this link to invite other players:</Label>
+      <Label><ClickToCopy text={getUrl(gameId)} /></Label>
+      <StyledDelayButton 
+        onEnd={onStart} 
+        countDownFormatter={rem => `Click to cancel, ${rem}s...`}
+      >
+        Start Game
+      </StyledDelayButton>
+    </InfoContainer>
+  )
+};
 
-const JoinForm = ({ gameId, onGameJoined }) => {
+const JoinForm = ({ onGameJoined }) => {
+  const gameId = useObservable(gameIdStore);
+  const playerName = useObservable(playerNameStore);
   const [loading, setLoading] = useState(false);
   const [failed, setFailed] = useState(false);
-  const [playerName, setPlayerName] = useState(null);
 
   if (loading) {
     return <InfoContainer><Loading/></InfoContainer>
@@ -75,7 +83,7 @@ const JoinForm = ({ gameId, onGameJoined }) => {
     // TODO would like to support re-joining a game you left
     setLoading(true);
     try {
-      await joinGame(gameId, playerName);
+      await joinGame();
     } catch (err) {
       // failed to join the game
       setFailed(true);
@@ -83,7 +91,8 @@ const JoinForm = ({ gameId, onGameJoined }) => {
       return;
     }
     setFailed(false);
-    onGameJoined({ gameId, playerName });
+    playerNameStore.set(playerName);
+    onGameJoined();
   };
   const cannotJoinGame = !playerName || playerName.length === 0;
 
@@ -91,20 +100,20 @@ const JoinForm = ({ gameId, onGameJoined }) => {
     <InfoContainer>
       {failed && <Label>Failed to join the game! Maybe try a different name?</Label>}
       <Label>Joining {gameId}...</Label>
-      <PlayerNameInput playerName={playerName} onChangePlayerName={setPlayerName}/>
+      <PlayerNameInput/>
       <FormButton onClick={onJoinGame} disabled={cannotJoinGame}>Join Game</FormButton>
     </InfoContainer>
   );
 }
 
-const PreRound = ({ gameId, playerName, joined, onGameJoined, onStart }) => (
+const PreRound = ({ joined, onGameJoined, onStart }) => (
   <PageContainer>
     {
       joined
-        ? <LobbyInfo gameId={gameId} playerName={playerName} onStart={onStart}/>
-        : <JoinForm gameId={gameId} onGameJoined={onGameJoined}/>
+        ? <LobbyInfo onStart={onStart}/>
+        : <JoinForm onGameJoined={onGameJoined}/>
     }
-    <LobbyPlayerList gameId={gameId} />
+    <LobbyPlayerList/>
   </PageContainer>
 );
 

+ 4 - 6
client/src/components/screens/RoundSummary/RoundInfoPane.jsx

@@ -6,8 +6,8 @@ import DelayedButton from "../../util/DelayedButton";
 import useRoundInfo from "../../../hooks/useRoundInfo";
 import Button from "../../util/Button";
 
-const useTopScorer = (gameId, roundNum) => {
-  const scores = usePlayerScores(gameId);
+const useTopScorer = (roundNum) => {
+  const scores = usePlayerScores();
   const top = { topPlayer: null, topScore: null };
 
   if (scores === null) {
@@ -54,8 +54,6 @@ const NextButton = styled(DelayedButton)`
 `
 
 export default ({
-  gameId,
-  playerName,
   selectedPoint,
   targetPoint,
   roundNum,
@@ -63,8 +61,8 @@ export default ({
   totalScore,
   onNext
 }) => {
-  const { topPlayer, topScore } = useTopScorer(gameId, roundNum);
-  const [gameFinished] = useRoundInfo(gameId, playerName)
+  const { topPlayer, topScore } = useTopScorer(roundNum);
+  const [gameFinished] = useRoundInfo()
 
   return (
     <Container>

+ 5 - 1
client/src/components/screens/RoundSummary/RoundSummary.jsx

@@ -3,6 +3,8 @@ import styled from "styled-components";
 import useMap from "../../../hooks/useMap";
 import useMarkedPoints from "./useMarkedPoints";
 import RoundInfoPane from "./RoundInfoPane";
+import useObservable from "../../../hooks/useObservable";
+import { gameIdStore, playerNameStore } from "../../../domain/store";
 
 const Container = styled.div`
   flex: 1;
@@ -25,7 +27,9 @@ const MapDiv = styled.div`
   }
 `
 
-export default ({ gameId, playerName, round, onNext }) => {
+export default ({ round, onNext }) => {
+  const gameId = useObservable(gameIdStore);
+  const playerName = useObservable(playerNameStore);
   const {
     roundNum,
     selectedPoint,

+ 16 - 10
client/src/components/util/PlayerNameInput.jsx

@@ -1,6 +1,8 @@
 import React from "react";
 import styled from "styled-components";
 import Input from "./Input";
+import { playerNameStore } from "../../domain/store";
+import useObservable from "../../hooks/useObservable";
 
 const Container = styled.div`
   display: flex;
@@ -17,13 +19,17 @@ const FormInput = styled(Input)`
   padding: 0.5em;
 `
 
-export default ({ playerName, onChangePlayerName }) => (
-  <Container>
-    <Label>Player Name</Label>
-    <FormInput
-      type="text" 
-      value={playerName || ""} 
-      onChange={({ target }) => onChangePlayerName(target.value)} 
-    />
-  </Container>
-);
+export default () => {
+  const playerName = useObservable(playerNameStore)
+  
+  return (
+    <Container>
+      <Label>Player Name</Label>
+      <FormInput
+        type="text" 
+        value={playerName || ""} 
+        onChange={({ target }) => playerNameStore.set(target.value)} 
+      />
+    </Container>
+  )
+};

+ 15 - 5
client/src/domain/GGSHService.js

@@ -1,3 +1,5 @@
+import { playerNameStore, gameIdStore } from "./store";
+
 const API_BASE = "https://kirkleon.ddns.net/terrassumptions/api";
 
 export const getStatus = async () => {
@@ -12,7 +14,8 @@ export const getStatus = async () => {
     }
 }
 
-export const createGame = async (name, timer) => {
+export const createGame = async (timer) => {
+    const name = playerNameStore.get();
     const res = await fetch(`${API_BASE}/game`, {
         method: "PUT",
         headers: {
@@ -28,7 +31,8 @@ export const createGame = async (name, timer) => {
     return gameId;
 }
 
-export const gameInfo = async (gameId) => {
+export const gameInfo = async () => {
+    const gameId = gameIdStore.get();
     const res = await fetch(`${API_BASE}/game/${gameId}`);
     if (!res.ok) {
         throw Error(res.statusText);
@@ -36,7 +40,9 @@ export const gameInfo = async (gameId) => {
     return await res.json();
 }
 
-export const joinGame = async (gameId, name) => {
+export const joinGame = async () => {
+    const gameId = gameIdStore.get();
+    const name = playerNameStore.get();
     const res = await fetch(`${API_BASE}/game/${gameId}/join`, {
         method: "POST",
         headers: {
@@ -48,7 +54,9 @@ export const joinGame = async (gameId, name) => {
     }
 }
 
-export const getCurrentRound = async (gameId, name) => {
+export const getCurrentRound = async () => {
+    const gameId = gameIdStore.get();
+    const name = playerNameStore.get();
     const res = await fetch(`${API_BASE}/game/${gameId}/current`, {
         headers: {
             "Authorization": `Name ${name}`
@@ -60,7 +68,9 @@ export const getCurrentRound = async (gameId, name) => {
     return await res.json();
 }
 
-export const sendGuess = async (gameId, name, round, point) => {
+export const sendGuess = async (round, point) => {
+    const gameId = gameIdStore.get();
+    const name = playerNameStore.get();
     const res = await fetch(`${API_BASE}/game/${gameId}/guesses/${round}`, {
         method: "POST",
         headers: {

+ 5 - 2
client/src/hooks/usePlayerScores.jsx

@@ -1,13 +1,16 @@
 import { useState, useEffect } from 'react';
 import { gameInfo } from '../domain/GGSHService';
+import useObservable from './useObservable';
+import { gameIdStore } from '../domain/store';
 
-export default gameId => {
+export default () => {
+  const gameId = useObservable(gameIdStore);
   const [scores, setScores] = useState(null);
 
   useEffect(() => {
     // define how to fetch scores
     const fetchInfo = async () => {
-      const { players } = await gameInfo(gameId);
+      const { players } = await gameInfo();
       // TODO would be nice to only setScores on a change - maybe have back-end provide timestamp?
       setScores(players);
     };

+ 5 - 1
client/src/hooks/useRoundInfo.jsx

@@ -1,7 +1,11 @@
 import { useState, useEffect } from 'react';
 import { getCurrentRound } from "../domain/GGSHService";
+import useObservable from './useObservable';
+import { gameIdStore, playerNameStore } from '../domain/store';
 
-export default (gameId, playerName) => {
+export default () => {
+  const gameId = useObservable(gameIdStore);
+  const playerName = useObservable(playerNameStore);
   const [finished, setFinished] = useState(false);
   const [roundInfo, setRoundInfo] = useState({currentRound: null, targetPoint: null, roundSeconds: null});