App.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import React from 'react';
  2. import { getGoogleApiKey, createGame, gameInfo, joinGame, getGuesses, sendGuess } from "./services/ggsh.service";
  3. import GamePanel from "./components/game-panel.component";
  4. import ApiInfo from "./components/api-info.component";
  5. import PlayerScores from "./components/player-scores.component";
  6. import RoundSummary from './components/round-summary.component';
  7. import PreGame from './components/pre-game.component';
  8. import PreRound from './components/pre-round.component';
  9. import './App.css';
  10. const LOADING = "LOADING"; // Application is loading
  11. const PRE_GAME = "PREGAME"; // Game is not yet started
  12. const PRE_ROUND = "PREROUND"; // Game is started or joined, but not playing yet
  13. const IN_ROUND = "INROUND"; // Actively playing
  14. const POST_ROUND = "POSTROUND"; // Round has finished
  15. const POST_GAME = "POSTGAME"; // Game has finished
  16. const ERROR = "ERROR"; // Error state
  17. class Game extends React.Component {
  18. constructor(props) {
  19. super(props);
  20. this.state = {
  21. googleApiKey: null,
  22. gameState: LOADING,
  23. playerName: null,
  24. gameId: null,
  25. currentRound: null,
  26. targetPoint: null,
  27. selectedPoint: null,
  28. lastScore: null,
  29. totalScore: null,
  30. players: null,
  31. roundTimer: null,
  32. }
  33. }
  34. // TODO error handling throughout - at the moment it assumes all calls always succeed
  35. async componentDidMount() {
  36. const googleApiKey = await getGoogleApiKey();
  37. this.setState({ googleApiKey, gameState: PRE_GAME });
  38. }
  39. async handleCreateGame() {
  40. this.setState({ gameState: LOADING });
  41. const { playerName } = this.state;
  42. const gameId = await createGame(playerName, 300);
  43. this.setState({ gameState: PRE_ROUND, gameId });
  44. }
  45. async handleJoinGame() {
  46. this.setState({ gameState: LOADING });
  47. const { gameId, playerName } = this.state;
  48. await joinGame(gameId, playerName);
  49. this.setState({ gameState: PRE_ROUND });
  50. }
  51. async updateRoundState() {
  52. this.setState({ gameState: LOADING })
  53. const { gameId, playerName } = this.state;
  54. const { currentRound } = await getGuesses(gameId, playerName);
  55. const { coords, players, timer } = await gameInfo(gameId);
  56. if (currentRound) {
  57. const targetPoint = coords[currentRound];
  58. this.setState({
  59. gameState: IN_ROUND,
  60. currentRound,
  61. targetPoint,
  62. selectedPoint: null,
  63. players,
  64. roundTimer: timer,
  65. });
  66. } else {
  67. this.setState({ gameState: POST_GAME, players });
  68. }
  69. }
  70. async handleSubmitGuess() {
  71. this.setState({ gameState: LOADING });
  72. const {
  73. gameId,
  74. playerName,
  75. currentRound,
  76. selectedPoint
  77. } = this.state;
  78. if (selectedPoint) {
  79. const { score, totalScore } = await sendGuess(gameId, playerName, currentRound, selectedPoint);
  80. this.setState({
  81. gameState: POST_ROUND,
  82. lastScore: score,
  83. totalScore
  84. });
  85. } else {
  86. const { score, totalScore } = await sendGuess(gameId, playerName, currentRound, { timeout: true });
  87. this.setState({
  88. gameState: POST_ROUND,
  89. lastScore: score,
  90. totalScore
  91. });
  92. }
  93. }
  94. render() {
  95. const {
  96. googleApiKey,
  97. gameState,
  98. gameId,
  99. playerName,
  100. targetPoint,
  101. selectedPoint,
  102. } = this.state;
  103. switch (gameState) {
  104. case LOADING:
  105. return <p>Loading...</p>
  106. case PRE_GAME:
  107. return <PreGame
  108. onCreateGame={() => this.handleCreateGame()}
  109. onJoinGame={() => this.handleJoinGame()}
  110. onChangeGameId={({ target }) => this.setState({gameId: target.value.trim()})}
  111. onChangePlayerName={({ target }) => this.setState({playerName: target.value.trim()})}
  112. gameId={gameId || ""}
  113. playerName={playerName || ""}
  114. />
  115. case PRE_ROUND:
  116. return <PreRound
  117. gameId={gameId}
  118. playerName={playerName}
  119. onStart={() => this.updateRoundState()}
  120. />
  121. case IN_ROUND:
  122. const { roundTimer } = this.state;
  123. return <GamePanel
  124. googleApiKey={googleApiKey}
  125. onSelectPoint={latLng => this.setState({selectedPoint: latLng})}
  126. onSubmitGuess={() => this.handleSubmitGuess()}
  127. streetViewPoint={targetPoint}
  128. selectedPoint={selectedPoint}
  129. roundSeconds={roundTimer}
  130. onTimeout={() => this.handleSubmitGuess()}
  131. />
  132. case POST_ROUND:
  133. const { currentRound, lastScore, totalScore } = this.state;
  134. return <RoundSummary
  135. googleApiKey={googleApiKey}
  136. roundNum={currentRound}
  137. score={lastScore}
  138. totalScore={totalScore}
  139. onAdvanceState={() => this.updateRoundState()}
  140. buttonText={currentRound === "5" ? "View Summary" : "Next Round"}
  141. selectedPoint={selectedPoint}
  142. targetPoint={targetPoint}
  143. />
  144. case POST_GAME:
  145. const { players } = this.state;
  146. return <PlayerScores
  147. players={players}
  148. onReturnToStart={() => this.setState({ gameState: PRE_GAME })}
  149. />
  150. case ERROR:
  151. return <p>Application encountered unrecoverable error, please refresh the page.</p>
  152. default:
  153. this.setState({ gameState: ERROR });
  154. return <p>Application state is inconsistent, please refresh and rejoin your previous game.</p>
  155. }
  156. }
  157. }
  158. const App = () => {
  159. return (
  160. <div className="App" style={{
  161. display: "flex",
  162. flexDirection: "column",
  163. justifyContent: "space-between",
  164. height: "100vh",
  165. }}>
  166. <div>
  167. <p>TerrAssumptions!</p>
  168. <hr/>
  169. </div>
  170. <Game style={{flex: 1}}/>
  171. <div>
  172. <hr/>
  173. <ApiInfo/>
  174. </div>
  175. </div>
  176. );
  177. }
  178. export default App;