2022-05-12 23:41:24 +03:00
|
|
|
import * as React from "react";
|
|
|
|
|
import { FunctionComponent, useEffect, useState } from "react";
|
2022-07-14 13:38:00 +03:00
|
|
|
import BoardView from "../components/board/BoardView";
|
|
|
|
|
import { RTMTWS } from "../rtmt/rtmt_websocket";
|
2022-05-12 23:41:24 +03:00
|
|
|
import {
|
|
|
|
|
channel_game_move,
|
|
|
|
|
channel_leave_game,
|
2022-05-15 02:06:09 +03:00
|
|
|
channel_on_game_crashed,
|
|
|
|
|
channel_on_game_start,
|
2022-05-12 23:41:24 +03:00
|
|
|
channel_on_game_update,
|
|
|
|
|
channel_on_game_user_leave,
|
2022-07-23 00:30:41 +03:00
|
|
|
channel_on_user_connection_change,
|
2022-07-14 13:38:00 +03:00
|
|
|
} from "../const/channel_names";
|
|
|
|
|
import InfoPanel from "../components/InfoPanel";
|
2022-05-12 23:41:24 +03:00
|
|
|
import { CommonMancalaGame, MancalaGame, Pit } from "mancala.js";
|
2022-07-14 13:38:00 +03:00
|
|
|
import { GameMove } from "../models/GameMove";
|
|
|
|
|
import PitAnimator from "../animation/PitAnimator";
|
|
|
|
|
import BoardViewModel from "../viewmodel/BoardViewModel";
|
2022-05-22 17:57:33 +03:00
|
|
|
import { v4 } from "uuid";
|
2022-07-14 13:38:00 +03:00
|
|
|
import { getColorByBrightness } from "../util/ColorUtil";
|
|
|
|
|
import { Theme } from "../theme/Theme";
|
2022-07-20 22:06:11 +03:00
|
|
|
import HeaderBar from "../components/headerbar/HeaderBar";
|
2022-07-14 13:38:00 +03:00
|
|
|
import PageContainer from "../components/PageContainer";
|
2022-07-15 18:11:09 +03:00
|
|
|
import Row from "../components/Row";
|
|
|
|
|
import HeaderbarIcon from "../components/headerbar/HeaderbarIcon";
|
|
|
|
|
import HeaderbarTitle from "../components/headerbar/HeaderbarTitle";
|
|
|
|
|
import ThemeSwitchMenu from "../components/headerbar/ThemeSwitchMenu";
|
|
|
|
|
import Button from "../components/Button";
|
2022-07-23 00:30:41 +03:00
|
|
|
import BoardToolbar from "../components/board/BoardToolbar";
|
|
|
|
|
import UserStatus from "../components/UserStatus";
|
|
|
|
|
import Center from "../components/Center";
|
|
|
|
|
import CircularPanel from "../components/CircularPanel";
|
|
|
|
|
import useWindowDimensions from "../hooks/useWindowDimensions";
|
|
|
|
|
import { UserConnectionInfo } from "../models/UserConnectionInfo";
|
2022-07-30 12:01:50 +03:00
|
|
|
import { Context } from "../context/context";
|
|
|
|
|
import { ConnectionState } from "../models/ConnectionState";
|
2022-05-12 23:41:24 +03:00
|
|
|
|
2022-07-30 12:01:50 +03:00
|
|
|
const Home: FunctionComponent<{
|
|
|
|
|
context: Context,
|
|
|
|
|
userKey?: string,
|
|
|
|
|
connectionState: ConnectionState
|
|
|
|
|
}> = ({ context, userKey, connectionState }) => {
|
2021-06-30 19:41:53 +03:00
|
|
|
|
2022-05-12 23:41:24 +03:00
|
|
|
const [searchingOpponent, setSearchingOpponent] = useState<boolean>(false);
|
2021-07-04 01:23:10 +03:00
|
|
|
|
2022-07-14 13:23:15 +03:00
|
|
|
const [game, setGame] = useState<CommonMancalaGame | undefined>(undefined);
|
2021-07-04 01:04:59 +03:00
|
|
|
|
2022-07-14 13:23:15 +03:00
|
|
|
const [crashMessage, setCrashMessage] = useState<string | undefined>(undefined);
|
2021-07-04 00:45:00 +03:00
|
|
|
|
2022-07-14 13:23:15 +03:00
|
|
|
const [userKeyWhoLeave, setUserKeyWhoLeave] = useState<string | undefined>(undefined);
|
2022-05-12 23:41:24 +03:00
|
|
|
|
2022-07-14 13:23:15 +03:00
|
|
|
const [boardViewModel, setBoardViewModel] = useState<BoardViewModel | undefined>(undefined);
|
2022-05-12 23:41:24 +03:00
|
|
|
|
2022-07-14 13:23:15 +03:00
|
|
|
const [boardId, setBoardId] = useState<string>("-1");
|
2022-05-12 23:41:24 +03:00
|
|
|
|
2022-07-14 13:23:15 +03:00
|
|
|
const [pitAnimator, setPitAnimator] = useState<PitAnimator | undefined>(undefined);
|
2021-07-04 01:04:59 +03:00
|
|
|
|
2022-07-09 08:31:43 +03:00
|
|
|
const [theme, setTheme] = useState<Theme | undefined>(undefined);
|
|
|
|
|
|
2022-07-15 18:50:05 +03:00
|
|
|
// It is a flag for ongoing action such as send game move.
|
|
|
|
|
// We have to block future actions if there is an ongoing action.
|
|
|
|
|
const [hasOngoingAction, setHasOngoingAction] = useState<boolean>(false);
|
|
|
|
|
|
2022-07-23 00:30:41 +03:00
|
|
|
const { height, width } = useWindowDimensions();
|
|
|
|
|
|
|
|
|
|
const [isOpponentOnline, setIsOpponentOnline] = useState<boolean>(false);
|
|
|
|
|
|
2022-05-15 02:01:51 +03:00
|
|
|
const listenMessages = (pitAnimator: PitAnimator) => {
|
2022-05-15 02:06:09 +03:00
|
|
|
context.rtmt.listenMessage(channel_on_game_start, (message: Object) => {
|
2022-05-04 23:41:04 +03:00
|
|
|
const newGame: CommonMancalaGame = message as CommonMancalaGame;
|
2022-05-12 23:41:24 +03:00
|
|
|
const mancalaGame = MancalaGame.createFromMancalaGame(newGame);
|
2022-05-15 02:01:51 +03:00
|
|
|
setSearchingOpponent(false);
|
|
|
|
|
setGame(mancalaGame);
|
|
|
|
|
pitAnimator.setNewGame(mancalaGame);
|
2022-07-15 18:50:05 +03:00
|
|
|
setHasOngoingAction(false);
|
2022-05-12 23:41:24 +03:00
|
|
|
});
|
2021-06-27 19:28:55 +03:00
|
|
|
|
2022-05-15 02:01:51 +03:00
|
|
|
context.rtmt.listenMessage(channel_on_game_update, (message: Object) => {
|
2022-05-04 23:41:04 +03:00
|
|
|
const newGame: CommonMancalaGame = message as CommonMancalaGame;
|
2022-05-15 02:01:51 +03:00
|
|
|
const mancalaGame = MancalaGame.createFromMancalaGame(newGame);
|
|
|
|
|
setGame(mancalaGame);
|
|
|
|
|
pitAnimator.setUpdatedGame(mancalaGame);
|
2022-07-15 18:50:05 +03:00
|
|
|
setHasOngoingAction(false);
|
2022-05-12 23:41:24 +03:00
|
|
|
});
|
2021-07-04 00:45:00 +03:00
|
|
|
|
2022-05-15 02:06:09 +03:00
|
|
|
context.rtmt.listenMessage(channel_on_game_crashed, (message: any) => {
|
2022-05-12 23:41:24 +03:00
|
|
|
const newCrashMessage = message as string;
|
2022-05-04 23:41:04 +03:00
|
|
|
console.error("on_game_crash");
|
|
|
|
|
console.error(newCrashMessage);
|
2022-05-12 23:41:24 +03:00
|
|
|
setCrashMessage(newCrashMessage);
|
|
|
|
|
});
|
2021-07-04 01:04:59 +03:00
|
|
|
|
2022-05-12 23:41:24 +03:00
|
|
|
context.rtmt.listenMessage(channel_on_game_user_leave, (message: any) => {
|
2022-04-23 12:53:08 +03:00
|
|
|
const userKeyWhoLeave = message;
|
2022-05-12 23:41:24 +03:00
|
|
|
setUserKeyWhoLeave(userKeyWhoLeave);
|
2022-07-15 18:50:05 +03:00
|
|
|
setHasOngoingAction(false);
|
2022-05-12 23:41:24 +03:00
|
|
|
});
|
2022-07-23 00:30:41 +03:00
|
|
|
|
|
|
|
|
context.rtmt.listenMessage(channel_on_user_connection_change, (message: any) => {
|
|
|
|
|
const userConnectionInfo = message as UserConnectionInfo;
|
|
|
|
|
//todo: change this when implementing watch the game feature
|
|
|
|
|
setIsOpponentOnline(userConnectionInfo.isOnline);
|
|
|
|
|
})
|
2022-05-12 23:41:24 +03:00
|
|
|
};
|
2021-06-27 19:28:55 +03:00
|
|
|
|
2022-05-15 02:01:51 +03:00
|
|
|
const updateBoardViewModel = (boardViewModel: BoardViewModel) => {
|
2022-05-22 17:57:33 +03:00
|
|
|
boardViewModel.id = v4();
|
2022-05-15 02:01:51 +03:00
|
|
|
setBoardId(boardViewModel.id);
|
|
|
|
|
setBoardViewModel(boardViewModel);
|
|
|
|
|
};
|
|
|
|
|
|
2022-07-13 22:39:01 +03:00
|
|
|
const resetGameState = () => {
|
|
|
|
|
setGame(undefined);
|
|
|
|
|
setCrashMessage(undefined);
|
|
|
|
|
setUserKeyWhoLeave(undefined);
|
2022-07-15 18:50:05 +03:00
|
|
|
setHasOngoingAction(false);
|
2022-07-13 22:39:01 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getBoardIndex = (index: number) => {
|
2022-07-15 18:50:05 +03:00
|
|
|
if (!game) return -1;
|
2022-07-13 22:39:01 +03:00
|
|
|
if (userKey === game.player2Id) return index + game.board.pits.length / 2;
|
|
|
|
|
return index;
|
|
|
|
|
};
|
|
|
|
|
|
2022-07-23 00:30:41 +03:00
|
|
|
const getOpponentId = () => game?.player1Id === userKey ? game?.player2Id : game?.player1Id;
|
|
|
|
|
|
2022-07-15 18:50:05 +03:00
|
|
|
const checkHasAnOngoingAction = () => hasOngoingAction;
|
|
|
|
|
|
2021-07-02 23:13:38 +03:00
|
|
|
React.useEffect(() => {
|
2022-07-09 08:31:43 +03:00
|
|
|
setTheme(context.themeManager.theme);
|
2022-05-15 02:01:51 +03:00
|
|
|
const pitAnimator = new PitAnimator(context, updateBoardViewModel);
|
|
|
|
|
setPitAnimator(pitAnimator);
|
|
|
|
|
listenMessages(pitAnimator);
|
|
|
|
|
return () => {
|
|
|
|
|
pitAnimator.dispose();
|
|
|
|
|
};
|
2022-05-12 23:41:24 +03:00
|
|
|
}, []);
|
2021-07-02 23:13:38 +03:00
|
|
|
|
2022-06-04 20:54:19 +03:00
|
|
|
React.useEffect(() => {
|
2022-07-09 08:31:43 +03:00
|
|
|
context.themeManager.onThemeChange = (theme) => {
|
|
|
|
|
setTheme(theme);
|
2022-07-14 13:23:15 +03:00
|
|
|
pitAnimator && game && updateBoardViewModel(pitAnimator.getBoardViewModelFromGame(game));
|
2022-06-04 20:54:19 +03:00
|
|
|
};
|
|
|
|
|
}, [boardViewModel]);
|
|
|
|
|
|
2022-07-13 22:39:01 +03:00
|
|
|
const onNewGameClick = () => {
|
2022-05-12 23:41:24 +03:00
|
|
|
resetGameState();
|
|
|
|
|
setSearchingOpponent(true);
|
|
|
|
|
context.rtmt.sendMessage("new_game", {});
|
|
|
|
|
};
|
2021-07-02 23:13:38 +03:00
|
|
|
|
2022-07-13 22:39:01 +03:00
|
|
|
const onLeaveGameClick = () => {
|
2022-05-12 23:41:24 +03:00
|
|
|
context.rtmt.sendMessage(channel_leave_game, {});
|
|
|
|
|
};
|
2021-06-29 03:29:46 +03:00
|
|
|
|
2022-07-13 22:45:06 +03:00
|
|
|
const onPitSelect = (index: number, pit: Pit) => {
|
2022-07-23 00:30:41 +03:00
|
|
|
if (checkHasAnOngoingAction()) {
|
2022-07-15 18:50:05 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setHasOngoingAction(true);
|
|
|
|
|
if (!boardViewModel) return;
|
2022-05-15 02:01:51 +03:00
|
|
|
//TODO : stoneCount comes from view model!
|
|
|
|
|
if (pit.stoneCount === 0) {
|
|
|
|
|
//TODO : warn user
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-05-22 17:58:11 +03:00
|
|
|
boardViewModel.pits[getBoardIndex(index)].pitColor =
|
|
|
|
|
context.themeManager.theme.pitSelectedColor;
|
|
|
|
|
updateBoardViewModel(boardViewModel);
|
2022-05-12 23:41:24 +03:00
|
|
|
const gameMove: GameMove = { index: index };
|
|
|
|
|
context.rtmt.sendMessage(channel_game_move, gameMove);
|
|
|
|
|
};
|
2021-06-29 03:29:46 +03:00
|
|
|
|
2022-07-13 22:39:01 +03:00
|
|
|
const textColorOnBoard = getColorByBrightness(
|
|
|
|
|
context.themeManager.theme.boardColor,
|
2022-07-13 15:21:30 +03:00
|
|
|
context.themeManager.theme.textColor,
|
|
|
|
|
context.themeManager.theme.textLightColor
|
2022-06-04 20:54:19 +03:00
|
|
|
);
|
2022-07-15 18:11:09 +03:00
|
|
|
const textColorOnAppBar = getColorByBrightness(
|
|
|
|
|
context.themeManager.theme.appBarBgColor,
|
|
|
|
|
context.themeManager.theme.textColor,
|
|
|
|
|
context.themeManager.theme.textLightColor
|
|
|
|
|
);
|
|
|
|
|
const renderNewGameBtn = userKeyWhoLeave || !game || (game && game.state == "ended");
|
2022-07-23 00:30:41 +03:00
|
|
|
const showBoardView = game && boardViewModel && userKey && true;
|
|
|
|
|
const opponentUser = { id: getOpponentId() || "0", name: "Anonymous", isOnline: isOpponentOnline, isAnonymous: true };
|
|
|
|
|
const user = { id: userKey || "1", name: "Anonymous", isOnline: connectionState === "connected", isAnonymous: true };
|
|
|
|
|
|
|
|
|
|
const isMobile = width < 600;
|
|
|
|
|
|
2022-05-12 23:41:24 +03:00
|
|
|
return (
|
2022-07-13 22:39:01 +03:00
|
|
|
<PageContainer theme={theme!}>
|
2022-07-15 18:11:09 +03:00
|
|
|
<HeaderBar color={theme?.appBarBgColor}>
|
2022-07-15 18:50:05 +03:00
|
|
|
<Row>
|
|
|
|
|
<HeaderbarIcon />
|
|
|
|
|
<HeaderbarTitle title={context.texts.Mancala} color={textColorOnAppBar} />
|
|
|
|
|
</Row>
|
|
|
|
|
<Row>
|
|
|
|
|
<ThemeSwitchMenu context={context} textColor={textColorOnAppBar} />
|
|
|
|
|
<Button
|
|
|
|
|
context={context}
|
|
|
|
|
color={context.themeManager.theme.pitColor}
|
|
|
|
|
text={renderNewGameBtn ? context.texts.NewGame : context.texts.Leave}
|
|
|
|
|
onClick={renderNewGameBtn ? onNewGameClick : onLeaveGameClick} />
|
|
|
|
|
</Row>
|
|
|
|
|
</HeaderBar>
|
2022-07-23 00:30:41 +03:00
|
|
|
<BoardToolbar style={{ justifyContent: "center" }} visible={showBoardView && isMobile || false}>
|
|
|
|
|
<InfoPanel
|
|
|
|
|
style={{ marginTop: "0.5rem", marginBottom: "0.5rem" }}
|
|
|
|
|
context={context}
|
|
|
|
|
game={game}
|
|
|
|
|
crashMessage={crashMessage}
|
|
|
|
|
userKey={userKey}
|
|
|
|
|
userKeyWhoLeave={userKeyWhoLeave} />
|
|
|
|
|
</BoardToolbar>
|
|
|
|
|
<BoardToolbar style={{ alignItems: "flex-end" }} visible={showBoardView || false}>
|
|
|
|
|
<UserStatus style={{
|
|
|
|
|
marginBottom: "0.5rem", marginLeft: "6%", maxWidth: isMobile ? "40vw" : "30vw",
|
|
|
|
|
width: isMobile ? "40vw" : "30vw"
|
|
|
|
|
}} context={context} layoutMode="left" user={opponentUser} visible={showBoardView || false} />
|
|
|
|
|
<InfoPanel
|
|
|
|
|
style={{
|
|
|
|
|
marginTop: "0.5rem", marginBottom: "0.5rem",
|
|
|
|
|
}}
|
|
|
|
|
context={context}
|
|
|
|
|
game={game}
|
|
|
|
|
crashMessage={crashMessage}
|
|
|
|
|
userKey={userKey}
|
|
|
|
|
userKeyWhoLeave={userKeyWhoLeave}
|
|
|
|
|
visible={!isMobile} />
|
|
|
|
|
<UserStatus style={{
|
|
|
|
|
marginBottom: "0.5rem", marginRight: "6%", maxWidth: isMobile ? "40vw" : "30vw",
|
|
|
|
|
width: isMobile ? "40vw" : "30vw"
|
|
|
|
|
}} context={context} layoutMode="right" user={user} visible={showBoardView || false} />
|
|
|
|
|
</BoardToolbar>
|
|
|
|
|
{showBoardView ? (
|
2022-05-12 23:41:24 +03:00
|
|
|
<BoardView
|
|
|
|
|
userKey={userKey}
|
|
|
|
|
game={game}
|
2022-05-15 02:01:51 +03:00
|
|
|
boardId={boardId}
|
|
|
|
|
boardViewModel={boardViewModel}
|
|
|
|
|
context={context}
|
2022-07-14 13:23:15 +03:00
|
|
|
onPitSelect={onPitSelect} />
|
2022-07-23 00:30:41 +03:00
|
|
|
) : searchingOpponent && (
|
|
|
|
|
<Center>
|
|
|
|
|
<CircularPanel color={context.themeManager.theme.boardColor}>
|
|
|
|
|
<h4 style={{ margin: "0", color: textColorOnBoard }}>{`${context.texts.SearchingOpponent} ${context.texts.PleaseWait}...`}</h4>
|
|
|
|
|
</CircularPanel>
|
|
|
|
|
</Center>
|
2022-05-12 23:41:24 +03:00
|
|
|
)}
|
2022-07-13 22:39:01 +03:00
|
|
|
</PageContainer>
|
2022-05-12 23:41:24 +03:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default Home;
|