{
+ const { showToast } = useToastStore.getState();
const [isHover, setIsHover] = useState(false);
const [isClicked, setClicked] = useState(false);
const handleClick = (e) => {
- setClicked(!isClicked);
- onClick?.(e); // 기존 클릭 핸들러 호출
+ if (onClick(e)) {
+ setClicked(!isClicked);
+ return;
+ }
+ showToast('alert', '모든 플레이어가 준비되어야 합니다!');
};
return (
@@ -30,13 +35,13 @@ const GameStartButton = ({ onClick }) => {
diff --git a/src/components/lobby/waiting/WaitingRoom.jsx b/src/components/lobby/waiting/WaitingRoom.jsx
index d4ef4a73..552d19b4 100644
--- a/src/components/lobby/waiting/WaitingRoom.jsx
+++ b/src/components/lobby/waiting/WaitingRoom.jsx
@@ -1,24 +1,28 @@
-import CheckBox from "commons/svgs/CheckBox";
-import GameReadyButton from "commons/svgs/GameReadyButton";
-import GameStartButton from "commons/svgs/GameStartButton";
-import PlayerSlot from "components/lobby/waiting/PlayerSlot";
-import useWaitingRoomSocket from "hooks/waiting-room/useWaitingRoomSocket";
-import { useEffect, useState } from "react";
-import { useLocation, useNavigate } from "react-router-dom";
-import { getRoomDetailAPI } from "services/lobby/waiting/waitingRoom";
-import "styles/components/lobby/waiting/WaitingRoom.scss";
-import { getUserUuid } from "utils/user";
+import CheckBox from 'commons/svgs/CheckBox';
+import GameReadyButton from 'commons/svgs/GameReadyButton';
+import GameStartButton from 'commons/svgs/GameStartButton';
+import PlayerSlot from 'components/lobby/waiting/PlayerSlot';
+import useWaitingRoomSocket from 'hooks/waiting-room/useWaitingRoomSocket';
+import { useEffect, useRef, useState } from 'react';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { getRoomDetailAPI } from 'services/lobby/waiting/waitingRoom';
+import { useToastStore } from 'store/toast';
+import 'styles/components/lobby/waiting/WaitingRoom.scss';
+import { getUserUuid } from 'utils/user';
const WaitingRoom = () => {
+ const { showToast } = useToastStore.getState();
const navigate = useNavigate();
const location = useLocation();
const userUuid = getUserUuid();
const [roomId, setRoomId] = useState(null); // 방번호
const [roomInfo, setRoomInfo] = useState(null); // 방 전체 정보
- const [selectedDifficulty, setSelectedDifficulty] = useState("BASIC"); // 난이도
+ const [selectedDifficulty, setSelectedDifficulty] = useState(null); // 난이도
const [isReady, setIsReady] = useState(false); // 준비 여부
const [isOwner, setIsOwner] = useState(false);
+ const ownerIsMe = userUuid === roomInfo?.roomInfo.ownerUuid;
+ const userInfos = roomInfo?.userInfos || [];
const {
startGame,
@@ -35,7 +39,7 @@ const WaitingRoom = () => {
// 최초 방정보 갱신
useEffect(() => {
const params = new URLSearchParams(location.search);
- const roomIdFromQuery = params.get("roomId");
+ const roomIdFromQuery = params.get('roomId');
if (roomIdFromQuery) {
setRoomId(roomIdFromQuery);
@@ -54,13 +58,14 @@ const WaitingRoom = () => {
}
};
+ const isFirstDifficultyInit = useRef(true);
// 방정보 갱신 시 데이터 분배
useEffect(() => {
if (!roomInfo) return;
// 내 정보 조회
const myState = roomInfo.userInfos.find(
- (user) => user.userUuid === userUuid
+ (user) => user.userUuid === userUuid,
);
const isMeOwner = roomInfo.roomInfo.ownerUuid === userUuid;
@@ -69,8 +74,20 @@ const WaitingRoom = () => {
// 레디 여부 적용
setIsReady(myState?.ready);
+
+ // 최초 난이도 세팅이면 토스트 스킵
+ if (isFirstDifficultyInit.current) {
+ isFirstDifficultyInit.current = false;
+ setSelectedDifficulty(roomInfo.roomInfo.difficulty);
+ return;
+ }
+
+ if (roomInfo.roomInfo.difficulty === selectedDifficulty) {
+ return;
+ }
// 난이도 적용
setSelectedDifficulty(roomInfo.roomInfo.difficulty);
+ showToast('alert', '로봇 성능이 변경되었습니다!');
}, [roomInfo]);
// 게임 시작
@@ -83,12 +100,14 @@ const WaitingRoom = () => {
// 방 나가기
const quitRoomHandler = () => {
quitRoom(roomId);
- navigate("/lobby");
+ navigate('/lobby');
};
// 게임 시작 요청
const gameStartHandler = () => {
+ if (!userInfos.every((user) => user.ready)) return false;
startGame(roomId);
+ return true;
};
// 게임 준비/취소
@@ -101,7 +120,10 @@ const WaitingRoom = () => {
// 난이도 변경
const updateDifficultyHandler = (difficulty) => {
- if (!isOwner) return;
+ if (!isOwner) {
+ showToast('alert', '방장만 로봇 성능을 변경할 수 있습니다!');
+ return;
+ }
if (difficulty === selectedDifficulty) return;
setSelectedDifficulty(difficulty);
@@ -150,24 +172,24 @@ const WaitingRoom = () => {
updateDifficultyHandler("BASIC")}
+ checked={selectedDifficulty === 'BASIC'}
+ onChange={() => updateDifficultyHandler('BASIC')}
/>
updateDifficultyHandler("ADVANCED")}
+ checked={selectedDifficulty === 'ADVANCED'}
+ onChange={() => updateDifficultyHandler('ADVANCED')}
/>
- {userUuid === roomInfo.roomInfo.ownerUuid ? (
+ {ownerIsMe ? (
) : (
)}
diff --git a/src/components/scenes/game1/BattleScene.jsx b/src/components/scenes/game1/BattleScene.jsx
index 754b67df..c56f1ad0 100644
--- a/src/components/scenes/game1/BattleScene.jsx
+++ b/src/components/scenes/game1/BattleScene.jsx
@@ -1,17 +1,17 @@
-import leftCloudImg from "assets/components/scenes/commons/left-cloud.png";
-import rightCloudImg from "assets/components/scenes/commons/right-cloud.png";
-import aiImg from "assets/components/scenes/game1/ai.png";
-import userImg from "assets/components/scenes/game1/player.png";
-import Timer from "commons/Timer";
-import { correctSFX, game1BattleBGM, incorrectSFX } from "constants/audio";
-import useAudio from "hooks/audio/useAudio";
-import useBattle from "hooks/game/game1/useBattle";
+import leftCloudImg from 'assets/components/scenes/commons/left-cloud.png';
+import rightCloudImg from 'assets/components/scenes/commons/right-cloud.png';
+import aiImg from 'assets/components/scenes/game1/ai.png';
+import userImg from 'assets/components/scenes/game1/player.png';
+import Timer from 'commons/Timer';
+import { correctSFX, game1BattleBGM, incorrectSFX } from 'constants/audio';
+import useAudio from 'hooks/audio/useAudio';
+import useBattle from 'hooks/game/game1/useBattle';
// import useEffectSound from "hooks/game/game1/useEffectSound";
-import { useEffect, useRef, useState } from "react";
-import { useToastStore } from "store/toast";
-import "styles/components/scenes/game1/BattleScene.scss";
-import { isPressEnterKey } from "utils/keyDown";
-import { isBlank } from "utils/validation";
+import { useEffect, useRef, useState } from 'react';
+import { useToastStore } from 'store/toast';
+import 'styles/components/scenes/game1/BattleScene.scss';
+import { isPressEnterKey } from 'utils/keyDown';
+import { isBlank } from 'utils/validation';
const BattleScene = ({ roomId, drawings }) => {
// 배틀 씬 guess 상태 관리 훅 호출
@@ -29,7 +29,7 @@ const BattleScene = ({ roomId, drawings }) => {
const { showToast } = useToastStore.getState();
const inputRef = useRef(null);
- const [inputValue, setInputValue] = useState("");
+ const [inputValue, setInputValue] = useState('');
const [aiSaying, setAiSaying] = useState(true);
useAudio(game1BattleBGM);
@@ -37,7 +37,7 @@ const BattleScene = ({ roomId, drawings }) => {
// const { playEffect } = useEffectSound();
useEffect(() => {
- showToast("gamealert", "AI가 맞출 차례입니다!");
+ showToast('gamealert', 'AI가 맞출 차례입니다!');
}, []);
useEffect(() => {
@@ -50,7 +50,7 @@ const BattleScene = ({ roomId, drawings }) => {
return () => clearTimeout(timer);
} else {
// ai 턴으로 이동
- setInputValue("");
+ setInputValue('');
setAiSaying(true);
}
}, [isAiguessTurn]);
@@ -66,11 +66,11 @@ const BattleScene = ({ roomId, drawings }) => {
if (guessResult === null) return;
if (guessResult) {
- showToast("gameO");
+ showToast('gameO');
playTrack(correctSFX);
// playEffect("correct");
} else {
- showToast("gameX");
+ showToast('gameX');
playTrack(incorrectSFX);
// playEffect("incorrect");
}
@@ -78,11 +78,11 @@ const BattleScene = ({ roomId, drawings }) => {
const handleKeyDown = (e) => {
if (isSubmit) return;
- if (isPressEnterKey(e)) {
+ if (isPressEnterKey(e) && e.nativeEvent.isComposing === false) {
e.preventDefault();
if (!isBlank(inputValue)) {
- inputRef.current?.blur();
sendGuess(inputValue);
+ inputRef.current?.blur();
}
}
};
diff --git a/src/hooks/game/game1/useBattle.js b/src/hooks/game/game1/useBattle.js
index 34dbc3f9..f3d95f6b 100644
--- a/src/hooks/game/game1/useBattle.js
+++ b/src/hooks/game/game1/useBattle.js
@@ -1,8 +1,8 @@
-import { SOCKET_GAME_API } from "constants/api";
-import { useCallback, useEffect, useState } from "react";
-import { useGameSocketStore } from "store/socket";
-import { useToastStore } from "store/toast";
-import { getUserUuid } from "utils/user";
+import { SOCKET_GAME_API } from 'constants/api';
+import { useCallback, useEffect, useState } from 'react';
+import { useGameSocketStore } from 'store/socket';
+import { useToastStore } from 'store/toast';
+import { getUserUuid } from 'utils/user';
const useBattle = ({ roomId }) => {
const userUuid = getUserUuid();
@@ -25,28 +25,29 @@ const useBattle = ({ roomId }) => {
(message) => {
const payload = JSON.parse(message.body);
const { eventType: type, data } = payload;
+ const aiSay = JSON.parse(payload.aiSays)?.message;
switch (type) {
- case "GUESS_REQUEST":
+ case 'GUESS_REQUEST':
setEndTime(data.guessEndTime);
setGuessResult(null);
setIsMyguessTurn(data.guesserUuid === userUuid);
- setIsAiguessTurn(data.guesserUuid === "AI");
+ setIsAiguessTurn(data.guesserUuid === 'AI');
setIsSubmit(false);
break;
- case "GUESS_SUBMIT":
+ case 'GUESS_SUBMIT':
setGuessWord(data.guessWord);
- setAiSays(payload.aiSays);
+ setAiSays(aiSay);
break;
- case "GUESS_RESULT":
+ case 'GUESS_RESULT':
setGuessResult(data.correct);
- setAiSays(payload.aiSays);
+ setAiSays(aiSay);
setGuessWord(null);
break;
default:
break;
}
- }
+ },
);
return () => sub.unsubscribe();
@@ -56,17 +57,17 @@ const useBattle = ({ roomId }) => {
(guess) => {
if (!isConnected) return;
setIsSubmit(true);
- showToast("alert", "제출 완료!");
+ showToast('alert', '제출 완료!');
stompClient.publish({
destination: `/pub${SOCKET_GAME_API}/${roomId}`,
body: JSON.stringify({
- eventType: "GUESS_SUBMIT",
+ eventType: 'GUESS_SUBMIT',
data: { guessWord: guess },
}),
});
},
- [isConnected, stompClient, roomId]
+ [isConnected, stompClient, roomId],
);
return {
diff --git a/src/hooks/waiting-room/useWaitingRoomSocket.js b/src/hooks/waiting-room/useWaitingRoomSocket.js
index e9e730c6..c1a4dde8 100644
--- a/src/hooks/waiting-room/useWaitingRoomSocket.js
+++ b/src/hooks/waiting-room/useWaitingRoomSocket.js
@@ -1,8 +1,8 @@
-import { SOCKET_ROOM_API, SOCKET_ROOM_ERROR_API } from "constants/api";
-import { useEffect, useState } from "react";
-import { useNavigate } from "react-router";
-import { useGameSocketStore } from "store/socket";
-import { useToastStore } from "store/toast";
+import { SOCKET_ROOM_API, SOCKET_ROOM_ERROR_API } from 'constants/api';
+import { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router';
+import { useGameSocketStore } from 'store/socket';
+import { useToastStore } from 'store/toast';
/**
* useWaitingRoom 커스텀 훅
@@ -10,19 +10,19 @@ import { useToastStore } from "store/toast";
*/
const roomExitEX = {
- eventType: "EXIT",
+ eventType: 'EXIT',
};
const gameStartEX = {
- eventType: "START",
+ eventType: 'START',
};
const gameReadyEX = {
- eventType: "READY",
+ eventType: 'READY',
};
const gameUnreadyEX = {
- eventType: "UNREADY",
+ eventType: 'UNREADY',
};
const useWaitingRoomSocket = ({ roomId, userUuid, setRoomInfo }) => {
@@ -30,18 +30,19 @@ const useWaitingRoomSocket = ({ roomId, userUuid, setRoomInfo }) => {
const { stompClient, isConnected } = useGameSocketStore();
const [isGameStart, setIsGameStart] = useState(false);
const [initGameInfo, setInitGameInfo] = useState(null);
+
const { showToast } = useToastStore.getState();
useEffect(() => {
if (roomId === null) return;
if (!isConnected) return;
- console.log("방 입장 : " + roomId);
+ console.log('방 입장 : ' + roomId);
const roomErrorSub = stompClient.subscribe(
`${SOCKET_ROOM_ERROR_API}/${userUuid}`,
(message) => {
const payload = JSON.parse(message.body);
- console.log("방 에러:", payload);
- }
+ console.log('방 에러:', payload);
+ },
);
// 🔔 방 이벤트 여부 구독
@@ -49,57 +50,57 @@ const useWaitingRoomSocket = ({ roomId, userUuid, setRoomInfo }) => {
`/sub${SOCKET_ROOM_API}/${roomId}`,
(message) => {
const payload = JSON.parse(message.body);
- console.log("방 이벤트:", payload);
+ console.log('방 이벤트:', payload);
const eventType = payload.type;
const data = payload.data;
switch (eventType) {
- case "READY":
- console.log("READY");
+ case 'READY':
+ console.log('READY');
setRoomInfo((prev) => ({
...prev,
userInfos: prev.userInfos.map((user) =>
- user.userUuid === data ? { ...user, ready: true } : user
+ user.userUuid === data ? { ...user, ready: true } : user,
),
}));
break;
- case "UNREADY":
- console.log("UNREADY");
+ case 'UNREADY':
+ console.log('UNREADY');
setRoomInfo((prev) => ({
...prev,
userInfos: prev.userInfos.map((user) =>
- user.userUuid === data ? { ...user, ready: false } : user
+ user.userUuid === data ? { ...user, ready: false } : user,
),
}));
break;
- case "START":
- console.log("START");
+ case 'START':
+ console.log('START');
setIsGameStart(true);
setInitGameInfo(data.gameData);
break;
- case "UPDATE":
- console.log("UPDATE");
+ case 'UPDATE':
+ console.log('UPDATE');
setRoomInfo(data);
break;
- case "JOIN":
- console.log("JOIN");
+ case 'JOIN':
+ console.log('JOIN');
setRoomInfo((prev) => ({
...prev,
userInfos: data,
}));
break;
- case "EXIT":
- console.log("EXIT");
+ case 'EXIT':
+ console.log('EXIT');
setRoomInfo((prev) => ({
...prev,
userInfos: prev.userInfos.filter((user) => user.userUuid != data),
}));
break;
- case "KICK":
- console.log("KICK");
+ case 'KICK':
+ console.log('KICK');
if (data == userUuid) {
- navigate("/lobby");
- showToast("alert", "나~~가 ㅋㅋ");
+ navigate('/lobby');
+ showToast('alert', '나~~가 ㅋㅋ');
}
setRoomInfo((prev) => ({
...prev,
@@ -110,7 +111,7 @@ const useWaitingRoomSocket = ({ roomId, userUuid, setRoomInfo }) => {
default:
break;
}
- }
+ },
);
// 🔕 대기방 이벤트 구독 해제
@@ -159,7 +160,7 @@ const useWaitingRoomSocket = ({ roomId, userUuid, setRoomInfo }) => {
// body
const roomUpdateEX = {
- eventType: "UPDATE",
+ eventType: 'UPDATE',
content: JSON.stringify(updateRoomDTO),
};
diff --git a/src/pages/game/Game1CreatePage.jsx b/src/pages/game/Game1CreatePage.jsx
index 0bc08fe2..351b4ba3 100644
--- a/src/pages/game/Game1CreatePage.jsx
+++ b/src/pages/game/Game1CreatePage.jsx
@@ -119,6 +119,7 @@ const Game1CreatePage = () => {
type="text"
placeholder="방 제목을 입력해주세요."
value={title}
+ maxLength={50}
onChange={(e) => setTitle(e.target.value)}
/>
{titleError && {titleError}}