diff options
Diffstat (limited to 'frontend')
| -rw-r--r-- | frontend/app/.server/api/schema.d.ts | 22 | ||||
| -rw-r--r-- | frontend/app/components/GolfPlayApp.client.tsx | 82 | ||||
| -rw-r--r-- | frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx | 13 | ||||
| -rw-r--r-- | frontend/app/components/GolfWatchApp.client.tsx | 8 |
4 files changed, 97 insertions, 28 deletions
diff --git a/frontend/app/.server/api/schema.d.ts b/frontend/app/.server/api/schema.d.ts index 5b37081..7fd612e 100644 --- a/frontend/app/.server/api/schema.d.ts +++ b/frontend/app/.server/api/schema.d.ts @@ -130,7 +130,7 @@ export interface components { description: string; }; GamePlayerMessage: components["schemas"]["GamePlayerMessageS2C"] | components["schemas"]["GamePlayerMessageC2S"]; - GamePlayerMessageS2C: components["schemas"]["GamePlayerMessageS2CStart"] | components["schemas"]["GamePlayerMessageS2CExecResult"]; + GamePlayerMessageS2C: components["schemas"]["GamePlayerMessageS2CStart"] | components["schemas"]["GamePlayerMessageS2CExecResult"] | components["schemas"]["GamePlayerMessageS2CSubmitResult"]; GamePlayerMessageS2CStart: { /** @constant */ type: "player:s2c:start"; @@ -146,11 +146,29 @@ export interface components { data: components["schemas"]["GamePlayerMessageS2CExecResultPayload"]; }; GamePlayerMessageS2CExecResultPayload: { + /** @example 1 */ + testcase_id: number | null; /** * @example success * @enum {string} */ - status: "success" | "failure" | "timeout" | "internal_error" | "compile_error" | "wrong_answer"; + status: "success" | "wrong_answer" | "timeout" | "runtime_error" | "internal_error" | "compile_error"; + /** @example Hello, world! */ + stdout: string; + /** @example */ + stderr: string; + }; + GamePlayerMessageS2CSubmitResult: { + /** @constant */ + type: "player:s2c:submitresult"; + data: components["schemas"]["GamePlayerMessageS2CSubmitResultPayload"]; + }; + GamePlayerMessageS2CSubmitResultPayload: { + /** + * @example success + * @enum {string} + */ + status: "success" | "wrong_answer" | "timeout" | "runtime_error" | "internal_error" | "compile_error"; /** @example 100 */ score: number | null; }; diff --git a/frontend/app/components/GolfPlayApp.client.tsx b/frontend/app/components/GolfPlayApp.client.tsx index ef3a229..dbc8c1b 100644 --- a/frontend/app/components/GolfPlayApp.client.tsx +++ b/frontend/app/components/GolfPlayApp.client.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import { useDebouncedCallback } from "use-debounce"; import type { components } from "../.server/api/schema"; import useWebSocket, { ReadyState } from "../hooks/useWebSocket"; +import type { PlayerInfo } from "../models/PlayerInfo"; import GolfPlayAppConnecting from "./GolfPlayApps/GolfPlayAppConnecting"; import GolfPlayAppFinished from "./GolfPlayApps/GolfPlayAppFinished"; import GolfPlayAppGaming from "./GolfPlayApps/GolfPlayAppGaming"; @@ -73,9 +74,21 @@ export default function GolfPlayApp({ } }, [gameState, startedAt, game.duration_seconds]); - const [currentScore, setCurrentScore] = useState<number | null>(null); - - const [lastExecStatus, setLastExecStatus] = useState<string | null>(null); + const [playerInfo, setPlayerInfo] = useState<Omit<PlayerInfo, "code">>({ + displayName: player.display_name, + iconPath: player.icon_path ?? null, + 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 onCodeChange = useDebouncedCallback((code: string) => { console.log("player:c2s:code"); @@ -94,6 +107,18 @@ export default function GolfPlayApp({ type: "player:c2s:submit", data: { code }, }); + setPlayerInfo((prev) => ({ + ...prev, + submitResult: { + status: "running", + execResults: prev.submitResult.execResults.map((r) => ({ + ...r, + status: "running", + stdout: "", + stderr: "", + })), + }, + })); }, 1000); if (readyState === ReadyState.UNINSTANTIATED) { @@ -123,14 +148,46 @@ export default function GolfPlayApp({ setGameState("starting"); } } else if (lastJsonMessage.type === "player:s2c:execresult") { + const { testcase_id, status, stdout, stderr } = lastJsonMessage.data; + setPlayerInfo((prev) => { + const ret = { ...prev }; + ret.submitResult = { + ...prev.submitResult, + execResults: prev.submitResult.execResults.map((r) => + r.testcase_id === testcase_id && r.status === "running" + ? { + ...r, + status, + stdout, + stderr, + } + : r, + ), + }; + return ret; + }); + } else if (lastJsonMessage.type === "player:s2c:submitresult") { const { status, score } = lastJsonMessage.data; - if ( - score !== null && - (currentScore === null || score < currentScore) - ) { - setCurrentScore(score); - } - setLastExecStatus(status); + setPlayerInfo((prev) => { + const ret = { ...prev }; + ret.submitResult = { + ...prev.submitResult, + status, + }; + if (status === "success") { + if (score) { + if (ret.score === null || score < ret.score) { + ret.score = score; + } + } + } else { + ret.submitResult.execResults = prev.submitResult.execResults.map( + (r) => + r.status === "running" ? { ...r, status: "canceled" } : r, + ); + } + return ret; + }); } } else { if (game.started_at) { @@ -165,7 +222,6 @@ export default function GolfPlayApp({ lastJsonMessage, readyState, gameState, - currentScore, ]); if (gameState === "connecting") { @@ -178,13 +234,11 @@ export default function GolfPlayApp({ return ( <GolfPlayAppGaming gameDisplayName={game.display_name} - playerDisplayName={player.display_name} + playerInfo={playerInfo} problemTitle={game.problem.title} problemDescription={game.problem.description} onCodeChange={onCodeChange} onCodeSubmit={onCodeSubmit} - currentScore={currentScore} - lastExecStatus={lastExecStatus} /> ); } else if (gameState === "finished") { diff --git a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx index 03acf5a..08490a6 100644 --- a/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx +++ b/frontend/app/components/GolfPlayApps/GolfPlayAppGaming.tsx @@ -1,28 +1,25 @@ import { Link } from "@remix-run/react"; import React, { useRef } from "react"; import SubmitButton from "../../components/SubmitButton"; +import type { PlayerInfo } from "../../models/PlayerInfo"; import BorderedContainer from "../BorderedContainer"; type Props = { gameDisplayName: string; - playerDisplayName: string; + playerInfo: Omit<PlayerInfo, "code">; problemTitle: string; problemDescription: string; onCodeChange: (code: string) => void; onCodeSubmit: (code: string) => void; - currentScore: number | null; - lastExecStatus: string | null; }; export default function GolfPlayAppGaming({ gameDisplayName, - playerDisplayName, + playerInfo, problemTitle, problemDescription, onCodeChange, onCodeSubmit, - currentScore, - lastExecStatus, }: Props) { const textareaRef = useRef<HTMLTextAreaElement>(null); @@ -45,7 +42,7 @@ export default function GolfPlayAppGaming({ </div> <div> <Link to={"/dashboard"} className="font-bold text-xl"> - {playerDisplayName} + {playerInfo.displayName} </Link> </div> </div> @@ -69,7 +66,7 @@ export default function GolfPlayAppGaming({ <SubmitButton onClick={handleSubmitButtonClick}>提出</SubmitButton> <div className="mb-2 mt-auto"> <div className="font-semibold text-green-500"> - Score: {currentScore ?? "-"} ({lastExecStatus ?? "-"}) + Score: {playerInfo.score ?? "-"} </div> </div> </div> diff --git a/frontend/app/components/GolfWatchApp.client.tsx b/frontend/app/components/GolfWatchApp.client.tsx index 7f582c9..9d3f752 100644 --- a/frontend/app/components/GolfWatchApp.client.tsx +++ b/frontend/app/components/GolfWatchApp.client.tsx @@ -161,8 +161,8 @@ export default function GolfWatchApp({ setter((prev) => { const ret = { ...prev }; ret.submitResult = { - ...ret.submitResult, - execResults: ret.submitResult.execResults.map((r) => + ...prev.submitResult, + execResults: prev.submitResult.execResults.map((r) => r.testcase_id === testcase_id && r.status === "running" ? { ...r, @@ -182,7 +182,7 @@ export default function GolfWatchApp({ setter((prev) => { const ret = { ...prev }; ret.submitResult = { - ...ret.submitResult, + ...prev.submitResult, status, }; if (status === "success") { @@ -192,7 +192,7 @@ export default function GolfWatchApp({ } } } else { - ret.submitResult.execResults = ret.submitResult.execResults.map( + ret.submitResult.execResults = prev.submitResult.execResults.map( (r) => r.status === "running" ? { ...r, status: "canceled" } : r, ); |
