diff options
Diffstat (limited to 'frontend/app/routes')
| -rw-r--r-- | frontend/app/routes/dashboard.tsx | 19 | ||||
| -rw-r--r-- | frontend/app/routes/golf.$gameId.play.tsx | 110 | ||||
| -rw-r--r-- | frontend/app/routes/golf.$gameId.watch.tsx | 185 |
3 files changed, 67 insertions, 247 deletions
diff --git a/frontend/app/routes/dashboard.tsx b/frontend/app/routes/dashboard.tsx index cf5453c..08461a5 100644 --- a/frontend/app/routes/dashboard.tsx +++ b/frontend/app/routes/dashboard.tsx @@ -1,7 +1,7 @@ import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; import { Form, useLoaderData } from "@remix-run/react"; -import { apiGetGames } from "../.server/api/client"; import { ensureUserLoggedIn } from "../.server/auth"; +import { apiGetGames } from "../api/client"; import BorderedContainer from "../components/BorderedContainer"; import NavigateLink from "../components/NavigateLink"; import UserIcon from "../components/UserIcon"; @@ -39,7 +39,7 @@ export default function Dashboard() { <BorderedContainer> <div className="px-4"> {games.length === 0 ? ( - <p>エントリーしている試合はありません</p> + <p>エントリーできる試合はありません</p> ) : ( <ul className="divide-y"> {games.map((game) => ( @@ -58,15 +58,12 @@ export default function Dashboard() { </span> </div> <span> - {game.state === "closed" || game.state === "finished" ? ( - <span className="text-lg text-gray-400 bg-gray-200 px-4 py-2 rounded"> - 入室 - </span> - ) : ( - <NavigateLink to={`/golf/${game.game_id}/play`}> - 入室 - </NavigateLink> - )} + <NavigateLink to={`/golf/${game.game_id}/play`}> + 対戦 + </NavigateLink> + <NavigateLink to={`/golf/${game.game_id}/watch`}> + 観戦 + </NavigateLink> </span> </li> ))} diff --git a/frontend/app/routes/golf.$gameId.play.tsx b/frontend/app/routes/golf.$gameId.play.tsx index 91a2b8c..e523187 100644 --- a/frontend/app/routes/golf.$gameId.play.tsx +++ b/frontend/app/routes/golf.$gameId.play.tsx @@ -1,17 +1,19 @@ import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; -import { ClientLoaderFunctionArgs, useLoaderData } from "@remix-run/react"; +import { useLoaderData } from "@remix-run/react"; import { useHydrateAtoms } from "jotai/utils"; -import { apiGetGame, apiGetToken } from "../.server/api/client"; import { ensureUserLoggedIn } from "../.server/auth"; -import GolfPlayApp from "../components/GolfPlayApp.client"; -import GolfPlayAppConnecting from "../components/GolfPlayApps/GolfPlayAppConnecting"; import { - scoreAtom, + ApiAuthTokenContext, + apiGetGame, + apiGetGamePlayLatestState, +} from "../api/client"; +import GolfPlayApp from "../components/GolfPlayApp"; +import { setCurrentTimestampAtom, setDurationSecondsAtom, - submitResultAtom, + setGameStartedAtAtom, + setLatestGameStateAtom, } from "../states/play"; -import { PlayerState } from "../types/PlayerState"; export const meta: MetaFunction<typeof loader> = ({ data }) => [ { @@ -24,105 +26,39 @@ export const meta: MetaFunction<typeof loader> = ({ data }) => [ export async function loader({ params, request }: LoaderFunctionArgs) { const { token, user } = await ensureUserLoggedIn(request); + const gameId = Number(params.gameId); + const fetchGame = async () => { - return (await apiGetGame(token, Number(params.gameId))).game; + return (await apiGetGame(token, gameId)).game; }; - const fetchSockToken = async () => { - return (await apiGetToken(token)).token; + const fetchGameState = async () => { + return (await apiGetGamePlayLatestState(token, gameId)).state; }; - const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]); - - const playerState: PlayerState = { - code: "", - score: null, - submitResult: { - status: "waiting_submission", - execResults: game.exec_steps.map((r) => ({ - testcase_id: r.testcase_id, - status: "waiting_submission", - label: r.label, - stdout: "", - stderr: "", - })), - }, - }; + const [game, state] = await Promise.all([fetchGame(), fetchGameState()]); return { + apiAuthToken: token, game, player: user, - sockToken, - playerState, + gameState: state, }; } -export async function clientLoader({ serverLoader }: ClientLoaderFunctionArgs) { - const data = await serverLoader<typeof loader>(); - const baseKey = `playerState:${data.game.game_id}:${data.player.user_id}`; - - const localCode = (() => { - const rawValue = window.localStorage.getItem(`${baseKey}:code`); - if (rawValue === null) { - return null; - } - return rawValue; - })(); - - const localScore = (() => { - const rawValue = window.localStorage.getItem(`${baseKey}:score`); - if (rawValue === null || rawValue === "") { - return null; - } - return Number(rawValue); - })(); - - const localSubmissionResult = (() => { - const rawValue = window.localStorage.getItem(`${baseKey}:submissionResult`); - if (rawValue === null) { - return null; - } - const parsed = JSON.parse(rawValue); - if (typeof parsed !== "object") { - return null; - } - return parsed; - })(); - - if (localCode !== null) { - data.playerState.code = localCode; - } - if (localScore !== null) { - data.playerState.score = localScore; - } - if (localSubmissionResult !== null) { - data.playerState.submitResult = localSubmissionResult; - } - - return data; -} -clientLoader.hydrate = true; - -export function HydrateFallback() { - return <GolfPlayAppConnecting />; -} - export default function GolfPlay() { - const { game, player, sockToken, playerState } = + const { apiAuthToken, game, player, gameState } = useLoaderData<typeof loader>(); useHydrateAtoms([ [setCurrentTimestampAtom, undefined], [setDurationSecondsAtom, game.duration_seconds], - [scoreAtom, playerState.score], - [submitResultAtom, playerState.submitResult], + [setGameStartedAtAtom, game.started_at ?? null], + [setLatestGameStateAtom, gameState], ]); return ( - <GolfPlayApp - game={game} - player={player} - initialCode={playerState.code} - sockToken={sockToken} - /> + <ApiAuthTokenContext.Provider value={apiAuthToken}> + <GolfPlayApp game={game} player={player} initialCode={gameState.code} /> + </ApiAuthTokenContext.Provider> ); } diff --git a/frontend/app/routes/golf.$gameId.watch.tsx b/frontend/app/routes/golf.$gameId.watch.tsx index 5a41de5..0c07633 100644 --- a/frontend/app/routes/golf.$gameId.watch.tsx +++ b/frontend/app/routes/golf.$gameId.watch.tsx @@ -1,21 +1,21 @@ import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; -import { ClientLoaderFunctionArgs, useLoaderData } from "@remix-run/react"; +import { useLoaderData } from "@remix-run/react"; import { useHydrateAtoms } from "jotai/utils"; -import { apiGetGame, apiGetToken } from "../.server/api/client"; import { ensureUserLoggedIn } from "../.server/auth"; -import GolfWatchApp from "../components/GolfWatchApp.client"; -import GolfWatchAppConnecting from "../components/GolfWatchApps/GolfWatchAppConnecting"; import { - codeAAtom, - codeBAtom, - scoreAAtom, - scoreBAtom, + ApiAuthTokenContext, + apiGetGame, + apiGetGameWatchLatestStates, + apiGetGameWatchRanking, +} from "../api/client"; +import GolfWatchApp from "../components/GolfWatchApp"; +import { setCurrentTimestampAtom, setDurationSecondsAtom, - submitResultAAtom, - submitResultBAtom, + setGameStartedAtAtom, + setLatestGameStatesAtom, + setRankingAtom, } from "../states/watch"; -import { PlayerState } from "../types/PlayerState"; export const meta: MetaFunction<typeof loader> = ({ data }) => [ { @@ -28,160 +28,47 @@ export const meta: MetaFunction<typeof loader> = ({ data }) => [ export async function loader({ params, request }: LoaderFunctionArgs) { const { token } = await ensureUserLoggedIn(request); + const gameId = Number(params.gameId); + const fetchGame = async () => { - return (await apiGetGame(token, Number(params.gameId))).game; + return (await apiGetGame(token, gameId)).game; }; - const fetchSockToken = async () => { - return (await apiGetToken(token)).token; + const fetchRanking = async () => { + return (await apiGetGameWatchRanking(token, gameId)).ranking; }; - - const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]); - - if (game.game_type !== "1v1") { - throw new Response("Not Found", { status: 404 }); - } - - const playerStateA: PlayerState = { - code: "", - score: null, - submitResult: { - status: "waiting_submission", - execResults: game.exec_steps.map((r) => ({ - testcase_id: r.testcase_id, - status: "waiting_submission", - label: r.label, - stdout: "", - stderr: "", - })), - }, + const fetchGameStates = async () => { + return (await apiGetGameWatchLatestStates(token, gameId)).states; }; - const playerStateB = structuredClone(playerStateA); + + const [game, ranking, gameStates] = await Promise.all([ + fetchGame(), + fetchRanking(), + fetchGameStates(), + ]); return { + apiAuthToken: token, game, - sockToken, - playerStateA, - playerStateB, + ranking, + gameStates, }; } -export async function clientLoader({ serverLoader }: ClientLoaderFunctionArgs) { - const data = await serverLoader<typeof loader>(); - - const playerIdA = data.game.players[0]?.user_id; - const playerIdB = data.game.players[1]?.user_id; - - if (playerIdA !== null) { - const baseKeyA = `watcherState:${data.game.game_id}:${playerIdA}`; - - const localCodeA = (() => { - const rawValue = window.localStorage.getItem(`${baseKeyA}:code`); - - if (rawValue === null) { - return null; - } - return rawValue; - })(); - - const localScoreA = (() => { - const rawValue = window.localStorage.getItem(`${baseKeyA}:score`); - if (rawValue === null || rawValue === "") { - return null; - } - return Number(rawValue); - })(); - - const localSubmissionResultA = (() => { - const rawValue = window.localStorage.getItem( - `${baseKeyA}:submissionResult`, - ); - if (rawValue === null) { - return null; - } - const parsed = JSON.parse(rawValue); - if (typeof parsed !== "object") { - return null; - } - return parsed; - })(); - - if (localCodeA !== null) { - data.playerStateA.code = localCodeA; - } - if (localScoreA !== null) { - data.playerStateA.score = localScoreA; - } - if (localSubmissionResultA !== null) { - data.playerStateA.submitResult = localSubmissionResultA; - } - } - - if (playerIdB !== null) { - const baseKeyB = `watcherState:${data.game.game_id}:${playerIdB}`; - - const localCodeB = (() => { - const rawValue = window.localStorage.getItem(`${baseKeyB}:code`); - if (rawValue === null) { - return null; - } - return rawValue; - })(); - - const localScoreB = (() => { - const rawValue = window.localStorage.getItem(`${baseKeyB}:score`); - if (rawValue === null || rawValue === "") { - return null; - } - return Number(rawValue); - })(); - - const localSubmissionResultB = (() => { - const rawValue = window.localStorage.getItem( - `${baseKeyB}:submissionResult`, - ); - if (rawValue === null) { - return null; - } - const parsed = JSON.parse(rawValue); - if (typeof parsed !== "object") { - return null; - } - return parsed; - })(); - - if (localCodeB !== null) { - data.playerStateB.code = localCodeB; - } - if (localScoreB !== null) { - data.playerStateB.score = localScoreB; - } - if (localSubmissionResultB !== null) { - data.playerStateB.submitResult = localSubmissionResultB; - } - } - - return data; -} -clientLoader.hydrate = true; - -export function HydrateFallback() { - return <GolfWatchAppConnecting />; -} - export default function GolfWatch() { - const { game, sockToken, playerStateA, playerStateB } = + const { apiAuthToken, game, ranking, gameStates } = useLoaderData<typeof loader>(); useHydrateAtoms([ [setCurrentTimestampAtom, undefined], [setDurationSecondsAtom, game.duration_seconds], - [codeAAtom, playerStateA.code], - [codeBAtom, playerStateB.code], - [scoreAAtom, playerStateA.score], - [scoreBAtom, playerStateB.score], - [submitResultAAtom, playerStateA.submitResult], - [submitResultBAtom, playerStateB.submitResult], + [setGameStartedAtAtom, game.started_at ?? null], + [setRankingAtom, ranking], + [setLatestGameStatesAtom, gameStates], ]); - return <GolfWatchApp game={game} sockToken={sockToken} />; + return ( + <ApiAuthTokenContext.Provider value={apiAuthToken}> + <GolfWatchApp game={game} /> + </ApiAuthTokenContext.Provider> + ); } |
