From 3a1e09991708bff1d0c0cfd5b1b091924cca9e8f Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 29 Jul 2024 22:15:03 +0900 Subject: fix(frontend): make GolfPlayApp and GolfWatchApp client-only components to fix SSR errors --- frontend/app/components/GolfPlayApp.client.tsx | 151 ++++++++++++++++++++++++ frontend/app/components/GolfPlayApp.tsx | 151 ------------------------ frontend/app/components/GolfWatchApp.client.tsx | 142 ++++++++++++++++++++++ frontend/app/components/GolfWatchApp.tsx | 142 ---------------------- 4 files changed, 293 insertions(+), 293 deletions(-) create mode 100644 frontend/app/components/GolfPlayApp.client.tsx delete mode 100644 frontend/app/components/GolfPlayApp.tsx create mode 100644 frontend/app/components/GolfWatchApp.client.tsx delete mode 100644 frontend/app/components/GolfWatchApp.tsx (limited to 'frontend/app/components') diff --git a/frontend/app/components/GolfPlayApp.client.tsx b/frontend/app/components/GolfPlayApp.client.tsx new file mode 100644 index 0000000..3cb512a --- /dev/null +++ b/frontend/app/components/GolfPlayApp.client.tsx @@ -0,0 +1,151 @@ +import type { components } from "../.server/api/schema"; +import { useState, useEffect } from "react"; +import useWebSocket, { ReadyState } from "react-use-websocket"; +import { useDebouncedCallback } from "use-debounce"; +import GolfPlayAppConnecting from "./GolfPlayApps/GolfPlayAppConnecting"; +import GolfPlayAppWaiting from "./GolfPlayApps/GolfPlayAppWaiting"; +import GolfPlayAppStarting from "./GolfPlayApps/GolfPlayAppStarting"; +import GolfPlayAppGaming from "./GolfPlayApps/GolfPlayAppGaming"; +import GolfPlayAppFinished from "./GolfPlayApps/GolfPlayAppFinished"; + +type WebSocketMessage = components["schemas"]["GamePlayerMessageS2C"]; + +type Game = components["schemas"]["Game"]; +type Problem = components["schemas"]["Problem"]; + +type GameState = "connecting" | "waiting" | "starting" | "gaming" | "finished"; + +export default function GolfPlayApp({ + game, + sockToken, +}: { + game: Game; + sockToken: string; +}) { + // const socketUrl = `wss://t.nil.ninja/iosdc/2024/sock/golf/${game.game_id}/play?token=${sockToken}`; + const socketUrl = + process.env.NODE_ENV === "development" + ? `ws://localhost:8002/sock/golf/${game.game_id}/play?token=${sockToken}` + : `ws://api-server/sock/golf/${game.game_id}/play?token=${sockToken}`; + + const { sendJsonMessage, lastJsonMessage, readyState } = + useWebSocket(socketUrl, {}); + + const [gameState, setGameState] = useState("connecting"); + + const [problem, setProblem] = useState(null); + + const [startedAt, setStartedAt] = useState(null); + + const [timeLeftSeconds, setTimeLeftSeconds] = useState(null); + + useEffect(() => { + if (gameState === "starting" && startedAt !== null) { + const timer1 = setInterval(() => { + setTimeLeftSeconds((prev) => { + if (prev === null) { + return null; + } + if (prev <= 1) { + clearInterval(timer1); + setGameState("gaming"); + return 0; + } + return prev - 1; + }); + }, 1000); + + const timer2 = setInterval(() => { + const nowSec = Math.floor(Date.now() / 1000); + const finishedAt = startedAt + game.duration_seconds; + if (nowSec >= finishedAt) { + clearInterval(timer2); + setGameState("finished"); + } + }, 1000); + + return () => { + clearInterval(timer1); + clearInterval(timer2); + }; + } + }, [gameState, startedAt, game.duration_seconds]); + + const [currentScore, setCurrentScore] = useState(null); + + const onCodeChange = useDebouncedCallback((code: string) => { + console.log("player:c2s:code"); + sendJsonMessage({ + type: "player:c2s:code", + data: { code }, + }); + }, 1000); + + if (readyState === ReadyState.UNINSTANTIATED) { + throw new Error("WebSocket is not connected"); + } + + useEffect(() => { + if (readyState === ReadyState.CLOSING || readyState === ReadyState.CLOSED) { + if (gameState !== "finished") { + setGameState("connecting"); + } + } else if (readyState === ReadyState.CONNECTING) { + setGameState("connecting"); + } else if (readyState === ReadyState.OPEN) { + if (lastJsonMessage !== null) { + console.log(lastJsonMessage.type); + if (lastJsonMessage.type === "player:s2c:prepare") { + const { problem } = lastJsonMessage.data; + setProblem(problem); + console.log("player:c2s:ready"); + sendJsonMessage({ type: "player:c2s:ready" }); + } else if (lastJsonMessage.type === "player:s2c:start") { + if ( + gameState !== "starting" && + gameState !== "gaming" && + gameState !== "finished" + ) { + const { start_at } = lastJsonMessage.data; + setStartedAt(start_at); + const nowSec = Math.floor(Date.now() / 1000); + setTimeLeftSeconds(start_at - nowSec); + setGameState("starting"); + } + } else if (lastJsonMessage.type === "player:s2c:execresult") { + const { score } = lastJsonMessage.data; + if ( + score !== null && + (currentScore === null || score < currentScore) + ) { + setCurrentScore(score); + } + } + } else { + setGameState("waiting"); + console.log("player:c2s:entry"); + sendJsonMessage({ type: "player:c2s:entry" }); + } + } + }, [sendJsonMessage, lastJsonMessage, readyState, gameState, currentScore]); + + if (gameState === "connecting") { + return ; + } else if (gameState === "waiting") { + return ; + } else if (gameState === "starting") { + return ; + } else if (gameState === "gaming") { + return ( + + ); + } else if (gameState === "finished") { + return ; + } else { + return null; + } +} diff --git a/frontend/app/components/GolfPlayApp.tsx b/frontend/app/components/GolfPlayApp.tsx deleted file mode 100644 index 3cb512a..0000000 --- a/frontend/app/components/GolfPlayApp.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import type { components } from "../.server/api/schema"; -import { useState, useEffect } from "react"; -import useWebSocket, { ReadyState } from "react-use-websocket"; -import { useDebouncedCallback } from "use-debounce"; -import GolfPlayAppConnecting from "./GolfPlayApps/GolfPlayAppConnecting"; -import GolfPlayAppWaiting from "./GolfPlayApps/GolfPlayAppWaiting"; -import GolfPlayAppStarting from "./GolfPlayApps/GolfPlayAppStarting"; -import GolfPlayAppGaming from "./GolfPlayApps/GolfPlayAppGaming"; -import GolfPlayAppFinished from "./GolfPlayApps/GolfPlayAppFinished"; - -type WebSocketMessage = components["schemas"]["GamePlayerMessageS2C"]; - -type Game = components["schemas"]["Game"]; -type Problem = components["schemas"]["Problem"]; - -type GameState = "connecting" | "waiting" | "starting" | "gaming" | "finished"; - -export default function GolfPlayApp({ - game, - sockToken, -}: { - game: Game; - sockToken: string; -}) { - // const socketUrl = `wss://t.nil.ninja/iosdc/2024/sock/golf/${game.game_id}/play?token=${sockToken}`; - const socketUrl = - process.env.NODE_ENV === "development" - ? `ws://localhost:8002/sock/golf/${game.game_id}/play?token=${sockToken}` - : `ws://api-server/sock/golf/${game.game_id}/play?token=${sockToken}`; - - const { sendJsonMessage, lastJsonMessage, readyState } = - useWebSocket(socketUrl, {}); - - const [gameState, setGameState] = useState("connecting"); - - const [problem, setProblem] = useState(null); - - const [startedAt, setStartedAt] = useState(null); - - const [timeLeftSeconds, setTimeLeftSeconds] = useState(null); - - useEffect(() => { - if (gameState === "starting" && startedAt !== null) { - const timer1 = setInterval(() => { - setTimeLeftSeconds((prev) => { - if (prev === null) { - return null; - } - if (prev <= 1) { - clearInterval(timer1); - setGameState("gaming"); - return 0; - } - return prev - 1; - }); - }, 1000); - - const timer2 = setInterval(() => { - const nowSec = Math.floor(Date.now() / 1000); - const finishedAt = startedAt + game.duration_seconds; - if (nowSec >= finishedAt) { - clearInterval(timer2); - setGameState("finished"); - } - }, 1000); - - return () => { - clearInterval(timer1); - clearInterval(timer2); - }; - } - }, [gameState, startedAt, game.duration_seconds]); - - const [currentScore, setCurrentScore] = useState(null); - - const onCodeChange = useDebouncedCallback((code: string) => { - console.log("player:c2s:code"); - sendJsonMessage({ - type: "player:c2s:code", - data: { code }, - }); - }, 1000); - - if (readyState === ReadyState.UNINSTANTIATED) { - throw new Error("WebSocket is not connected"); - } - - useEffect(() => { - if (readyState === ReadyState.CLOSING || readyState === ReadyState.CLOSED) { - if (gameState !== "finished") { - setGameState("connecting"); - } - } else if (readyState === ReadyState.CONNECTING) { - setGameState("connecting"); - } else if (readyState === ReadyState.OPEN) { - if (lastJsonMessage !== null) { - console.log(lastJsonMessage.type); - if (lastJsonMessage.type === "player:s2c:prepare") { - const { problem } = lastJsonMessage.data; - setProblem(problem); - console.log("player:c2s:ready"); - sendJsonMessage({ type: "player:c2s:ready" }); - } else if (lastJsonMessage.type === "player:s2c:start") { - if ( - gameState !== "starting" && - gameState !== "gaming" && - gameState !== "finished" - ) { - const { start_at } = lastJsonMessage.data; - setStartedAt(start_at); - const nowSec = Math.floor(Date.now() / 1000); - setTimeLeftSeconds(start_at - nowSec); - setGameState("starting"); - } - } else if (lastJsonMessage.type === "player:s2c:execresult") { - const { score } = lastJsonMessage.data; - if ( - score !== null && - (currentScore === null || score < currentScore) - ) { - setCurrentScore(score); - } - } - } else { - setGameState("waiting"); - console.log("player:c2s:entry"); - sendJsonMessage({ type: "player:c2s:entry" }); - } - } - }, [sendJsonMessage, lastJsonMessage, readyState, gameState, currentScore]); - - if (gameState === "connecting") { - return ; - } else if (gameState === "waiting") { - return ; - } else if (gameState === "starting") { - return ; - } else if (gameState === "gaming") { - return ( - - ); - } else if (gameState === "finished") { - return ; - } else { - return null; - } -} diff --git a/frontend/app/components/GolfWatchApp.client.tsx b/frontend/app/components/GolfWatchApp.client.tsx new file mode 100644 index 0000000..00ad005 --- /dev/null +++ b/frontend/app/components/GolfWatchApp.client.tsx @@ -0,0 +1,142 @@ +import type { components } from "../.server/api/schema"; +import { useState, useEffect } from "react"; +import useWebSocket, { ReadyState } from "react-use-websocket"; +import GolfWatchAppConnecting from "./GolfWatchApps/GolfWatchAppConnecting"; +import GolfWatchAppWaiting from "./GolfWatchApps/GolfWatchAppWaiting"; +import GolfWatchAppStarting from "./GolfWatchApps/GolfWatchAppStarting"; +import GolfWatchAppGaming from "./GolfWatchApps/GolfWatchAppGaming"; +import GolfWatchAppFinished from "./GolfWatchApps/GolfWatchAppFinished"; + +type WebSocketMessage = components["schemas"]["GameWatcherMessageS2C"]; + +type Game = components["schemas"]["Game"]; +type Problem = components["schemas"]["Problem"]; + +type GameState = "connecting" | "waiting" | "starting" | "gaming" | "finished"; + +export default function GolfWatchApp({ + game, + sockToken, +}: { + game: Game; + sockToken: string; +}) { + // const socketUrl = `wss://t.nil.ninja/iosdc/2024/sock/golf/${game.game_id}/watch?token=${sockToken}`; + const socketUrl = + process.env.NODE_ENV === "development" + ? `ws://localhost:8002/sock/golf/${game.game_id}/watch?token=${sockToken}` + : `ws://api-server/sock/golf/${game.game_id}/watch?token=${sockToken}`; + + const { lastJsonMessage, readyState } = useWebSocket( + socketUrl, + {}, + ); + + const [gameState, setGameState] = useState("connecting"); + + const [problem, setProblem] = useState(null); + + const [startedAt, setStartedAt] = useState(null); + + const [timeLeftSeconds, setTimeLeftSeconds] = useState(null); + + useEffect(() => { + if (gameState === "starting" && startedAt !== null) { + const timer1 = setInterval(() => { + setTimeLeftSeconds((prev) => { + if (prev === null) { + return null; + } + if (prev <= 1) { + clearInterval(timer1); + setGameState("gaming"); + return 0; + } + return prev - 1; + }); + }, 1000); + + const timer2 = setInterval(() => { + const nowSec = Math.floor(Date.now() / 1000); + const finishedAt = startedAt + game.duration_seconds; + if (nowSec >= finishedAt) { + clearInterval(timer2); + setGameState("finished"); + } + }, 1000); + + return () => { + clearInterval(timer1); + clearInterval(timer2); + }; + } + }, [gameState, startedAt, game.duration_seconds]); + + const [scoreA, setScoreA] = useState(null); + const [scoreB, setScoreB] = useState(null); + const [codeA, setCodeA] = useState(""); + const [codeB, setCodeB] = useState(""); + + if (readyState === ReadyState.UNINSTANTIATED) { + throw new Error("WebSocket is not connected"); + } + + useEffect(() => { + if (readyState === ReadyState.CLOSING || readyState === ReadyState.CLOSED) { + if (gameState !== "finished") { + setGameState("connecting"); + } + } else if (readyState === ReadyState.CONNECTING) { + setGameState("connecting"); + } else if (readyState === ReadyState.OPEN) { + if (lastJsonMessage !== null) { + console.log(lastJsonMessage.type); + if (lastJsonMessage.type === "watcher:s2c:start") { + if ( + gameState !== "starting" && + gameState !== "gaming" && + gameState !== "finished" + ) { + const { start_at } = lastJsonMessage.data; + setStartedAt(start_at); + const nowSec = Math.floor(Date.now() / 1000); + setTimeLeftSeconds(start_at - nowSec); + setGameState("starting"); + } + } else if (lastJsonMessage.type === "watcher:s2c:code") { + const { player_id, code } = lastJsonMessage.data; + setCodeA(code); + } else if (lastJsonMessage.type === "watcher:s2c:execresult") { + const { score } = lastJsonMessage.data; + if (score !== null && (scoreA === null || score < scoreA)) { + setScoreA(score); + } + } + } else { + setGameState("waiting"); + } + } + }, [lastJsonMessage, readyState, gameState, scoreA]); + + if (gameState === "connecting") { + return ; + } else if (gameState === "waiting") { + return ; + } else if (gameState === "starting") { + return ; + } else if (gameState === "gaming") { + return ( + + ); + } else if (gameState === "finished") { + return ; + } else { + return null; + } +} diff --git a/frontend/app/components/GolfWatchApp.tsx b/frontend/app/components/GolfWatchApp.tsx deleted file mode 100644 index 00ad005..0000000 --- a/frontend/app/components/GolfWatchApp.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import type { components } from "../.server/api/schema"; -import { useState, useEffect } from "react"; -import useWebSocket, { ReadyState } from "react-use-websocket"; -import GolfWatchAppConnecting from "./GolfWatchApps/GolfWatchAppConnecting"; -import GolfWatchAppWaiting from "./GolfWatchApps/GolfWatchAppWaiting"; -import GolfWatchAppStarting from "./GolfWatchApps/GolfWatchAppStarting"; -import GolfWatchAppGaming from "./GolfWatchApps/GolfWatchAppGaming"; -import GolfWatchAppFinished from "./GolfWatchApps/GolfWatchAppFinished"; - -type WebSocketMessage = components["schemas"]["GameWatcherMessageS2C"]; - -type Game = components["schemas"]["Game"]; -type Problem = components["schemas"]["Problem"]; - -type GameState = "connecting" | "waiting" | "starting" | "gaming" | "finished"; - -export default function GolfWatchApp({ - game, - sockToken, -}: { - game: Game; - sockToken: string; -}) { - // const socketUrl = `wss://t.nil.ninja/iosdc/2024/sock/golf/${game.game_id}/watch?token=${sockToken}`; - const socketUrl = - process.env.NODE_ENV === "development" - ? `ws://localhost:8002/sock/golf/${game.game_id}/watch?token=${sockToken}` - : `ws://api-server/sock/golf/${game.game_id}/watch?token=${sockToken}`; - - const { lastJsonMessage, readyState } = useWebSocket( - socketUrl, - {}, - ); - - const [gameState, setGameState] = useState("connecting"); - - const [problem, setProblem] = useState(null); - - const [startedAt, setStartedAt] = useState(null); - - const [timeLeftSeconds, setTimeLeftSeconds] = useState(null); - - useEffect(() => { - if (gameState === "starting" && startedAt !== null) { - const timer1 = setInterval(() => { - setTimeLeftSeconds((prev) => { - if (prev === null) { - return null; - } - if (prev <= 1) { - clearInterval(timer1); - setGameState("gaming"); - return 0; - } - return prev - 1; - }); - }, 1000); - - const timer2 = setInterval(() => { - const nowSec = Math.floor(Date.now() / 1000); - const finishedAt = startedAt + game.duration_seconds; - if (nowSec >= finishedAt) { - clearInterval(timer2); - setGameState("finished"); - } - }, 1000); - - return () => { - clearInterval(timer1); - clearInterval(timer2); - }; - } - }, [gameState, startedAt, game.duration_seconds]); - - const [scoreA, setScoreA] = useState(null); - const [scoreB, setScoreB] = useState(null); - const [codeA, setCodeA] = useState(""); - const [codeB, setCodeB] = useState(""); - - if (readyState === ReadyState.UNINSTANTIATED) { - throw new Error("WebSocket is not connected"); - } - - useEffect(() => { - if (readyState === ReadyState.CLOSING || readyState === ReadyState.CLOSED) { - if (gameState !== "finished") { - setGameState("connecting"); - } - } else if (readyState === ReadyState.CONNECTING) { - setGameState("connecting"); - } else if (readyState === ReadyState.OPEN) { - if (lastJsonMessage !== null) { - console.log(lastJsonMessage.type); - if (lastJsonMessage.type === "watcher:s2c:start") { - if ( - gameState !== "starting" && - gameState !== "gaming" && - gameState !== "finished" - ) { - const { start_at } = lastJsonMessage.data; - setStartedAt(start_at); - const nowSec = Math.floor(Date.now() / 1000); - setTimeLeftSeconds(start_at - nowSec); - setGameState("starting"); - } - } else if (lastJsonMessage.type === "watcher:s2c:code") { - const { player_id, code } = lastJsonMessage.data; - setCodeA(code); - } else if (lastJsonMessage.type === "watcher:s2c:execresult") { - const { score } = lastJsonMessage.data; - if (score !== null && (scoreA === null || score < scoreA)) { - setScoreA(score); - } - } - } else { - setGameState("waiting"); - } - } - }, [lastJsonMessage, readyState, gameState, scoreA]); - - if (gameState === "connecting") { - return ; - } else if (gameState === "waiting") { - return ; - } else if (gameState === "starting") { - return ; - } else if (gameState === "gaming") { - return ( - - ); - } else if (gameState === "finished") { - return ; - } else { - return null; - } -} -- cgit v1.2.3-70-g09d2