Browse Source

Moving the join existing logic to only be after redirect

Kirk Trombley 5 years ago
parent
commit
e5955365c5

+ 1 - 1
client/package.json

@@ -7,7 +7,7 @@
     "pretty-ms": "^5.1.0",
     "react": "^16.12.0",
     "react-dom": "^16.12.0",
-    "react-scripts": "3.1.1",
+    "react-scripts": "^3.3.0-next.80",
     "styled-components": "^4.4.1"
   },
   "scripts": {

+ 0 - 2
client/src/App.js

@@ -13,9 +13,7 @@ const Wrapper = styled.div`
 `
 
 export default () => (
-  <React.StrictMode>
     <Wrapper>
       <Game/>
     </Wrapper>
-  </React.StrictMode>
 );

+ 10 - 3
client/src/components/Game/Game.jsx

@@ -19,6 +19,7 @@ const initialState = {
   gameId: null,
   playerName: null,
   lastRound: null,
+  joined: false,
 }
 
 const extractAndRemoveSearchParam = param => {
@@ -33,8 +34,13 @@ 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 joinCode = extractAndRemoveSearchParam("join");
+  if (joinCode) {
+    setGameStateAnd(PRE_ROUND, { gameId: joinCode, joined: false });
+  }
+
   const summaryCode = extractAndRemoveSearchParam("summary");
   if (summaryCode) {
     setGameStateAnd(POST_GAME, { gameId: summaryCode });
@@ -46,8 +52,7 @@ const Game = () => {
         <HeaderAndFooter>
           <PreGame
             initPlayerName={state.playerName}
-            initGameId={joinCode}
-            onGameJoined={({ gameId, playerName }) => setGameStateAnd(PRE_ROUND, { gameId, playerName })}
+            onGameJoined={onGameJoined}
           />
         </HeaderAndFooter>
       );
@@ -57,6 +62,8 @@ const Game = () => {
           <PreRound
             gameId={state.gameId}
             playerName={state.playerName}
+            joined={state.joined}
+            onGameJoined={onGameJoined}
             onStart={() => setGameState(IN_ROUND)}
           />
         </HeaderAndFooter>
@@ -79,7 +86,7 @@ const Game = () => {
         <HeaderAndFooter>
           <PlayerScores
             gameId={state.gameId}
-            onReturnToStart={() => setGameState(PRE_GAME)}
+            onReturnToStart={() => setGameStateAnd(PRE_GAME, { gameId: null, joined: false })}
           />
         </HeaderAndFooter>
       );

+ 0 - 37
client/src/components/screens/PreGame/JoinGameInput.jsx

@@ -1,37 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-import Button from "../../util/Button";
-import Input from "../../util/Input";
-
-const Container = styled.div`
-  display: flex;
-  flex-flow: column nowrap;
-  justify-content: center;
-  align-items: center;
-`
-
-const Label = styled.span`
-  padding: 0.1em;
-`
-
-const FormInput = styled(Input)`
-  padding: 0.5em;
-`
-
-const FormButton = styled(Button)`
-  margin: 0.1em;
-`
-
-export default ({ gameId, onChangeGameId, onJoinGame, cannotJoinGame }) => (
-  <Container>
-    <Label>Game ID:</Label>
-    <FormInput 
-      type="text" 
-      value={gameId || ""} 
-      onChange={onChangeGameId} 
-    />
-    <FormButton onClick={onJoinGame} disabled={cannotJoinGame}>
-      Join Existing Game
-    </FormButton>
-  </Container>
-);

+ 17 - 19
client/src/components/screens/PreGame/NewGameInput.jsx

@@ -50,22 +50,20 @@ const DropdownItem = styled.div`
   }
 `
 
-export default ({ onCreateGame, cannotCreateGame, timer, setTimer }) => {
-  return (
-    <Container>
-      <Dropdown>
-        <DropdownButton>
-          Round Timer: {ms(timer * 1000)}
-        </DropdownButton>
-        <DropdownList>
-          <DropdownItem onClick={() => setTimer(30)}>30 Seconds</DropdownItem>
-          <DropdownItem onClick={() => setTimer(300)}>5 Minutes</DropdownItem>
-          <DropdownItem onClick={() => setTimer(3600)}>1 Hour</DropdownItem>
-        </DropdownList>
-      </Dropdown>
-      <Button onClick={onCreateGame} disabled={cannotCreateGame}>
-        Create New Game
-      </Button>
-    </Container>
-  );
-};
+export default ({ onCreateGame, cannotCreateGame, timer, setTimer }) => (
+  <Container>
+    <Dropdown>
+      <DropdownButton>
+        Round Timer: {ms(timer * 1000)}
+      </DropdownButton>
+      <DropdownList>
+        <DropdownItem onClick={() => setTimer(30)}>30 Seconds</DropdownItem>
+        <DropdownItem onClick={() => setTimer(300)}>5 Minutes</DropdownItem>
+        <DropdownItem onClick={() => setTimer(3600)}>1 Hour</DropdownItem>
+      </DropdownList>
+    </Dropdown>
+    <Button onClick={onCreateGame} disabled={cannotCreateGame}>
+      Create New Game
+    </Button>
+  </Container>
+);

+ 2 - 14
client/src/components/screens/PreGame/PreGame.jsx

@@ -3,8 +3,7 @@ import styled from "styled-components";
 import Loading from "../../util/Loading";
 import PlayerNameInput from "./PlayerNameInput";
 import NewGameInput from "./NewGameInput"
-import JoinGameInput from "./JoinGameInput";
-import { createGame, joinGame } from "../../../domain/GGSHService";
+import { createGame } from "../../../domain/GGSHService";
 
 const Container = styled.div`
   display: flex;
@@ -30,9 +29,8 @@ const Divider = styled.hr`
   }
 `
 
-export default ({ initPlayerName, initGameId, onGameJoined }) => {
+export default ({ initPlayerName, onGameJoined }) => {
   const [loading, setLoading] = useState(false);
-  const [gameId, setGameId] = useState(initGameId);
   const [playerName, setPlayerName] = useState(initPlayerName);
   const [timer, setTimer] = useState(300);
 
@@ -41,28 +39,18 @@ export default ({ initPlayerName, initGameId, onGameJoined }) => {
   }
 
   const onChangePlayerName = ({ target }) => setPlayerName(target.value);
-  const onChangeGameId = ({ target }) => setGameId(target.value.trim());
   const onCreateGame = async () => {
     setLoading(true);
     const gameId = await createGame(playerName, timer);
     onGameJoined({ gameId, playerName });
   };
   const cannotCreateGame = !playerName || playerName.length === 0;
-  const onJoinGame = async () => {
-    // TODO would like to support re-joining a game you left
-    setLoading(true);
-    await joinGame(gameId, playerName);
-    onGameJoined({ gameId, playerName });
-  };
-  const cannotJoinGame = cannotCreateGame || !gameId || gameId.length === 0;
 
   return (
     <Container>
       <PlayerNameInput {...{ playerName, onChangePlayerName }} />
       <Divider/>
       <NewGameInput {...{ onCreateGame, cannotCreateGame, timer, setTimer }} />
-      <Divider/>
-      <JoinGameInput {...{ gameId, onChangeGameId, onJoinGame, cannotJoinGame }} />
     </Container>
   );
 }

+ 96 - 8
client/src/components/screens/PreRound.jsx

@@ -1,10 +1,16 @@
-import React from "react";
+import React, { useState } from "react";
 import styled from "styled-components";
 import ClickToCopy from "../util/ClickToCopy";
 import Button from "../util/Button";
+import Loading from "../util/Loading";
+import PlayerNameInput from "../screens/PreGame/PlayerNameInput";
+import usePlayerScores from "../../hooks/usePlayerScores";
+import { joinGame } from "../../domain/GGSHService";
+
+const InfoContainer = styled.div`
+  flex: 3;
+  align-self: center;
 
-const Container = styled.div`
-  flex: 1;
   display: flex;
   flex-flow: column nowrap;
   justify-content: center;
@@ -15,26 +21,108 @@ const Label = styled.span`
   padding: 0.2em;
 `
 
-const StartButton = styled(Button)`
+const FormButton = styled(Button)`
   padding: 1em;
   margin: 0.2em;
   margin-top: 0.3em;
 `
 
+const PlayerListContainer = styled.div`
+  flex: 1;
+  
+  display: flex;
+  flex-flow: column nowrap;
+  justify-content: flex-start;
+  align-items: flex-start;
+
+  border-left: 2px solid #777;
+  height: 100%;
+  padding-left: 1rem;
+`
+
+const PlayerList = styled.ul`
+`
+
+const PlayerName = styled.li`
+`
+
+const PageContainer = styled.div`
+  flex: 1;
+  display: flex;
+  flex-flow: row nowrap;
+  justify-content: space-between;
+  align-items: flex-start;
+`
+
 const getUrl = gameId => {
   const u = new URL(window.location.href);
   u.searchParams.append("join", gameId);
   return u.href;
 }
 
-const PreRound = ({ gameId, playerName, onStart }) => (
-  <Container>
+const JoinedInfo = ({ gameId, playerName, onStart }) => (
+  <InfoContainer>
     <Label>Playing as {playerName}</Label>
     <Label>Game Code: <ClickToCopy text={gameId} /></Label>
     <Label>Or, send the following link:</Label>
     <Label><ClickToCopy text={getUrl(gameId)} /></Label>
-    <StartButton onClick={onStart}>Start Game</StartButton>
-  </Container>
+    <FormButton onClick={onStart}>Start Game</FormButton>
+  </InfoContainer>
+);
+
+const UnjoinedInfo = ({ gameId, onGameJoined }) => {
+  const [loading, setLoading] = useState(false);
+  const [failed, setFailed] = useState(false);
+  const [playerName, setPlayerName] = useState(null);
+
+  if (loading) {
+    return <InfoContainer><Loading/></InfoContainer>
+  }
+
+  const onJoinGame = async () => {
+    // TODO would like to support re-joining a game you left
+    setLoading(true);
+    try {
+      await joinGame(gameId, playerName);
+    } catch (err) {
+      // failed to join the game
+      setFailed(true);
+      setLoading(false);
+      return;
+    }
+    setFailed(false);
+    onGameJoined({ gameId, playerName });
+  };
+  const cannotJoinGame = !playerName || playerName.length === 0;
+
+  return (
+    <InfoContainer>
+      {failed && <Label>Failed to join the game! Maybe try a different name?</Label>}
+      <Label>Joining {gameId}...</Label>
+      <PlayerNameInput playerName={playerName} onChangePlayerName={({ target }) => setPlayerName(target.value)}/>
+      <FormButton onClick={onJoinGame} disabled={cannotJoinGame}>Join Game</FormButton>
+    </InfoContainer>
+  );
+}
+
+const PreRound = ({ gameId, playerName, joined, onGameJoined, onStart }) => (
+  <PageContainer>
+    {
+      joined
+        ? <JoinedInfo gameId={gameId} playerName={playerName} onStart={onStart}/>
+        : <UnjoinedInfo gameId={gameId} onGameJoined={onGameJoined}/>
+    }
+    <PlayerListContainer>
+      <Label>Players</Label>
+      <PlayerList>
+        {
+          usePlayerScores(gameId)?.map(({ name }) => (
+            <PlayerName key={name}>{name}</PlayerName>
+          ))
+        }
+      </PlayerList>
+    </PlayerListContainer>
+  </PageContainer>
 );
 
 export default PreRound;

+ 1 - 0
client/src/hooks/usePlayerScores.jsx

@@ -8,6 +8,7 @@ export default gameId => {
     // define how to fetch scores
     const fetchInfo = async () => {
       const { players } = await gameInfo(gameId);
+      // TODO would be nice to only setScores on a change - maybe have back-end provide timestamp?
       setScores(players);
     };
     // when the hook triggers, fetch the game info

File diff suppressed because it is too large
+ 258 - 263
client/yarn.lock


Some files were not shown because too many files changed in this diff