diff --git a/package.json b/package.json index 0550783..23ae830 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mancala-frontend", - "version": "0.1.3-beta.1", + "version": "0.1.3-beta.2", "description": "", "main": "index.js", "scripts": { @@ -14,7 +14,7 @@ "author": "", "license": "ISC", "dependencies": { - "mancala.js": "^0.0.2-beta.0", + "mancala.js": "^0.0.2-beta.1", "react": "^17.0.2", "react-dom": "^17.0.2" }, diff --git a/src/Home.tsx b/src/Home.tsx index 7b2fc59..3e95d96 100644 --- a/src/Home.tsx +++ b/src/Home.tsx @@ -1,179 +1,255 @@ -import * as React from 'react'; -import { FunctionComponent, useState } from 'react'; -import BoardView from './components/BoardView'; -import { context } from './context' -import { RTMTWS } from './rtmt/rtmt_websocket'; -import { channel_game_move, channel_leave_game, channel_on_game_update, channel_on_game_user_leave } from './channel_names'; -import Button from './components/Button'; -import InfoPanel from './components/InfoPanel'; -import { CommonMancalaGame, MancalaGame, Pit } from 'mancala.js' -import { GameMove } from './models/GameMove'; +import * as React from "react"; +import { FunctionComponent, useEffect, useState } from "react"; +import BoardView from "./components/BoardView"; +import { context } from "./context"; +import { RTMTWS } from "./rtmt/rtmt_websocket"; +import { + channel_game_move, + channel_leave_game, + channel_on_game_update, + channel_on_game_user_leave, +} from "./channel_names"; +import Button from "./components/Button"; +import InfoPanel from "./components/InfoPanel"; +import { CommonMancalaGame, MancalaGame, Pit } from "mancala.js"; +import { GameMove } from "./models/GameMove"; -type ConnectionState = "connecting" | "error" | "connected" | "reconnecting" +type ConnectionState = "connecting" | "error" | "connected" | "reconnecting"; const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => { const [userKey, setUserKey] = useState(undefined); - const [connectionState, setConnetionState] = useState("connecting") + const [connectionState, setConnetionState] = + useState("connecting"); - const [searchingOpponent, setSearchingOpponent] = useState(false) + const [searchingOpponent, setSearchingOpponent] = useState(false); - const [game, setGame] = useState(undefined) + const [game, setGame] = useState(undefined); + const gameRef = React.useRef(game); - const [crashMessage, setCrashMessage] = useState(undefined) + const [crashMessage, setCrashMessage] = useState(undefined); - const [userKeyWhoLeave, setUserKeyWhoLeave] = useState(undefined) + const [userKeyWhoLeave, setUserKeyWhoLeave] = useState(undefined); + + const [animationPitIndex, setAnimationPitIndex] = useState(-1); + + const [intervalId, setIntervalId] = useState(-1); + + useEffect(() => { + gameRef.current = game; + }); const onConnectionDone = () => { - setConnetionState("connected") - } + setConnetionState("connected"); + }; const onConnectionLost = () => { - connectToServer("reconnecting") - } + connectToServer("reconnecting"); + }; const onConnectionError = (event: Event) => { - setConnetionState("error") - } + setConnetionState("error"); + }; const connectToServer = (connectionState: ConnectionState) => { - setConnetionState(connectionState) + setConnetionState(connectionState); context.userKeyStore.getUserKey((userKey: string) => { - setUserKey(userKey) - const rtmtws = context.rtmt as RTMTWS + setUserKey(userKey); + const rtmtws = context.rtmt as RTMTWS; if (rtmtws) { - rtmtws.initWebSocket(userKey, onConnectionDone, onConnectionLost, onConnectionError) + rtmtws.initWebSocket( + userKey, + onConnectionDone, + onConnectionLost, + onConnectionError + ); } else { console.error("context.rtmt is not RTMTWS"); } - }) - } + }); + }; const listenMessages = () => { context.rtmt.listenMessage(channel_on_game_update, (message: Object) => { const newGame: CommonMancalaGame = message as CommonMancalaGame; - setGame(MancalaGame.createFromMancalaGame(newGame)) - }) + const mancalaGame = MancalaGame.createFromMancalaGame(newGame); + if (gameRef.current && mancalaGame.history.length > 0) { + const lastHistoryItem = + mancalaGame.history[mancalaGame.history.length - 1]; + if (lastHistoryItem.gameSteps.length > 0) { + let stepIndex = 0; + if (intervalId) { + clearInterval(intervalId); + } + const id = setInterval(() => { + if (stepIndex === lastHistoryItem.gameSteps.length) { + clearInterval(id); + setAnimationPitIndex(-1); + setGame(mancalaGame); + } else { + const gameStep = lastHistoryItem.gameSteps[stepIndex]; + const index = mancalaGame.board.getPitIndexCircularly( + gameStep.index + ); + setAnimationPitIndex(index); + } + stepIndex++; + }, 250); + setIntervalId(intervalId); + } + } + }); context.rtmt.listenMessage("on_game_start", (message: Object) => { const newGame: CommonMancalaGame = message as CommonMancalaGame; - setSearchingOpponent(false) - setGame(MancalaGame.createFromMancalaGame(newGame)) - }) + setSearchingOpponent(false); + setGame(MancalaGame.createFromMancalaGame(newGame)); + }); - context.rtmt.listenMessage("on_game_crashed", (message : any) => { - const newCrashMessage = message as string + context.rtmt.listenMessage("on_game_crashed", (message: any) => { + const newCrashMessage = message as string; console.error("on_game_crash"); console.error(newCrashMessage); - setCrashMessage(newCrashMessage) - }) + setCrashMessage(newCrashMessage); + }); - context.rtmt.listenMessage(channel_on_game_user_leave, (message : any) => { + context.rtmt.listenMessage(channel_on_game_user_leave, (message: any) => { const userKeyWhoLeave = message; - setUserKeyWhoLeave(userKeyWhoLeave) - }) - } + setUserKeyWhoLeave(userKeyWhoLeave); + }); + }; React.useEffect(() => { - listenMessages() - connectToServer("connecting") - }, []) + listenMessages(); + connectToServer("connecting"); + }, []); const resetGameState = () => { - setGame(undefined) - setCrashMessage(undefined) - setUserKeyWhoLeave(undefined) - } + setGame(undefined); + setCrashMessage(undefined); + setUserKeyWhoLeave(undefined); + }; const newGameClick = () => { - resetGameState() - setSearchingOpponent(true) - context.rtmt.sendMessage("new_game", {}) - } + resetGameState(); + setSearchingOpponent(true); + context.rtmt.sendMessage("new_game", {}); + }; const leaveGame = () => { - context.rtmt.sendMessage(channel_leave_game, {}) - } + context.rtmt.sendMessage(channel_leave_game, {}); + }; const onHoleSelect = (index: number, hole: Pit) => { - const gameMove: GameMove = { index: index } - context.rtmt.sendMessage(channel_game_move, gameMove) - } + const gameMove: GameMove = { index: index }; + context.rtmt.sendMessage(channel_game_move, gameMove); + }; - const showConnectionState = connectionState != "connected" + const showConnectionState = connectionState != "connected"; const connectionStateText = () => { let map: { [key: string]: string } = { - "connecting": context.texts.Connecting, - "connected": context.texts.Connected, - "error": context.texts.CannotConnect, - "reconnecting": context.texts.ConnectingAgain + connecting: context.texts.Connecting, + connected: context.texts.Connected, + error: context.texts.CannotConnect, + reconnecting: context.texts.ConnectingAgain, }; - return map[connectionState] - } + return map[connectionState]; + }; const renderNewGameButton = () => { - const newGame =