Skip to content

Commit dd04bb4

Browse files
committed
feat: 게임 진행화면 구현중
1 parent db5f77a commit dd04bb4

File tree

11 files changed

+169
-114
lines changed

11 files changed

+169
-114
lines changed

src/hooks/useStompClient.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,26 @@ export default function useStompClient(roomId, onMessage) {
9090
if (!isConnected || !stompClient || !roomId || hasInitializedRoomRef.current === true) return;
9191
const localKey = `enteredRoom_${roomId}`;
9292
const hasEnteredBefore = localStorage.getItem(localKey);
93+
console.log(localKey, hasEnteredBefore, roomId)
9394
if (hasEnteredBefore) {
9495
// 재접속 또는 새로고침
9596
stompClient.publish({
9697
destination: `/pub/room/reconnect/${roomId}`,
9798
body: '',
9899
});
100+
console.log(`🚀 초기화 메시지 전송됨: /pub/room/reconnect/${roomId}`);
101+
99102
} else {
100103
// 첫 입장
101104
stompClient.publish({
102105
destination: `/pub/room/initializeRoomSocket/${roomId}`,
103106
body: '',
104107
});
108+
console.log(`🚀 초기화 메시지 전송됨: /pub/room/initializeRoomSocket/${roomId}`);
105109
localStorage.setItem(localKey, 'true'); //방 목록에서 제거됨
106110
}
107111

108112
hasInitializedRoomRef.current = true;
109-
console.log(`🚀 초기화 메시지 전송됨: /pub/room/initializeRoomSocket/${roomId}`);
110113
}, [roomId, ready]);
111114

112115
return {

src/layout/game/GameLayout.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ const GameLayout = () => {
9696
setChat(payload.message);
9797
break;
9898
case "GAME_START":
99-
setQuestions(payload.message);
99+
setQuestions(payload.message.questions);
100+
navigate("play");
100101
break;
101102
case "QUESTION_START":
102103
setQuestionStart(payload.message);
@@ -121,7 +122,6 @@ const GameLayout = () => {
121122
const { sendMessage } = useStompClient(roomId, handleStompMessage);
122123

123124
useEffect(() => {
124-
console.log('📦 sendMessage:', sendMessage);
125125
if (sendMessage) {
126126
setSendMessage(() => sendMessage); // Recoil 전역 등록
127127
}

src/layout/game/components/GameSettings.js

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import {useRecoilValue} from "recoil";
66
import {stompSendMessageAtom} from "../../../state/atoms";
77

88
function GameSettings({ roomId, gameSetting, allReady }) {
9-
const [timePerQuestion, setTimePerQuestion] = useState(60);
10-
const [questionCount, setQuestionCount] = useState(gameSetting?.quiz.numberOfQuestion);
119
const [quizSelectModalOpen, setQuizSelectModalOpen] = useState(false);
1210
const navigate = useNavigate();
1311
const sendMessage = useRecoilValue(stompSendMessageAtom);
@@ -23,29 +21,26 @@ function GameSettings({ roomId, gameSetting, allReady }) {
2321
"quizId" : quiz.quizId,
2422
}
2523
}
26-
sendMessage(`/room/quiz/${roomId}`, quizChangeMessage);
24+
sendMessage(`/pub/room/quiz/${roomId}`, quizChangeMessage);
2725
}
2826

29-
const handleTimePerQuestionChange = (e) => {
30-
console.log('handleTimePerQuestionChange: ', e.target.value)
31-
setTimePerQuestion(e.target.value);
32-
const timePerQuestionMessage = {
27+
const handleTimeLimitChange = (e) => {
28+
const timeLimitMessage = {
3329
"message" : {
3430
"timeLimit" : e.target.value
3531
}
3632
}
37-
sendMessage(`/room/time-limit/${roomId}`, timePerQuestionMessage);
33+
sendMessage(`/pub/room/time-limit/${roomId}`, timeLimitMessage);
3834
};
3935

40-
const handleQuestionCountChange = (e) => {
36+
const handleRoundChange = (e) => {
4137
console.log('handleQuestionCountChange: ', e.target.value)
42-
setQuestionCount(e.target.value);
43-
const questionCountMessage = {
38+
const roundMessage = {
4439
"message" : {
4540
"round" : e.target.value
4641
}
4742
}
48-
sendMessage(`/room/round/${roomId}`, questionCountMessage);
43+
sendMessage(`/pub/room/round/${roomId}`, roundMessage);
4944
};
5045

5146
return (
@@ -69,8 +64,8 @@ function GameSettings({ roomId, gameSetting, allReady }) {
6964
<div>
7065
<label className="block text-sm font-medium text-gray-700 mb-2">제한 시간</label>
7166
<select
72-
value={timePerQuestion}
73-
onChange={handleTimePerQuestionChange}
67+
value={gameSetting?.timeLimit}
68+
onChange={handleTimeLimitChange}
7469
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500 appearance-none bg-white bg-no-repeat bg-right pr-8"
7570
style={{
7671
backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' strokeLinecap='round' strokeLinejoin='round' strokeWidth='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e")`,
@@ -87,8 +82,8 @@ function GameSettings({ roomId, gameSetting, allReady }) {
8782
<div>
8883
<label className="block text-sm font-medium text-gray-700 mb-2">라운드</label>
8984
<select
90-
value={questionCount}
91-
onChange={handleQuestionCountChange}
85+
value={gameSetting?.round}
86+
onChange={handleRoundChange}
9287
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500 appearance-none bg-white bg-no-repeat bg-right pr-8"
9388
style={{
9489
backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' strokeLinecap='round' strokeLinejoin='round' strokeWidth='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e")`,

src/layout/game/components/QuizCompleted.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function QuizCompleted({ totalQuestions }) {
1+
function QuizCompleted() {
22
return (
33
<div className="bg-white rounded-2xl shadow-lg p-8 mb-8 flex-1 flex items-center justify-center">
44
<div className="text-center">

src/layout/game/components/QuizInfoCard.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { MessageCircleQuestion, Clock, List } from "lucide-react"
2-
import {useRecoilValue} from "recoil";
3-
import {gameSettingAtom} from "../../../state/atoms";
1+
import {Clock, List, MessageCircleQuestion} from "lucide-react"
42

53
function QuizInfoCard({ gameSetting }) {
64
return (

src/layout/game/components/QuizQuestion.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function QuizQuestion({ questionNumber, totalQuestions, question }) {
1+
function QuizQuestion({ question }) {
22
return (
33
<div className="bg-white rounded-2xl shadow-lg p-8 mb-8 flex-1 flex items-center justify-center">
44
<div className="text-center">

src/pages/game/GamePlay.js

Lines changed: 23 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,16 @@
1-
"use client"
2-
3-
import { useState, useEffect } from "react"
4-
import { useParams } from "react-router-dom"
1+
import {useState} from "react"
2+
import {useParams} from "react-router-dom"
53
import F1StartingLights from "../../layout/game/components/F1StartingLights"
64
import QuizTimer from "../../layout/game/components/QuizTimer"
75
import QuizQuestion from "../../layout/game/components/QuizQuestion"
86
import QuizCompleted from "../../layout/game/components/QuizCompleted"
9-
import WinnerModal from "../../layout/game/components/WinnerModal"
107
import QuizResultsModal from "../../layout/game/components/QuizResultsModal"
11-
12-
const sampleQuestions = [
13-
{
14-
question: "Which driver won the 2024 Monaco Grand Prix?",
15-
answer: "Charles Leclerc",
16-
},
17-
{
18-
question: "What is the maximum number of power units a driver can use in a season?",
19-
answer: "4",
20-
},
21-
{
22-
question: 'Which circuit is known as "The Temple of Speed"?',
23-
answer: "Monza",
24-
},
25-
]
8+
import {useRecoilValue} from "recoil";
9+
import {
10+
questionResultAtom,
11+
questionsAtom,
12+
questionStartAtom
13+
} from "../../state/atoms";
2614

2715
const finalResults = [
2816
{ rank: 1, name: "Lewis Hamilton", correctAnswers: 12, totalQuestions: 25, score: 120, color: "text-red-600" },
@@ -32,74 +20,39 @@ const finalResults = [
3220
]
3321

3422
function GamePlay() {
35-
const { id: roomId } = useParams()
36-
const [showStartingLights, setShowStartingLights] = useState(true)
37-
const [currentQuestion, setCurrentQuestion] = useState(0)
38-
const [quizStarted, setQuizStarted] = useState(false)
39-
const [quizCompleted, setQuizCompleted] = useState(false)
40-
const [showWinnerModal, setShowWinnerModal] = useState(false)
41-
const [winner, setWinner] = useState("")
42-
const [correctAnswer, setCorrectAnswer] = useState("")
43-
const [showResultsModal, setShowResultsModal] = useState(false)
23+
const { id: roomId } = useParams();
24+
const [showStartingLights, setShowStartingLights] = useState(true);
25+
const [quizStarted, setQuizStarted] = useState(false);
26+
const [quizCompleted, setQuizCompleted] = useState(false);
27+
const [showResultsModal, setShowResultsModal] = useState(false);
28+
const questions = useRecoilValue(questionsAtom);
29+
const currentQuestion = useRecoilValue(questionStartAtom);
30+
const questionsResult = useRecoilValue(questionResultAtom);
31+
console.log('questions: ', questions, currentQuestion, questionsResult);
4432

4533
const handleLightsComplete = () => {
4634
setShowStartingLights(false)
4735
setQuizStarted(true)
4836
}
4937

5038
const handleTimeUp = () => {
51-
handleNextQuestion()
52-
}
53-
54-
const handleCorrectAnswer = (winnerName, answer) => {
55-
setWinner(winnerName)
56-
setCorrectAnswer(answer)
57-
setShowWinnerModal(true)
58-
}
59-
60-
const handleCloseWinnerModal = () => {
61-
setShowWinnerModal(false)
62-
handleNextQuestion()
63-
}
64-
65-
const handleNextQuestion = () => {
66-
if (currentQuestion < sampleQuestions.length - 1) {
67-
setCurrentQuestion((prev) => prev + 1)
68-
} else {
69-
setQuizCompleted(true)
70-
setTimeout(() => {
71-
setShowResultsModal(true)
72-
}, 1000)
73-
}
7439
}
7540

7641
const handleCloseResultsModal = () => {
77-
setShowResultsModal(false)
42+
setShowResultsModal(false);
7843
}
7944

80-
// 테스트를 위해 3초 후 정답 모달 표시
81-
useEffect(() => {
82-
if (quizStarted && !showWinnerModal && !quizCompleted) {
83-
const timer = setTimeout(() => {
84-
handleCorrectAnswer("Charles Leclerc", sampleQuestions[currentQuestion]?.answer || "")
85-
}, 3000)
86-
return () => clearTimeout(timer)
87-
}
88-
}, [quizStarted, currentQuestion, showWinnerModal, quizCompleted])
89-
9045
return (
9146
<div className="p-8 flex flex-col h-full">
9247
{showStartingLights && <F1StartingLights onComplete={handleLightsComplete} />}
9348

94-
<div
95-
className={`transition-opacity duration-500 ${quizStarted ? "opacity-100" : "opacity-0"} h-full flex flex-col`}
96-
>
49+
<div className={`transition-opacity duration-500 ${quizStarted ? "opacity-100" : "opacity-0"} h-full flex flex-col`}>
9750
{/* Quiz Header */}
9851
<div className="flex items-center justify-between mb-8">
9952
<div className="bg-white rounded-lg px-4 py-2 shadow-sm">
10053
<span className="text-lg text-gray-600">문제</span>
10154
<span className="text-lg font-bold text-gray-900 ml-2">
102-
{quizCompleted ? sampleQuestions.length : currentQuestion + 1} / {sampleQuestions.length}
55+
{quizCompleted ? questions?.length : currentQuestion?.round} / {questions?.length}
10356
</span>
10457
</div>
10558
<QuizTimer duration={quizCompleted ? 0 : 30} onTimeUp={handleTimeUp} />
@@ -108,28 +61,15 @@ function GamePlay() {
10861
{/* Question Content */}
10962
<div className="flex-1 flex justify-center">
11063
{quizCompleted ? (
111-
<QuizCompleted totalQuestions={sampleQuestions.length} />
64+
<QuizCompleted totalQuestions={questions?.length} />
11265
) : (
11366
quizStarted &&
114-
currentQuestion < sampleQuestions.length && (
115-
<QuizQuestion
116-
questionNumber={currentQuestion + 1}
117-
totalQuestions={sampleQuestions.length}
118-
question={sampleQuestions[currentQuestion].question}
119-
/>
67+
currentQuestion?.round <= questions?.length && (
68+
<QuizQuestion question={questions[currentQuestion?.round]?.question}/>
12069
)
12170
)}
12271
</div>
12372
</div>
124-
125-
{/* Modals */}
126-
<WinnerModal
127-
isVisible={showWinnerModal}
128-
winner={winner}
129-
correctAnswer={correctAnswer}
130-
onClose={handleCloseWinnerModal}
131-
/>
132-
13373
<QuizResultsModal isVisible={showResultsModal} results={finalResults} onClose={handleCloseResultsModal} />
13474
</div>
13575
)

src/pages/game/HostPage.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
1-
import {useNavigate, useParams} from "react-router-dom"
1+
import {useParams} from "react-router-dom"
22
import QuizInfoCard from "../../layout/game/components/QuizInfoCard"
33
import GameSettings from "../../layout/game/components/GameSettings"
44
import {useRecoilValue} from "recoil";
55
import {
66
gameSettingAtom,
77
loginUserAtom,
88
playerListAtom,
9-
roomSettingAtom,
109
stompSendMessageAtom
1110
} from "../../state/atoms";
1211
import HostPageHeader from "./HostPageHeader";
1312
import {useEffect, useState} from "react";
1413
import clsx from "clsx";
14+
import {isEmptyOrNull} from "../../utils/utils";
1515

1616
function HostPage() {
1717
const { id: roomId } = useParams();
18-
const navigate = useNavigate();
1918
const [isReady, setReady] = useState(false);
2019
const sendMessage = useRecoilValue(stompSendMessageAtom);
2120
const gameSetting = useRecoilValue(gameSettingAtom);
22-
console.log(gameSetting);
2321

2422
//현재 로그인 유저를 찾고 방장인지 확인
2523
const playerList = useRecoilValue(playerListAtom);
2624
const loginUser = useRecoilValue(loginUserAtom);
2725
const matchingPlayers = playerList.filter(player => player.nickname === loginUser.name);
2826
const isHost = matchingPlayers.some(player => player.status === "host");
29-
const allReady = playerList.every(player => player.ready);
27+
const allReady = playerList.length > 1 && playerList.every(player => player.ready);
3028

3129
useEffect(() => {
32-
if (playerList) {
33-
setReady(matchingPlayers.ready);
30+
if (playerList && !isEmptyOrNull(matchingPlayers)) {
31+
setReady(matchingPlayers[0].ready);
3432
}
3533
}, [playerList])
3634

0 commit comments

Comments
 (0)