Skip to content

Commit 9e1cf2c

Browse files
committed
feat: 게임 결과 모달 추가
- 게임 종료 시 팀랭킹 및 개인 랭킹 추가
1 parent ceed642 commit 9e1cf2c

File tree

5 files changed

+126
-12
lines changed

5 files changed

+126
-12
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useRecoilValue } from "recoil";
2+
import { gameState } from "../../recoil/atoms/gameState";
3+
import { userState } from "../../recoil/atoms/userState";
4+
import { RoomDataProps } from "../../types/RoomData.type.ts";
5+
import { RoomClientProps } from "../../types/RoomClient.type.ts";
6+
7+
export default function IndividualRank() {
8+
const game = useRecoilValue<RoomDataProps | null>(gameState); // 게임 상태
9+
const user = useRecoilValue<RoomClientProps>(userState); // 사용자 상태
10+
11+
// 모든 팀의 유저를 단일 배열로 변환 및 클릭 수 기준 내림차순 정렬
12+
const rankedUsers = game?.teams
13+
.flatMap((team) =>
14+
team.users.map((teamUser) => ({
15+
nickname: teamUser.nickname,
16+
clickCount: teamUser.clickCount,
17+
isCurrentUser: teamUser.nickname === user.nickname,
18+
}))
19+
)
20+
.sort((a, b) => b.clickCount - a.clickCount)
21+
.slice(0, 3); // 상위 3명만 선택
22+
23+
return (
24+
<div className="w-full flex flex-col rounded-lg p-4 border-2 border-orange-300">
25+
<h2 className="text-xl font-semibold text-orange-500 mb-4 text-center">
26+
🏆 개인 랭킹
27+
</h2>
28+
29+
<ul className="space-y-3">
30+
{rankedUsers?.map((user, index) => (
31+
<li
32+
key={index}
33+
className={`flex justify-between items-center px-4 py-2 rounded-lg
34+
${
35+
user.isCurrentUser
36+
? " text-black font-bold border-2 border-orange-500 shadow-lg bg-orange-500"
37+
: " text-gray-600 border-2 border-gray-300 shadow-sm"
38+
}`}
39+
>
40+
<span className="text-lg">
41+
{index === 0 && '🥇 '}
42+
{index === 1 && '🥈 '}
43+
{index === 2 && '🥉 '}
44+
{user.nickname}</span>
45+
<span className="text-lg">{user.clickCount} clicks</span>
46+
</li>
47+
))}
48+
</ul>
49+
</div>
50+
);
51+
}
Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ import { gameState} from "../../recoil/atoms/gameState";
33
import { userState } from "../../recoil/atoms/userState";
44
import { RoomDataProps } from "../../types/RoomData.type.ts";
55
import { RoomClientProps } from "../../types/RoomClient.type.ts";
6+
import React from "react";
67

7-
export default function TeamRank() {
8+
type TeamRankProps = {
9+
resultModal: boolean;
10+
};
11+
12+
const TeamRank: React.FC<TeamRankProps> = ({resultModal})=> {
813
const game = useRecoilValue<RoomDataProps | null>(gameState); // 게임 상태
914
const user = useRecoilValue<RoomClientProps>(userState); // 사용자 상태
1015

@@ -22,27 +27,34 @@ export default function TeamRank() {
2227
.sort((a, b) => b.totalClicks - a.totalClicks).slice(0, 3);
2328

2429
return (
25-
<div className="w-full h-full rounded-lg p-4 border-2 border-orange-300">
26-
<h2 className="text-xl font-semibold text-orange-500 mb-4 text-center">
27-
🏆 Team Rankings
28-
</h2>
30+
<div
31+
className={`w-full ${resultModal ? '' : 'h-full'} rounded-lg p-4 border-2 border-orange-300 box-border`}
32+
>
33+
<h2 className="text-xl font-semibold text-orange-500 mb-4 text-center">🏆 팀 랭킹</h2>
2934

3035
<ul className="space-y-3">
3136
{rankedTeams?.map((team, index) => (
3237
<li
3338
key={index}
3439
className={`flex justify-between items-center px-4 py-2 rounded-lg
3540
${
36-
team.isUserTeam
37-
? " text-black font-bold border-2 border-orange-500 shadow-lg bg-orange-500"
38-
: " text-gray-600 border-2 border-gray-300 shadow-sm"
39-
}`}
41+
team.isUserTeam
42+
? ' text-black font-bold border-2 border-orange-500 shadow-lg bg-orange-500'
43+
: ' text-gray-600 border-2 border-gray-300 shadow-sm'
44+
}`}
4045
>
41-
<span className="text-lg">{index + 1}. {team.teamName}</span>
46+
<span className="text-lg">
47+
{index === 0 && '🥇 '}
48+
{index === 1 && '🥈 '}
49+
{index === 2 && '🥉 '}
50+
{team.teamName}
51+
</span>
4252
<span className="text-lg">{team.totalClicks} clicks</span>
4353
</li>
4454
))}
4555
</ul>
4656
</div>
4757
);
4858
}
59+
60+
export default TeamRank;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from "react";
2+
import TeamRank from "../Rank/TeamRank.tsx";
3+
import IndividualRank from "../Rank/IndividualRank.tsx";
4+
import { useNavigate } from "react-router-dom";
5+
6+
interface ResultModalProps {
7+
setResultModal: React.Dispatch<React.SetStateAction<boolean>>;
8+
}
9+
10+
const ResultModal: React.FC<ResultModalProps> = ({setResultModal}) => {
11+
const navigate = useNavigate();
12+
13+
const handleButtonClick = () =>{
14+
setResultModal(false);
15+
navigate('/');
16+
}
17+
18+
return (
19+
<>
20+
<div className="absolute inset-0 bg-black bg-opacity-50"></div>
21+
<div className="fixed inset-0 flex items-center justify-center z-50">
22+
<div className="flex flex-col gap-4 bg-white rounded-lg p-8 shadow-lg relative w-full max-w-4xl">
23+
<h2 className="text-2xl font-bold text-gray-800 mb-4">게임 결과</h2>
24+
<TeamRank resultModal={true} />
25+
<IndividualRank />
26+
<button
27+
className="px-4 py-4 flex items-center justify-center bg-orange-400 text-white rounded hover:bg-orange-500 transition"
28+
onClick={handleButtonClick}
29+
>
30+
홈 페이지로 가기
31+
</button>
32+
</div>
33+
</div>
34+
</>
35+
36+
);
37+
};
38+
39+
export default ResultModal;

src/pages/game/Game.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { useEffect, useState } from 'react';
22
import IndividualChart from '../../components/chart/IndividualChart';
33
import TeamChart from '../../components/chart/TeamChart';
4-
import TeamRank from '../../components/teamRank/TeamRank';
4+
import TeamRank from '../../components/Rank/TeamRank';
55
import WebSocketManager from '../../services/WebSocketManager.ts';
6+
import ResultModal from "../../components/modal/ResultModal.tsx";
67

78
const Game = () => {
89
const [count, setCount] = useState<number>(4);
910
const [moveMessage, setMoveMessage] = useState<boolean>(false);
11+
const [resultModal, setResultModal] = useState<boolean>(true);
1012
const webSocketManager = WebSocketManager.getInstance();
1113

1214
const startMessage = '🚀 Game Start! 🧑‍🚀';
1315
const second = 1000;
1416
const halfSecond = 500;
1517

1618
useEffect(() => {
19+
webSocketManager.setShowResultMessage(setResultModal);
1720
setMoveMessage(true);
1821
const handleCountdown = () => {
1922
setCount((prevCount) => {
@@ -47,6 +50,7 @@ const Game = () => {
4750

4851
return (
4952
<>
53+
{resultModal && <ResultModal setResultModal = {setResultModal}/>}
5054
{count > 0 ? (
5155
<div className="fixed inset-0 flex items-center justify-center z-50">
5256
<div className="absolute inset-0 bg-gray-500 bg-opacity-50"></div>
@@ -71,7 +75,7 @@ const Game = () => {
7175
<IndividualChart />
7276
</div>
7377
<div className="flex-1 h-3/4 p-4 md:h-1/2">
74-
<TeamRank />
78+
<TeamRank resultModal={false} />
7579
</div>
7680
</div>
7781
</div>

src/services/WebSocketManager.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class WebSocketManager {
1313
private navigate: NavigateFunction | null = null;
1414
private showMessage: ((state: any) => void) | null = null;
1515
private showRoomChiefLeaveMessage: ((state: any) => void) | null = null;
16+
private showResultMessage: ((state: any) => void) | null = null;
1617

1718
public static getInstance(): WebSocketManager {
1819
if (!WebSocketManager.instance) {
@@ -116,6 +117,9 @@ class WebSocketManager {
116117
console.log(`${message.type} 처리`);
117118
this.updateGameState(message.data);
118119
}
120+
if (this.showResultMessage) {
121+
this.showResultMessage(true);
122+
}
119123
break;
120124

121125
case 'ROOM_CHIEF_LEAVE':
@@ -154,6 +158,10 @@ class WebSocketManager {
154158
this.showRoomChiefLeaveMessage = showRoomChiefLeaveMessage;
155159
}
156160

161+
setShowResultMessage(showResultMessage: (state: any) => void): void {
162+
this.showResultMessage = showResultMessage;
163+
}
164+
157165
// 메시지 전송 메소드
158166
sendMessage(destination: string, body: Object = ''): void {
159167
if (!this.client || !this.client.connected) {

0 commit comments

Comments
 (0)