diff --git a/apps/client/src/app.tsx b/apps/client/src/app.tsx index 2e36f59..832b480 100644 --- a/apps/client/src/app.tsx +++ b/apps/client/src/app.tsx @@ -19,6 +19,7 @@ joinErrorMessage, isJoining, requestJoin, + returnToTitle, } = useAppFlow(); let scene = ; @@ -41,13 +42,19 @@ room={room} myId={myId} onStart={() => socketManager.lobby.startGame()} + onBackToTitle={() => returnToTitle({ leaveRoom: true })} /> ); } // 結果画面分岐 if (scenePhase === appConsts.ScenePhase.RESULT) { - scene = ; + scene = ( + returnToTitle({ leaveRoom: true })} + /> + ); } return {scene}; diff --git a/apps/client/src/hooks/useAppFlow.ts b/apps/client/src/hooks/useAppFlow.ts index 01aa243..403a442 100644 --- a/apps/client/src/hooks/useAppFlow.ts +++ b/apps/client/src/hooks/useAppFlow.ts @@ -19,6 +19,7 @@ joinErrorMessage: string | null; isJoining: boolean; requestJoin: (payload: roomTypes.JoinRoomPayload) => void; + returnToTitle: (options?: { leaveRoom?: boolean }) => void; }; type JoinState = { @@ -26,7 +27,9 @@ joinFailure: JoinFailure | null; }; -type JoinFailureReason = roomTypes.JoinRoomRejectedPayload["reason"] | "timeout"; +type JoinFailureReason = + | roomTypes.JoinRoomRejectedPayload["reason"] + | "timeout"; type JoinFailure = { reason: JoinFailureReason; @@ -62,13 +65,17 @@ /** アプリ全体のシーン状態と参加要求フローを管理するフック */ export const useAppFlow = (): AppFlowState => { - const [scenePhase, setScenePhase] = useState(appConsts.ScenePhase.TITLE); + const [scenePhase, setScenePhase] = useState( + appConsts.ScenePhase.TITLE, + ); const [room, setRoom] = useState(null); const [myId, setMyId] = useState(null); const [gameResult, setGameResult] = useState(null); const [joinState, dispatchJoin] = useReducer(joinReducer, initialJoinState); const joinTimeoutRef = useRef | null>(null); - const joinRejectedHandlerRef = useRef<((payload: roomTypes.JoinRoomRejectedPayload) => void) | null>(null); + const joinRejectedHandlerRef = useRef< + ((payload: roomTypes.JoinRoomRejectedPayload) => void) | null + >(null); const clearJoinRejectedHandler = useCallback(() => { if (!joinRejectedHandlerRef.current) { @@ -88,56 +95,85 @@ joinTimeoutRef.current = null; }, []); - const completeJoinRequest = useCallback((joinFailure: JoinFailure | null = null) => { - clearJoinTimeout(); - clearJoinRejectedHandler(); - dispatchJoin({ type: "complete", joinFailure }); - }, [clearJoinRejectedHandler, clearJoinTimeout]); + const completeJoinRequest = useCallback( + (joinFailure: JoinFailure | null = null) => { + clearJoinTimeout(); + clearJoinRejectedHandler(); + dispatchJoin({ type: "complete", joinFailure }); + }, + [clearJoinRejectedHandler, clearJoinTimeout], + ); - const getJoinErrorMessage = useCallback((joinFailure: JoinFailure | null): string | null => { - if (!joinFailure) { + const getJoinErrorMessage = useCallback( + (joinFailure: JoinFailure | null): string | null => { + if (!joinFailure) { + return null; + } + + if (joinFailure.reason === "full") { + return `ルーム ${joinFailure.roomId ?? ""} は満員です`; + } + + if (joinFailure.reason === "duplicate") { + return `ルーム ${joinFailure.roomId ?? ""} への参加要求が重複しました`; + } + + if (joinFailure.reason === "timeout") { + return "参加要求がタイムアウトしました,もう一度お試しください"; + } + return null; - } + }, + [], + ); - if (joinFailure.reason === "full") { - return `ルーム ${joinFailure.roomId ?? ""} は満員です`; - } + const requestJoin = useCallback( + (payload: roomTypes.JoinRoomPayload) => { + if (joinState.isJoining) { + return; + } - if (joinFailure.reason === "duplicate") { - return `ルーム ${joinFailure.roomId ?? ""} への参加要求が重複しました`; - } + completeJoinRequest(); + dispatchJoin({ type: "start" }); - if (joinFailure.reason === "timeout") { - return "参加要求がタイムアウトしました,もう一度お試しください"; - } + const handleJoinRejected = ( + payload: roomTypes.JoinRoomRejectedPayload, + ) => { + completeJoinRequest({ + reason: payload.reason, + roomId: payload.roomId, + }); + }; - return null; - }, []); + joinRejectedHandlerRef.current = handleJoinRejected; + socketManager.title.onceJoinRejected(handleJoinRejected); - const requestJoin = useCallback((payload: roomTypes.JoinRoomPayload) => { - if (joinState.isJoining) { - return; - } + joinTimeoutRef.current = setTimeout(() => { + completeJoinRequest({ reason: "timeout" }); + }, config.GAME_CONFIG.JOIN_REQUEST_TIMEOUT_MS); - completeJoinRequest(); - dispatchJoin({ type: "start" }); + socketManager.title.joinRoom(payload); + }, + [completeJoinRequest, joinState.isJoining], + ); - const handleJoinRejected = (payload: roomTypes.JoinRoomRejectedPayload) => { - completeJoinRequest({ - reason: payload.reason, - roomId: payload.roomId, - }); - }; + const returnToTitle = useCallback( + (options?: { leaveRoom?: boolean }) => { + completeJoinRequest(); + setRoom(null); + setGameResult(null); + setScenePhase(appConsts.ScenePhase.TITLE); - joinRejectedHandlerRef.current = handleJoinRejected; - socketManager.title.onceJoinRejected(handleJoinRejected); + if (!options?.leaveRoom) { + return; + } - joinTimeoutRef.current = setTimeout(() => { - completeJoinRequest({ reason: "timeout" }); - }, config.GAME_CONFIG.JOIN_REQUEST_TIMEOUT_MS); - - socketManager.title.joinRoom(payload); - }, [completeJoinRequest, joinState.isJoining]); + setMyId(null); + socketManager.socket.disconnect(); + socketManager.socket.connect(); + }, + [completeJoinRequest], + ); useSocketSubscriptions({ completeJoinRequest, @@ -155,5 +191,6 @@ joinErrorMessage: getJoinErrorMessage(joinState.joinFailure), isJoining: joinState.isJoining, requestJoin, + returnToTitle, }; }; diff --git a/apps/client/src/scenes/lobby/LobbyScene.tsx b/apps/client/src/scenes/lobby/LobbyScene.tsx index 22977e2..7f353e0 100644 --- a/apps/client/src/scenes/lobby/LobbyScene.tsx +++ b/apps/client/src/scenes/lobby/LobbyScene.tsx @@ -4,9 +4,10 @@ room: roomTypes.Room | null; myId: string | null; onStart: () => void; + onBackToTitle: () => void; }; -export const LobbyScene = ({ room, myId, onStart }: Props) => { +export const LobbyScene = ({ room, myId, onStart, onBackToTitle }: Props) => { if (!room) return
読み込み中...
; @@ -70,6 +71,24 @@ ルーム: {room.roomId} (待機中) + +
void; }; const formatPaintRate = (value: number): string => `${value.toFixed(1)}%`; @@ -238,7 +239,7 @@ }; /** 最終結果データを受け取り,順位一覧を表示する */ -export const ResultScene = ({ result }: Props) => { +export const ResultScene = ({ result, onBackToTitle }: Props) => { if (!result) { return (
結果を読み込み中...
@@ -291,6 +292,24 @@
+ +

結果発表