diff --git a/packages/frontend/src/components/gamePage/leftSection/GamePhases/EndingResult.tsx b/packages/frontend/src/components/gamePage/leftSection/GamePhases/EndingResult.tsx index f8c5cfa..8bf4c6f 100644 --- a/packages/frontend/src/components/gamePage/leftSection/GamePhases/EndingResult.tsx +++ b/packages/frontend/src/components/gamePage/leftSection/GamePhases/EndingResult.tsx @@ -15,38 +15,43 @@ export default function EndingResult({ endingResult }: IEndingPhaseProps) { const resultImage = isPinocoWin ? '/images/ending/pinocoWin.png' : '/images/ending/geppettoWin.png'; + const getEndingMessage = () => { - if (guessingWord && isGuessed) { + if (!isGuessed) return; + if (isPinocoWin) { return ( <> - 피노코가 제시어를 맞추며 위기에서 벗어났습니다! 제출된 제시어:{' '} + 피노코가 제시어를 맞추며 위기에서 벗어났습니다! 제출된 제시어 :{' '} + {guessingWord} + + ); + } else { + return ( + <> + 투표로 지목된 피노코가 제시어를 맞추지 못했습니다! 제출된 제시어 :{' '} {guessingWord} ); } - - if (isPinocoWin) { - return <>피노코가 자신의 정체를 숨기며 제페토를 속였습니다 😉; - } - - return <>피노코가 제시어를 맞추지 못했습니다 🔨; }; return ( -
- {isPinocoWin +
+
+ {isPinocoWin +
-

+

{isPinocoWin ? '피노코가 승리했습니다 🤥' : '제페토가 승리했습니다 🧓🏻'}

-
+

- 피노코는 {pinoco} 였습니다! + 피노코는 {pinoco}였습니다!

{getEndingMessage()}

diff --git a/packages/frontend/src/components/gamePage/leftSection/VideoFeed.tsx b/packages/frontend/src/components/gamePage/leftSection/VideoFeed.tsx index 9c9636c..541a533 100644 --- a/packages/frontend/src/components/gamePage/leftSection/VideoFeed.tsx +++ b/packages/frontend/src/components/gamePage/leftSection/VideoFeed.tsx @@ -1,23 +1,25 @@ -import { useRef, useEffect } from 'react'; +import { useEffect } from 'react'; import VideoStream from '@/components/gamePage/stream/VideoStream'; import { useAuthStore } from '@/store/authStore'; import { useLocalStreamStore } from '@/store/localStreamStore'; import { usePeerConnectionStore } from '@/store/peerConnectionStore'; -import { useReadyStatus } from '@/hooks/useReadyStatus'; +import { useRoomStore } from '@/store/roomStore'; export default function VideoFeed() { const localStream = useLocalStreamStore((state) => state.localStream); const remoteStreams = usePeerConnectionStore((state) => state.remoteStreams); const { userId } = useAuthStore(); const feedHeight = 'h-[180px]'; - const { isUserReady } = useReadyStatus(); + const readyUsers = useRoomStore((state) => state.readyUsers); + const isUserReady = (userId: string) => readyUsers.includes(userId); return ( <>
{isUserReady(userId || '') && ( @@ -40,7 +42,7 @@ export default function VideoFeed() { height={feedHeight} /> {isUserReady(remoteUserId) && ( -
+
READY diff --git a/packages/frontend/src/components/lobbyPage/GameEntryModal.tsx b/packages/frontend/src/components/lobbyPage/GameEntryModal.tsx index b8d2ca0..f819129 100644 --- a/packages/frontend/src/components/lobbyPage/GameEntryModal.tsx +++ b/packages/frontend/src/components/lobbyPage/GameEntryModal.tsx @@ -1,5 +1,5 @@ import ReactDOM from 'react-dom'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { Tooltip } from 'react-tooltip'; interface IGameEntryModalProps { @@ -20,6 +20,7 @@ export default function GameEntryModal({ const [gameCode, setGameCode] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const [showTooltip, setShowTooltip] = useState(false); + const inputRef = useRef(null); useEffect(() => { if (!localStorage.getItem('modalTooltipShown')) { @@ -28,6 +29,10 @@ export default function GameEntryModal({ } }, []); + useEffect(() => { + inputRef.current?.focus(); + }, []); + const handleConfirm = () => { if (gameCode.trim()) { setErrorMessage(''); @@ -59,6 +64,7 @@ export default function GameEntryModal({ {textForm && ( setGameCode(e.target.value)} diff --git a/packages/frontend/src/hooks/useEnding.ts b/packages/frontend/src/hooks/useEnding.ts index a9cd2f1..7bede17 100644 --- a/packages/frontend/src/hooks/useEnding.ts +++ b/packages/frontend/src/hooks/useEnding.ts @@ -33,7 +33,7 @@ export default function useEnding(setGamePhase: (phase: GamePhase) => void) { const timeout = setTimeout(() => { setGamePhase(GAME_PHASE.WAITING); setEndingResult(null); - }, 5000); + }, 7000); return () => clearTimeout(timeout); } diff --git a/packages/frontend/src/hooks/useGameSocket.ts b/packages/frontend/src/hooks/useGameSocket.ts index 637290b..241dba8 100644 --- a/packages/frontend/src/hooks/useGameSocket.ts +++ b/packages/frontend/src/hooks/useGameSocket.ts @@ -25,8 +25,7 @@ interface ISpeakingStart { export const useGameSocket = (onPhaseChange?: (phase: GamePhase) => void) => { const socket = useSocketStore((state) => state.socket); - const { setIsPinoco, setAllUsers } = useRoomStore(); - const [readyUsers, setReadyUsers] = useState([]); + const { setIsPinoco, setAllUsers, setReadyUsers } = useRoomStore(); const [error, setError] = useState(null); const [gameStartData, setGameStartData] = useState(null); const [currentSpeaker, setCurrentSpeaker] = useState(null); @@ -34,6 +33,10 @@ export const useGameSocket = (onPhaseChange?: (phase: GamePhase) => void) => { useEffect(() => { if (!socket) return; + const handleUpdateReady = (data: IReadyUsers) => { + setReadyUsers(data.readyUsers); + }; + const handleStartSpeaking = (data: ISpeakingStart) => { setCurrentSpeaker(data.speakerId); if (onPhaseChange) { @@ -46,6 +49,7 @@ export const useGameSocket = (onPhaseChange?: (phase: GamePhase) => void) => { setGameStartData(data); setCurrentSpeaker(data.speakerId); setIsPinoco(data.isPinoco); + setReadyUsers([]); }; const handleStartVote = () => { @@ -54,27 +58,23 @@ export const useGameSocket = (onPhaseChange?: (phase: GamePhase) => void) => { } }; - socket.on('update_ready', (data: IReadyUsers) => { - setReadyUsers(data.readyUsers); - }); - + socket.on('update_ready', handleUpdateReady); socket.on('error', (data: IGameErrorMessage) => { setError(data.errorMessage); setTimeout(() => setError(null), 3000); }); - socket.on('start_game_success', handleStartGame); socket.on('start_speaking', handleStartSpeaking); socket.on('start_vote', handleStartVote); return () => { - socket.off('update_ready'); + socket.off('update_ready', handleUpdateReady); socket.off('error'); socket.off('start_game_success', handleStartGame); socket.off('start_speaking', handleStartSpeaking); socket.off('start_vote', handleStartVote); }; - }, [socket, setIsPinoco, onPhaseChange, setCurrentSpeaker, setAllUsers]); + }, [socket, setIsPinoco, onPhaseChange, setCurrentSpeaker, setAllUsers, setReadyUsers]); const sendReady = (isReady: boolean) => { if (!socket) return; @@ -100,7 +100,6 @@ export const useGameSocket = (onPhaseChange?: (phase: GamePhase) => void) => { }; return { - readyUsers, sendReady, startGame, error, diff --git a/packages/frontend/src/hooks/useReadyStatus.ts b/packages/frontend/src/hooks/useReadyStatus.ts deleted file mode 100644 index d2cf7b8..0000000 --- a/packages/frontend/src/hooks/useReadyStatus.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useGameSocket } from '@/hooks/useGameSocket'; - -export const useReadyStatus = () => { - const { readyUsers } = useGameSocket(); - - const isUserReady = (userId: string) => readyUsers.includes(userId); - - return { isUserReady }; -}; diff --git a/packages/frontend/src/store/roomStore.ts b/packages/frontend/src/store/roomStore.ts index 481b7e3..75c6825 100644 --- a/packages/frontend/src/store/roomStore.ts +++ b/packages/frontend/src/store/roomStore.ts @@ -1,16 +1,21 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; + interface IRoomState { isHost: boolean; gsid: string | null; isPinoco: boolean; allUsers: Set; + readyUsers: string[]; setRoomData: (gsid: string | null, isHost: boolean, isPinoco: boolean) => void; setIsPinoco: (isPinoco: boolean) => void; setAllUsers: (allUsers: string[]) => void; addUser: (userId: string) => void; removeUser: (userId: string) => void; setIsHost: (isHost: boolean) => void; + setReadyUsers: (readyUsers: string[]) => void; + addReadyUser: (userId: string) => void; + removeReadyUser: (userId: string) => void; } export const useRoomStore = create()( persist( @@ -19,6 +24,8 @@ export const useRoomStore = create()( gsid: null, isPinoco: false, allUsers: new Set(), + readyUsers: [], + setRoomData: (gsid, isHost, isPinoco) => set({ gsid, isHost, isPinoco }), setIsPinoco: (isPinoco) => set({ isPinoco }), setAllUsers: (allUsers) => set({ allUsers: new Set(allUsers) }), @@ -31,6 +38,16 @@ export const useRoomStore = create()( allUsers: new Set([...state.allUsers].filter((id) => id !== userId)), })), setIsHost: (isHost) => set({ isHost }), + + setReadyUsers: (readyUsers) => set({ readyUsers }), + addReadyUser: (userId) => + set((state) => ({ + readyUsers: [...state.readyUsers, userId], + })), + removeReadyUser: (userId) => + set((state) => ({ + readyUsers: state.readyUsers.filter((id) => id !== userId), + })), }), { name: 'room-storage' }, ),