Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] 게임 결과 리팩토링 #220

Merged
merged 10 commits into from
Dec 3, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
피노코가 제시어를 맞추며 위기에서 벗어났습니다! 제출된 제시어:{' '}
피노코가 제시어를 맞추며 위기에서 벗어났습니다! 제출된 제시어 :{' '}
<span className="font-extrabold">{guessingWord}</span>
</>
);
} else {
return (
<>
투표로 지목된 피노코가 제시어를 맞추지 못했습니다! 제출된 제시어 :{' '}
<span className="font-extrabold">{guessingWord}</span>
</>
);
}

if (isPinocoWin) {
return <>피노코가 자신의 정체를 숨기며 제페토를 속였습니다 😉</>;
}

return <>피노코가 제시어를 맞추지 못했습니다 🔨</>;
};

return (
<div className="flex flex-col items-center justify-center h-full space-y-8">
<img
src={resultImage}
alt={isPinocoWin ? '피노코 승리' : '제페토 승리'}
className="w-[60%] max-h-[400px] object-contain"
/>
<div className="flex flex-col items-center justify-center h-full space-y-6 overflow-hidden">
<div className="w-full max-w-sm">
<img
src={resultImage}
alt={isPinocoWin ? '피노코 승리' : '제페토 승리'}
className="w-full h-auto object-contain"
/>
</div>

<h2 className="text-3xl font-extrabold text-white-default">
<h2 className="text-2xl font-extrabold text-white-default text-center">
{isPinocoWin ? '피노코가 승리했습니다 🤥' : '제페토가 승리했습니다 🧓🏻'}
</h2>

<div className="text-2xl text-center text-white-default leading-relaxed">
<div className="text-lg text-center text-white-default leading-relaxed px-4">
<p>
피노코는 <span className="font-extrabold">{pinoco}</span> 였습니다!
피노코는 <span className="font-extrabold">{pinoco}</span>였습니다!
</p>
<p>{getEndingMessage()}</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<>
<div
className={`relative ${
isUserReady(userId || '') ? 'border-4 border-white' : ''
} rounded-lg`}
className={`relative rounded-lg`}
style={{
boxShadow: isUserReady(userId || '') ? '0 0 0 4px white' : '',
}}
>
<VideoStream stream={localStream} userName={userId} isLocal={true} height={feedHeight} />
{isUserReady(userId || '') && (
Expand All @@ -40,7 +42,7 @@ export default function VideoFeed() {
height={feedHeight}
/>
{isUserReady(remoteUserId) && (
<div className="absolute bottom-2 right-2 animate-spin-slow">
<div className="absolute bottom-2 right-2">
<span className="px-2 py-1 text-orange-500 font-bold bg-white rounded-full">
READY
</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -20,6 +20,7 @@ export default function GameEntryModal({
const [gameCode, setGameCode] = useState('');
const [errorMessage, setErrorMessage] = useState('');
const [showTooltip, setShowTooltip] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);

useEffect(() => {
if (!localStorage.getItem('modalTooltipShown')) {
Expand All @@ -28,6 +29,10 @@ export default function GameEntryModal({
}
}, []);

useEffect(() => {
inputRef.current?.focus();
}, []);

const handleConfirm = () => {
if (gameCode.trim()) {
setErrorMessage('');
Expand Down Expand Up @@ -59,6 +64,7 @@ export default function GameEntryModal({
{textForm && (
<input
type="text"
ref={inputRef}
placeholder={textForm}
value={gameCode}
onChange={(e) => setGameCode(e.target.value)}
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/hooks/useEnding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
19 changes: 9 additions & 10 deletions packages/frontend/src/hooks/useGameSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,18 @@ interface ISpeakingStart {

export const useGameSocket = (onPhaseChange?: (phase: GamePhase) => void) => {
const socket = useSocketStore((state) => state.socket);
const { setIsPinoco, setAllUsers } = useRoomStore();
const [readyUsers, setReadyUsers] = useState<string[]>([]);
const { setIsPinoco, setAllUsers, setReadyUsers } = useRoomStore();
const [error, setError] = useState<string | null>(null);
const [gameStartData, setGameStartData] = useState<IGameStart | null>(null);
const [currentSpeaker, setCurrentSpeaker] = useState<string | null>(null);

useEffect(() => {
if (!socket) return;

const handleUpdateReady = (data: IReadyUsers) => {
setReadyUsers(data.readyUsers);
};

const handleStartSpeaking = (data: ISpeakingStart) => {
setCurrentSpeaker(data.speakerId);
if (onPhaseChange) {
Expand All @@ -46,6 +49,7 @@ export const useGameSocket = (onPhaseChange?: (phase: GamePhase) => void) => {
setGameStartData(data);
setCurrentSpeaker(data.speakerId);
setIsPinoco(data.isPinoco);
setReadyUsers([]);
};

const handleStartVote = () => {
Expand All @@ -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;
Expand All @@ -100,7 +100,6 @@ export const useGameSocket = (onPhaseChange?: (phase: GamePhase) => void) => {
};

return {
readyUsers,
sendReady,
startGame,
error,
Expand Down
9 changes: 0 additions & 9 deletions packages/frontend/src/hooks/useReadyStatus.ts

This file was deleted.

17 changes: 17 additions & 0 deletions packages/frontend/src/store/roomStore.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface IRoomState {
isHost: boolean;
gsid: string | null;
isPinoco: boolean;
allUsers: Set<string>;
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<IRoomState>()(
persist(
Expand All @@ -19,6 +24,8 @@ export const useRoomStore = create<IRoomState>()(
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) }),
Expand All @@ -31,6 +38,16 @@ export const useRoomStore = create<IRoomState>()(
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' },
),
Expand Down
Loading