feature: spectator
This commit is contained in:
parent
419ae7600a
commit
c73333667d
@ -2,70 +2,83 @@ import * as React from "react";
|
|||||||
import { FunctionComponent } from "react";
|
import { FunctionComponent } from "react";
|
||||||
import { Context } from "../context/context";
|
import { Context } from "../context/context";
|
||||||
import { Game } from "../models/Game";
|
import { Game } from "../models/Game";
|
||||||
|
import { User } from "../models/User";
|
||||||
import { getColorByBrightness } from "../util/ColorUtil";
|
import { getColorByBrightness } from "../util/ColorUtil";
|
||||||
import CircularPanel from "./CircularPanel";
|
import CircularPanel from "./CircularPanel";
|
||||||
|
|
||||||
function getInfoPanelTextByGameState(params: {
|
function getInfoPanelTextByGameState(params: {
|
||||||
context: Context;
|
context: Context;
|
||||||
game?: Game;
|
game?: Game;
|
||||||
crashMessage?: string;
|
currentUser: User;
|
||||||
userKey?: string;
|
whitePlayer: User;
|
||||||
userKeyWhoLeave?: string;
|
blackPlayer: User;
|
||||||
|
leftPlayer?: User;
|
||||||
|
isSpectator?: boolean;
|
||||||
}): string | undefined {
|
}): string | undefined {
|
||||||
const {
|
const {
|
||||||
context,
|
context,
|
||||||
game,
|
game,
|
||||||
crashMessage,
|
currentUser,
|
||||||
userKey,
|
whitePlayer,
|
||||||
userKeyWhoLeave,
|
blackPlayer,
|
||||||
|
leftPlayer,
|
||||||
|
isSpectator
|
||||||
} = params;
|
} = params;
|
||||||
if (crashMessage) {
|
|
||||||
return context.texts.GameCrashed + " " + crashMessage;
|
if (leftPlayer) {
|
||||||
} else if (userKeyWhoLeave) {
|
return isSpectator ? `${leftPlayer.name} ${context.texts.UserLeftTheGame}` :
|
||||||
let message = context.texts.OpponentLeavesTheGame;
|
leftPlayer.id == currentUser.id ? context.texts.YouLeftTheGame : context.texts.OpponentLeftTheGame;
|
||||||
if (userKeyWhoLeave == userKey) {
|
|
||||||
message = context.texts.YouLeftTheGame;
|
|
||||||
}
|
}
|
||||||
return message;
|
|
||||||
} else if (game?.mancalaGame.state == "ended") {
|
const isGameEnded = game?.mancalaGame.state == "ended";
|
||||||
const wonPlayer = game.mancalaGame.getWonPlayerId();
|
if (isGameEnded) {
|
||||||
let whoWon =
|
const wonPlayerID = game.mancalaGame.getWonPlayerId();
|
||||||
game.mancalaGame.getWonPlayerId() === userKey
|
const wonPlayer = wonPlayerID == whitePlayer.id ? whitePlayer : blackPlayer;
|
||||||
|
let whoWon;
|
||||||
|
if (wonPlayer) {
|
||||||
|
whoWon = isSpectator ? `${wonPlayer.name} ${context.texts.Won}` :
|
||||||
|
game.mancalaGame.getWonPlayerId() === currentUser.id
|
||||||
? context.texts.YouWon
|
? context.texts.YouWon
|
||||||
: context.texts.YouLost;
|
: context.texts.YouLost;
|
||||||
if (!wonPlayer) {
|
} else {
|
||||||
whoWon = context.texts.GameDraw;
|
whoWon = context.texts.GameDraw;
|
||||||
}
|
}
|
||||||
return context.texts.GameEnded + " " + whoWon;
|
return context.texts.GameEnded + " " + whoWon;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
if (game) {
|
if (game) {
|
||||||
return userKey ? game.mancalaGame.checkIsPlayerTurn(userKey)
|
const playingPlayer = game.mancalaGame.checkIsPlayerTurn(whitePlayer.id) ? whitePlayer : blackPlayer;
|
||||||
|
return isSpectator ? `${playingPlayer.name} ${context.texts.Playing}` : game.mancalaGame.checkIsPlayerTurn(currentUser.id)
|
||||||
? context.texts.YourTurn
|
? context.texts.YourTurn
|
||||||
: context.texts.OpponentTurn : undefined;
|
: context.texts.OpponentTurn;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InfoPanel: FunctionComponent<{
|
const InfoPanel: FunctionComponent<{
|
||||||
context: Context;
|
context: Context;
|
||||||
game?: Game;
|
game?: Game;
|
||||||
crashMessage?: string;
|
currentUser: User;
|
||||||
userKey?: string;
|
whitePlayer: User;
|
||||||
userKeyWhoLeave?: string;
|
blackPlayer: User;
|
||||||
|
leftPlayer?: User;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
|
isSpectator?: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
context,
|
context,
|
||||||
game,
|
game,
|
||||||
crashMessage,
|
currentUser,
|
||||||
userKey,
|
whitePlayer,
|
||||||
userKeyWhoLeave,
|
blackPlayer,
|
||||||
|
leftPlayer,
|
||||||
style,
|
style,
|
||||||
visible
|
visible,
|
||||||
|
isSpectator
|
||||||
}) => {
|
}) => {
|
||||||
if (visible === false) return <></>;
|
if (visible === false) return <></>;
|
||||||
const isUserTurn = userKey ? game?.mancalaGame.checkIsPlayerTurn(userKey) : false;
|
const isUserTurn = currentUser.id ? game?.mancalaGame.checkIsPlayerTurn(currentUser.id) : false;
|
||||||
const containerColor = isUserTurn
|
const containerColor = isUserTurn
|
||||||
? context.themeManager.theme.playerTurnColor
|
? context.themeManager.theme.playerTurnColor
|
||||||
: context.themeManager.theme.boardColor;
|
: context.themeManager.theme.boardColor;
|
||||||
@ -77,9 +90,11 @@ const InfoPanel: FunctionComponent<{
|
|||||||
const text = getInfoPanelTextByGameState({
|
const text = getInfoPanelTextByGameState({
|
||||||
context,
|
context,
|
||||||
game,
|
game,
|
||||||
crashMessage,
|
currentUser,
|
||||||
userKey,
|
whitePlayer,
|
||||||
userKeyWhoLeave
|
blackPlayer,
|
||||||
|
leftPlayer,
|
||||||
|
isSpectator
|
||||||
});
|
});
|
||||||
if (text) {
|
if (text) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -39,8 +39,6 @@ const GamePage: FunctionComponent<{
|
|||||||
|
|
||||||
const [game, setGame] = useState<Game | undefined>(undefined);
|
const [game, setGame] = useState<Game | undefined>(undefined);
|
||||||
|
|
||||||
const [crashMessage, setCrashMessage] = useState<string | undefined>(undefined);
|
|
||||||
|
|
||||||
const [userKeyWhoLeave, setUserKeyWhoLeave] = useState<string | undefined>(undefined);
|
const [userKeyWhoLeave, setUserKeyWhoLeave] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
const [boardViewModel, setBoardViewModel] = useState<BoardViewModel | undefined>(undefined);
|
const [boardViewModel, setBoardViewModel] = useState<BoardViewModel | undefined>(undefined);
|
||||||
@ -93,7 +91,6 @@ const GamePage: FunctionComponent<{
|
|||||||
const newCrashMessage = message as string;
|
const newCrashMessage = message as string;
|
||||||
console.error("on_game_crash");
|
console.error("on_game_crash");
|
||||||
console.error(newCrashMessage);
|
console.error(newCrashMessage);
|
||||||
setCrashMessage(newCrashMessage);
|
|
||||||
}
|
}
|
||||||
const onGameUserLeave = (message: any) => {
|
const onGameUserLeave = (message: any) => {
|
||||||
const userKeyWhoLeave = message;
|
const userKeyWhoLeave = message;
|
||||||
@ -107,17 +104,17 @@ const GamePage: FunctionComponent<{
|
|||||||
|
|
||||||
const listenMessages = (game: Game, pitAnimator: PitAnimator): () => void => {
|
const listenMessages = (game: Game, pitAnimator: PitAnimator): () => void => {
|
||||||
const _onGameUpdate = (message: object) => onGameUpdateEvent(pitAnimator, message);
|
const _onGameUpdate = (message: object) => onGameUpdateEvent(pitAnimator, message);
|
||||||
context.rtmt.listenMessage(channel_on_game_update, _onGameUpdate);
|
context.rtmt.addMessageListener(channel_on_game_update, _onGameUpdate);
|
||||||
context.rtmt.listenMessage(channel_on_game_crashed, onGameCrashed);
|
context.rtmt.addMessageListener(channel_on_game_crashed, onGameCrashed);
|
||||||
context.rtmt.listenMessage(channel_on_game_user_leave, onGameUserLeave);
|
context.rtmt.addMessageListener(channel_on_game_user_leave, onGameUserLeave);
|
||||||
context.rtmt.listenMessage(channel_on_user_connection_change, onUserConnectionChange);
|
context.rtmt.addMessageListener(channel_on_user_connection_change, onUserConnectionChange);
|
||||||
checkIsSpectator(game) && userKey && context.rtmt.sendMessage(channel_listen_game_events, game.id);
|
checkIsSpectator(game) && userKey && context.rtmt.sendMessage(channel_listen_game_events, game.id);
|
||||||
return () => {
|
return () => {
|
||||||
checkIsSpectator(game) && userKey && context.rtmt.sendMessage(channel_unlisten_game_events, game.id);
|
checkIsSpectator(game) && userKey && context.rtmt.sendMessage(channel_unlisten_game_events, game.id);
|
||||||
context.rtmt.unlistenMessage(channel_on_game_update, _onGameUpdate);
|
context.rtmt.removeMessageListener(channel_on_game_update, _onGameUpdate);
|
||||||
context.rtmt.unlistenMessage(channel_on_game_crashed, onGameCrashed);
|
context.rtmt.removeMessageListener(channel_on_game_crashed, onGameCrashed);
|
||||||
context.rtmt.unlistenMessage(channel_on_game_user_leave, onGameUserLeave);
|
context.rtmt.removeMessageListener(channel_on_game_user_leave, onGameUserLeave);
|
||||||
context.rtmt.unlistenMessage(channel_on_user_connection_change, onUserConnectionChange);
|
context.rtmt.removeMessageListener(channel_on_user_connection_change, onUserConnectionChange);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -198,17 +195,53 @@ const GamePage: FunctionComponent<{
|
|||||||
context.themeManager.theme.textColor,
|
context.themeManager.theme.textColor,
|
||||||
context.themeManager.theme.textLightColor
|
context.themeManager.theme.textLightColor
|
||||||
);
|
);
|
||||||
const renderNewGameBtn = userKeyWhoLeave || !game || (game && game.mancalaGame.state == "ended");
|
|
||||||
const showBoardView = game && boardViewModel && userKey && true;
|
|
||||||
const opponentId = getOpponentId();
|
|
||||||
const opponentUser = { id: getOpponentId() || "0", name: "Anonymous", isOnline: opponentId ? isUserOnline(opponentId) : false, isAnonymous: true };
|
|
||||||
const user = { id: userKey || "1", name: "Anonymous", isOnline: connectionState === "connected", isAnonymous: true };
|
|
||||||
|
|
||||||
const isMobile = width < 600;
|
const isMobile = width < 600;
|
||||||
|
|
||||||
|
const renderNewGameBtn = isSpectator || (userKeyWhoLeave || !game || (game && game.mancalaGame.state == "ended"));
|
||||||
|
const showBoardView = game && boardViewModel && userKey && true;
|
||||||
|
const topLocatedUserId = (isSpectator ? mancalaGame?.player2Id : getOpponentId()) || "0";
|
||||||
|
const bottomLocatedUserId = (isSpectator ? mancalaGame?.player1Id : userKey) || "1";
|
||||||
|
const topLocatedUser = {
|
||||||
|
id: topLocatedUserId,
|
||||||
|
name: "Anonymous",
|
||||||
|
isOnline: isUserOnline(topLocatedUserId),
|
||||||
|
isAnonymous: true
|
||||||
|
};
|
||||||
|
const bottomLocatedUser = {
|
||||||
|
id: bottomLocatedUserId,
|
||||||
|
name: "Anonymous",
|
||||||
|
isOnline: isSpectator ? isUserOnline(bottomLocatedUserId) : connectionState === "connected",
|
||||||
|
isAnonymous: true
|
||||||
|
};
|
||||||
|
const currentUser = isSpectator ? {
|
||||||
|
id: "2",
|
||||||
|
name: "Anonymous",
|
||||||
|
isOnline: connectionState === "connected",
|
||||||
|
isAnonymous: true
|
||||||
|
} : bottomLocatedUser;
|
||||||
|
const leftPlayer = userKeyWhoLeave ? (userKeyWhoLeave === topLocatedUser.id ? topLocatedUser : bottomLocatedUser) : undefined;
|
||||||
return (
|
return (
|
||||||
<PageContainer theme={theme!}>
|
<PageContainer theme={theme!}>
|
||||||
<HeaderBar color={theme?.appBarBgColor}>
|
{renderHeaderBar()}
|
||||||
|
{renderMobileBoardToolbar()}
|
||||||
|
{buildBoardTopToolbar()}
|
||||||
|
{showBoardView && (
|
||||||
|
<BoardView
|
||||||
|
game={game}
|
||||||
|
boardId={boardId}
|
||||||
|
boardViewModel={boardViewModel}
|
||||||
|
context={context}
|
||||||
|
onPitSelect={onPitSelect}
|
||||||
|
revert={isPlayer2} />
|
||||||
|
)}
|
||||||
|
<Center>
|
||||||
|
<LoadingComponent context={context} loadingState={gameLoadingState}></LoadingComponent>
|
||||||
|
</Center>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
function renderHeaderBar() {
|
||||||
|
return <HeaderBar color={theme?.appBarBgColor}>
|
||||||
<Row>
|
<Row>
|
||||||
<Link style={{ textDecoration: 'none' }} to={"/"}>
|
<Link style={{ textDecoration: 'none' }} to={"/"}>
|
||||||
<HeaderbarIcon />
|
<HeaderbarIcon />
|
||||||
@ -225,50 +258,44 @@ const GamePage: FunctionComponent<{
|
|||||||
text={renderNewGameBtn ? context.texts.NewGame : context.texts.Leave}
|
text={renderNewGameBtn ? context.texts.NewGame : context.texts.Leave}
|
||||||
onClick={renderNewGameBtn ? onNewGameClick : onLeaveGameClick} />
|
onClick={renderNewGameBtn ? onNewGameClick : onLeaveGameClick} />
|
||||||
</Row>
|
</Row>
|
||||||
</HeaderBar>
|
</HeaderBar>;
|
||||||
<BoardToolbar style={{ justifyContent: "center" }} visible={showBoardView && isMobile || false}>
|
}
|
||||||
|
|
||||||
|
function renderMobileBoardToolbar() {
|
||||||
|
return <BoardToolbar style={{ justifyContent: "center" }} visible={showBoardView && isMobile || false}>
|
||||||
|
{buildInfoPanel()}
|
||||||
|
</BoardToolbar>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBoardTopToolbar() {
|
||||||
|
return <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={topLocatedUser} visible={showBoardView || false} />
|
||||||
|
{buildInfoPanel()}
|
||||||
|
<UserStatus style={{
|
||||||
|
marginBottom: "0.5rem", marginRight: "6%", maxWidth: isMobile ? "40vw" : "30vw",
|
||||||
|
width: isMobile ? "40vw" : "30vw"
|
||||||
|
}} context={context} layoutMode="right" user={bottomLocatedUser} visible={showBoardView || false} />
|
||||||
|
</BoardToolbar>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildInfoPanel() {
|
||||||
|
return (
|
||||||
<InfoPanel
|
<InfoPanel
|
||||||
style={{ marginTop: "0.5rem", marginBottom: "0.5rem" }}
|
style={{ marginTop: "0.5rem", marginBottom: "0.5rem" }}
|
||||||
context={context}
|
context={context}
|
||||||
game={game}
|
game={game}
|
||||||
crashMessage={crashMessage}
|
currentUser={currentUser}
|
||||||
userKey={userKey}
|
whitePlayer={topLocatedUser}
|
||||||
userKeyWhoLeave={userKeyWhoLeave} />
|
blackPlayer={bottomLocatedUser}
|
||||||
</BoardToolbar>
|
leftPlayer={leftPlayer}
|
||||||
<BoardToolbar style={{ alignItems: "flex-end" }} visible={showBoardView || false}>
|
visible={!isMobile}
|
||||||
<UserStatus style={{
|
isSpectator={isSpectator} />
|
||||||
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 && (
|
|
||||||
<BoardView
|
|
||||||
game={game}
|
|
||||||
boardId={boardId}
|
|
||||||
boardViewModel={boardViewModel}
|
|
||||||
context={context}
|
|
||||||
onPitSelect={onPitSelect}
|
|
||||||
revert={isPlayer2} />
|
|
||||||
)}
|
|
||||||
<Center>
|
|
||||||
<LoadingComponent context={context} loadingState={gameLoadingState}></LoadingComponent>
|
|
||||||
</Center>
|
|
||||||
</PageContainer>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default GamePage;
|
export default GamePage;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user