90 lines
3.8 KiB
TypeScript
90 lines
3.8 KiB
TypeScript
|
|
import * as React from "react";
|
||
|
|
import { FunctionComponent, useEffect, useState } from "react";
|
||
|
|
import {
|
||
|
|
channel_on_live_games,
|
||
|
|
channel_subscribe_live_games,
|
||
|
|
channel_unsubscribe_live_games,
|
||
|
|
LiveGameSummary,
|
||
|
|
} from "@mancala/core";
|
||
|
|
import { useTranslation } from "react-i18next";
|
||
|
|
import { useNavigate } from "react-router-dom";
|
||
|
|
import { Context } from "../../context/context";
|
||
|
|
import { PopularGames } from "../../store/GameStore";
|
||
|
|
import { formatDate, getResultText, getScoreText } from "../../util/GameResultText";
|
||
|
|
import GamePreviewCard from "./GamePreviewCard";
|
||
|
|
import HomeSection from "./HomeSection";
|
||
|
|
|
||
|
|
const GAME_COUNT = 5;
|
||
|
|
|
||
|
|
// Live games, updated as they are played; when nothing is live, the most watched finished games.
|
||
|
|
const LiveGamesSection: FunctionComponent<{ context: Context }> = ({ context }) => {
|
||
|
|
const { t, i18n } = useTranslation();
|
||
|
|
const navigate = useNavigate();
|
||
|
|
const [popular, setPopular] = useState<PopularGames | undefined>(undefined);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
let disposed = false;
|
||
|
|
const loadPopular = () => context.gameStore.getPopularGames(GAME_COUNT)
|
||
|
|
.then((games) => !disposed && setPopular(games))
|
||
|
|
.catch((error) => console.error("(Home) Could not load popular games", error));
|
||
|
|
const onLiveGames = (message: { games: LiveGameSummary[] }) => {
|
||
|
|
if (message.games.length > 0) setPopular({ kind: "live", games: message.games });
|
||
|
|
else loadPopular();
|
||
|
|
};
|
||
|
|
// Subscribe whenever the connection is (re)established; the server forgets subscriptions of offline users.
|
||
|
|
const subscribe = () => context.rtmt.sendMessage(channel_subscribe_live_games, {});
|
||
|
|
context.rtmt.addMessageListener(channel_on_live_games, onLiveGames);
|
||
|
|
context.rtmt.on("open", subscribe);
|
||
|
|
if (context.rtmt.connectionState === "connected") subscribe();
|
||
|
|
loadPopular();
|
||
|
|
return () => {
|
||
|
|
disposed = true;
|
||
|
|
context.rtmt.off("open", subscribe);
|
||
|
|
context.rtmt.removeMessageListener(channel_on_live_games, onLiveGames);
|
||
|
|
if (context.rtmt.connectionState === "connected") context.rtmt.sendMessage(channel_unsubscribe_live_games, {});
|
||
|
|
};
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
if (!popular) return <></>;
|
||
|
|
const openGame = (id: string) => navigate(`/game/${id}`);
|
||
|
|
|
||
|
|
if (popular.kind === "live") {
|
||
|
|
return (
|
||
|
|
<HomeSection context={context} title={t("LiveGames")}>
|
||
|
|
{popular.games.map((game) => (
|
||
|
|
<GamePreviewCard
|
||
|
|
key={game.id}
|
||
|
|
context={context}
|
||
|
|
id={game.id}
|
||
|
|
player1={game.player1}
|
||
|
|
player2={game.player2}
|
||
|
|
board={game.board}
|
||
|
|
turnPlayerId={game.turnPlayerId}
|
||
|
|
lines={[t("Watching", { count: game.spectatorCount }) + " · " + t("MoveNumber", { number: game.moveCount })]}
|
||
|
|
onClick={() => openGame(game.id)} />
|
||
|
|
))}
|
||
|
|
</HomeSection>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return (
|
||
|
|
<HomeSection context={context} title={t("PopularGames")} emptyText={t("NoGamesYet")}>
|
||
|
|
{popular.games.map((game) => (
|
||
|
|
<GamePreviewCard
|
||
|
|
key={game.id}
|
||
|
|
context={context}
|
||
|
|
id={game.id}
|
||
|
|
player1={game.player1}
|
||
|
|
player2={game.player2}
|
||
|
|
board={game.board}
|
||
|
|
lines={[
|
||
|
|
`${getResultText(game, undefined, t)} · ${getScoreText(game, undefined)}`,
|
||
|
|
`${formatDate(game.endedAt, i18n.language)} · ${t("Watching", { count: game.peakSpectators })}`,
|
||
|
|
]}
|
||
|
|
onClick={() => openGame(game.id)} />
|
||
|
|
))}
|
||
|
|
</HomeSection>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default LiveGamesSection;
|