-
-
{
- setSearch(e.target.value);
- }}
- endAdornment={
-
-
-
- }
- />
+
+
void;
}) => {
const [data, setData] = useState(initialData);
const { accessToken } = useAuthStore();
@@ -33,6 +36,7 @@ const QuizInfoCard = ({
try {
const response: QuizInfo = await getQuizInfo(roomId, accessToken);
setData(response);
+ setRoomCode(response.code);
if (userStatuses.length >= response.maxPlayers) {
alert('방이 다 찼습니다. 로비로 이동합니다.');
router.push('/main-lobby');
@@ -45,9 +49,10 @@ const QuizInfoCard = ({
description: '',
numOfQuiz: 0,
isFull: false,
+ code: '',
});
alert(error.message);
- router.push('/main-lobby');
+ // router.push('/main-lobby');
}
};
if (accessToken && isSubscribed) {
diff --git a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/ReadyButton/ReadyButton.css.ts b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/ReadyButton/ReadyButton.css.ts
index 51ede9c9f..229ffd724 100644
--- a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/ReadyButton/ReadyButton.css.ts
+++ b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/ReadyButton/ReadyButton.css.ts
@@ -3,18 +3,27 @@ import { globals } from '@/app/globals.css';
export const readyContainer = style({
display: 'flex',
- flexDirection: 'column',
- gap: 30,
+ // flexDirection: 'column',
+ gap: 12,
alignItems: 'center',
- justifyContent: 'flex-end',
+ justifyContent: 'space-between',
width: '100%',
+ overflow: 'visible',
'@media': {
'(max-width: 768px)': {
- flexDirection: 'row',
+ padding: 4,
+ flexDirection: 'column',
+ overflow: 'clip',
+ height: '100%',
+ gap: 5,
},
},
});
+export const readyButtonWrapper = style({
+ width: '100%',
+});
+
export const readyStatus = style({
fontSize: 30,
fontFamily: 'var(--bagel-font)',
@@ -25,15 +34,6 @@ export const readyStatus = style({
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
- '@media': {
- '(max-width: 768px)': {
- // writingMode: 'vertical-rl',
- // textOrientation: 'upright',
- // whiteSpace: 'nowrap',
- // width: "100%",
- // marginLeft: '50vw',
- },
- },
});
const blinkingText = keyframes({
@@ -49,15 +49,17 @@ const blinkingText = keyframes({
});
export const buttonText = style({
- fontSize: 30,
+ fontSize: 28,
fontWeight: 600,
lineHeight: '44px',
minWidth: 70,
+ whiteSpace: 'nowrap',
+ textOverflow: 'ellipsis',
'@media': {
+ // '(max-width: '
'(max-width: 768px)': {
fontSize: 15,
- lineHeight: '22px',
- height: 110,
+ lineHeight: '50px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
@@ -68,3 +70,74 @@ export const buttonText = style({
export const blinking = style({
animation: `${blinkingText} 2s infinite`,
});
+
+export const shareButton = style({
+ height: 44,
+ '@media': {
+ '(max-width: 768px)': {
+ height: 20,
+ },
+ },
+});
+
+export const modalContent = style({
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ padding: '0 40px',
+});
+
+export const titleContainer = style({
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ width: 435,
+ marginBottom: 32,
+ '@media': {
+ '(max-width: 768px)': {
+ width: '100%',
+ },
+ },
+});
+
+export const titleWrapper = style({
+ width: '100%',
+ textAlign: 'center',
+ borderRadius: 16,
+ border: `4px solid ${globals.color.blue_main}`,
+ padding: 12,
+ fontSize: 20,
+ fontWeight: 600,
+ color: globals.color.black,
+ position: 'relative',
+ top: -5,
+});
+
+export const textContainer = style({
+ display: 'flex',
+ alignItems: 'center',
+ gap: 8,
+ textAlign: 'center',
+ marginBottom: 24,
+ color: globals.color.red,
+ fontWeight: 600,
+});
+
+export const inputWrapper = style({
+ width: 240,
+ '@media': {
+ '(max-width: 768px)': {
+ width: '100%',
+ },
+ },
+});
+
+export const copyButton = style({
+ padding: 10,
+ backgroundColor: globals.color.black_7,
+ border: `1px solid ${globals.color.black_6}`,
+ borderTopRightRadius: 12,
+ borderBottomRightRadius: 12,
+ cursor: 'pointer',
+ height: 60,
+});
diff --git a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/ReadyButton/ReadyButton.tsx b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/ReadyButton/ReadyButton.tsx
index 1c363831b..c900085a4 100644
--- a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/ReadyButton/ReadyButton.tsx
+++ b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/ReadyButton/ReadyButton.tsx
@@ -6,25 +6,26 @@ import useWaitingStore from '@/app/_store/useWaitingStore';
import { UserStatus } from '@/app/_types/WaitingStatus';
import stompClient from '../../../../_utils/stomp';
import * as styles from './ReadyButton.css';
-import { useRouter } from 'next/navigation';
-import { startQuiz } from '@/app/_api/quiz';
+import Image from 'next/image';
+import useModal from '@/app/_hooks/useModal';
+import TextInputField from '@/app/_components/TextInputField';
const ReadyButton = ({
roomId,
userId,
- isQuizReady,
- accessToken,
+ isConnected,
+ toggleBlock,
+ code,
}: {
roomId: number;
userId: number | undefined;
- isQuizReady: boolean;
- accessToken: string;
+ isConnected: boolean;
+ toggleBlock: boolean;
+ code: string;
}) => {
const { userStatuses, allUsersReady } = useWaitingStore();
const [isReady, setIsReady] = useState(false);
- const [countdown, setCountdown] = useState(3);
- const router = useRouter();
- const [toggleBlock, setToggleBlock] = useState(false);
+ const { Modal, isOpen, openModal, closeModal } = useModal();
useEffect(() => {
setIsReady(
@@ -34,65 +35,72 @@ const ReadyButton = ({
}, [userStatuses]);
const toggleReady = () => {
+ if (!isConnected) return;
stompClient.publish({
destination: `/pub/rooms/${roomId}/ready`,
});
};
- useEffect(() => {
- setCountdown(3);
- let timer: NodeJS.Timeout | null = null;
- if (isQuizReady && allUsersReady) {
- timer = setInterval(() => {
- setCountdown((prevCount) => {
- const nextCount = prevCount - 1;
- if (nextCount === 0) {
- setToggleBlock(true);
- // userStatuses에서 내 userId가 min값이면 퀴즈 시작
- const minUserId = userStatuses.reduce((prev, curr) =>
- prev.userId < curr.userId ? prev : curr,
- ).userId;
- if (minUserId === userId) {
- startQuiz(roomId, accessToken);
- }
- }
- if (nextCount === -1) {
- clearInterval(timer!);
- router.push(`/quiz-room/${roomId}`);
- }
- return nextCount;
- });
- }, 1000);
- }
- return () => {
- if (timer) clearInterval(timer);
- };
- }, [isQuizReady, allUsersReady]);
-
return (
-
- {isReady &&
- (isQuizReady && allUsersReady ? (
-
- {countdown > 0 ? countdown : '퀴즈 시작 !'}
-
- ) : (
-
- waiting
-
- ))}
-
-
- {isReady ? '준비취소' : '준비하기'}
-
-
-
+ <>
+
+
+
+
+ {isReady ? '준비취소' : '준비하기'}
+
+
+
+
+
+
+
+
+
+
+
+
+ navigator.clipboard.writeText(code)}
+ className={styles.copyButton}
+ >
+ 복사하기
+
+ }
+ />
+
+
+
+
+ >
);
};
diff --git a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserCard/UserCard.css.ts b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserCard/UserCard.css.ts
index e389933c9..b7e9ac7d3 100644
--- a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserCard/UserCard.css.ts
+++ b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserCard/UserCard.css.ts
@@ -81,6 +81,11 @@ export const chatMessage = style({
borderColor: 'white transparent',
zIndex: 5,
},
+ "@media" : {
+ "(max-width: 768px)": {
+ fontSize: 10,
+ },
+ }
});
export const profileImage = style({
diff --git a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserCard/UserCard.tsx b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserCard/UserCard.tsx
index e10f04e5b..3166e9249 100644
--- a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserCard/UserCard.tsx
+++ b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserCard/UserCard.tsx
@@ -18,11 +18,11 @@ const UserCard = ({
const [profileSize, setProfileSize] = useState(20);
useEffect(() => {
- console.log('cardHeight', cardHeight);
+ // console.log('cardHeight', cardHeight);
if (cardHeight > 350) {
setProfileSize(350 * 0.54);
} else if (cardHeight < 150) {
- console.log('profileSize', cardHeight * 0.4);
+ // console.log('profileSize', cardHeight * 0.4);
setProfileSize(cardHeight * 0.4);
} else setProfileSize(cardHeight * 0.54);
}, [cardHeight]);
diff --git a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserList/UserList.tsx b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserList/UserList.tsx
index 462dde679..03e621654 100644
--- a/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserList/UserList.tsx
+++ b/chatty-fe/src/app/(game)/waiting-room/[id]/_components/UserList/UserList.tsx
@@ -15,8 +15,10 @@ const UserList = ({ isQuizReady }: { isQuizReady: boolean }) => {
const [botStatus, setBotStatus] = useState({
userId: -1,
isReady: isQuizReady,
- nickname: '문제 생성 확인 봇',
- profileImage: null,
+ nickname: '퀴즈 생성 AI',
+ profileImage: isQuizReady === true
+ ? '/images/bot_ready.svg'
+ : '/images/bot_not_ready.svg',
});
const calculateCardSize = (
@@ -32,7 +34,7 @@ const UserList = ({ isQuizReady }: { isQuizReady: boolean }) => {
if (containerWidth / containerHeight > cardAspectRatio) {
numRows = Math.ceil(
Math.sqrt(
- numCards * (containerHeight / containerWidth) * (cardAspectRatio ** 2),
+ numCards * (containerHeight / containerWidth) * cardAspectRatio ** 2,
),
);
numCols = Math.ceil(numCards / numRows);
@@ -40,7 +42,8 @@ const UserList = ({ isQuizReady }: { isQuizReady: boolean }) => {
// 세로 길이가 더 긴 경우
numCols = Math.ceil(
Math.sqrt(
- (numCards * (containerWidth / containerHeight)) / (cardAspectRatio ** 2),
+ (numCards * (containerWidth / containerHeight)) /
+ cardAspectRatio ** 2,
),
);
numRows = Math.ceil(numCards / numCols);
@@ -84,7 +87,6 @@ const UserList = ({ isQuizReady }: { isQuizReady: boolean }) => {
containerSize.height - 49,
userStatuses.length + 1,
);
- console.log(calculatedSize);
setCardSize(calculatedSize);
}
}, [containerSize, userStatuses.length]);
@@ -95,7 +97,7 @@ const UserList = ({ isQuizReady }: { isQuizReady: boolean }) => {
userId: -1,
isReady: isQuizReady,
message: '나도 준비 완료!',
- nickname: '문제 생성 확인 봇',
+ nickname: '퀴즈 생성 AI',
profileImage: null,
});
}
diff --git a/chatty-fe/src/app/(game)/waiting-room/[id]/page.css.ts b/chatty-fe/src/app/(game)/waiting-room/[id]/page.css.ts
index 59554f46b..138e7f913 100644
--- a/chatty-fe/src/app/(game)/waiting-room/[id]/page.css.ts
+++ b/chatty-fe/src/app/(game)/waiting-room/[id]/page.css.ts
@@ -1,4 +1,5 @@
-import { style } from '@vanilla-extract/css';
+import { style, keyframes } from '@vanilla-extract/css';
+import { globals } from '@/app/globals.css';
export const roomContainer = style({
display: 'flex',
@@ -6,6 +7,7 @@ export const roomContainer = style({
maxWidth: 1830,
gap: 30,
height: '100%',
+ position: 'relative',
'@media': {
'(max-width: 768px)': {
position: 'relative',
@@ -26,6 +28,7 @@ export const wideArea = style({
maxWidth: 768,
width: '100%',
position: 'absolute',
+ gap: 12,
top: 170,
left: 0,
zIndex: 10,
@@ -91,7 +94,47 @@ export const buttonWrapper = style({
position: 'absolute',
right: 0,
width: 80,
- padding: 5,
+ height: "100%",
},
},
});
+
+export const readyStatus = style({
+ position: 'absolute',
+ top: 'calc(40% - 15px)',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: '100%',
+ height: 120,
+ background:
+ 'linear-gradient(90deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.4) 15%, rgba(255,255,255,0.7) 40%, rgba(255,255,255,0.7) 60%, rgba(255,255,255,0.4) 85%, rgba(255,255,255,0.2) 100%)',
+ zIndex: 10,
+});
+
+const blinkingText = keyframes({
+ '0%': {
+ opacity: 0,
+ },
+ '50%': {
+ opacity: 1,
+ },
+ '100%': {
+ opacity: 0,
+ },
+});
+export const blinking = style({
+ animation: `${blinkingText} 2s infinite`,
+});
+
+export const readyStatusText = style({
+ fontSize: 38,
+ fontFamily: 'var(--bagel-font)',
+ fontWeight: 400,
+ WebkitTextStrokeColor: globals.color.sub,
+ WebkitTextStrokeWidth: 2,
+ background: `linear-gradient(180deg, #FFF 30%, ${globals.color.sub} 100%)`,
+ backgroundClip: 'text',
+ WebkitBackgroundClip: 'text',
+ WebkitTextFillColor: 'transparent',
+});
diff --git a/chatty-fe/src/app/(game)/waiting-room/[id]/page.tsx b/chatty-fe/src/app/(game)/waiting-room/[id]/page.tsx
index 041455e24..6c5c37967 100644
--- a/chatty-fe/src/app/(game)/waiting-room/[id]/page.tsx
+++ b/chatty-fe/src/app/(game)/waiting-room/[id]/page.tsx
@@ -12,6 +12,9 @@ import useWaitingStore from '@/app/_store/useWaitingStore';
import ReadyButton from './_components/ReadyButton/ReadyButton';
import QuizSummaryCard from './_components/QuizSummaryCard/QuizSummaryCard';
import { usePathname, useSearchParams } from 'next/navigation';
+import { UserStatus } from '@/app/_types/WaitingStatus';
+import { startQuiz } from '@/app/_api/quiz';
+import { useRouter } from 'next/navigation';
const WaitingRoom = ({ params }: { params: { id: number } }) => {
const { id: userId } = useUserInfoStore();
@@ -21,12 +24,56 @@ const WaitingRoom = ({ params }: { params: { id: number } }) => {
const [isConnected, setIsConnected] = useState(false);
const [isSubscribed, setIsSubscribed] = useState(false);
const { accessToken } = useAuthStore();
- const { userStatuses, updateUsers, setMessage } = useWaitingStore();
+ const { userStatuses, updateUsers, setMessage, allUsersReady } =
+ useWaitingStore();
// 퀴즈 요약
const [quizSummary, setQuizSummary] = useState('');
// 퀴즈 생성 완료 체크
const [isQuizReady, setIsQuizReady] = useState(false);
+ const [isReady, setIsReady] = useState(false);
+ const [countdown, setCountdown] = useState(3);
+ const router = useRouter();
+ const [toggleBlock, setToggleBlock] = useState(false);
+ const [roomCode, setRoomCode] = useState('');
+
+ useEffect(() => {
+ setIsReady(
+ userStatuses.find((user: UserStatus) => user.userId === userId)
+ ?.isReady || false,
+ );
+ }, [userStatuses]);
+
+ useEffect(() => {
+ setCountdown(3);
+ let timer: NodeJS.Timeout | null = null;
+ if (isQuizReady && allUsersReady) {
+ timer = setInterval(() => {
+ setCountdown((prevCount) => {
+ const nextCount = prevCount - 1;
+ if (nextCount === 0) {
+ setToggleBlock(true);
+ // userStatuses에서 내 userId가 min값이면 퀴즈 시작
+ const minUserId = userStatuses.reduce((prev, curr) =>
+ prev.userId < curr.userId ? prev : curr,
+ ).userId;
+ if (minUserId === userId) {
+ startQuiz(params.id, accessToken);
+ }
+ }
+ if (nextCount === -1) {
+ clearInterval(timer!);
+ router.push(`/quiz-room/${params.id}`);
+ }
+ return nextCount;
+ });
+ }, 1000);
+ }
+ return () => {
+ if (timer) clearInterval(timer);
+ };
+ }, [isQuizReady, allUsersReady]);
+
useEffect(() => {
const subscribeToStatus = (roomId: number) => {
stompClient.subscribe(`/sub/rooms/${roomId}/status`, (message) => {
@@ -129,18 +176,36 @@ const WaitingRoom = ({ params }: { params: { id: number } }) => {
+ {isReady && (
+
+ {isQuizReady && allUsersReady ? (
+
+ {countdown > 0 ? countdown : '퀴즈 시작 !'}
+
+ ) : (
+
+ waiting
+
+ )}
+
+ )}
>
);
diff --git a/chatty-fe/src/app/_api/quiz.ts b/chatty-fe/src/app/_api/quiz.ts
index d16d84bf6..57b6551a1 100644
--- a/chatty-fe/src/app/_api/quiz.ts
+++ b/chatty-fe/src/app/_api/quiz.ts
@@ -9,8 +9,8 @@ export const getQuizInfo = async (id: number, accessToken: string) => {
});
return response.data;
} catch (error: any) {
- if (error.response.data.exceptionCode > 1000) {
- throw new Error(error.response.data.message);
+ if (error.response.exceptionCode > 1000) {
+ throw new Error(error.response.message);
} else {
console.error('error: ', error);
throw new Error(error);
@@ -52,3 +52,26 @@ export const getUsersQuiz = async (pageNum: number, token: string) => {
throw new Error('Failed to get user quiz');
}
};
+
+export const findByCode = async (code: string, token: string) => {
+ try {
+ const response = await client.post(
+ 'rooms/find-by-code',
+ {
+ code: code,
+ },
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ },
+ );
+ return response.data;
+ } catch (error: any) {
+ if (error.response.data.exceptionCode > 1000) {
+ throw new Error(error.response.data.message);
+ } else {
+ throw new Error('오류가 발생했습니다.');
+ }
+ }
+};
diff --git a/chatty-fe/src/app/_components/GradButton.css.ts b/chatty-fe/src/app/_components/GradButton.css.ts
index 2ae69ed0f..7b61692cb 100644
--- a/chatty-fe/src/app/_components/GradButton.css.ts
+++ b/chatty-fe/src/app/_components/GradButton.css.ts
@@ -24,9 +24,9 @@ export const gradButton = recipe({
color: {
primary: {
background: `linear-gradient(180deg, rgba(38, 146, 255, 0.10) 0%, #2692FF 77.5%), #eaf4ff`,
- boxShadow: '0px 2px 8px 2px rgba(23, 96, 171, 0.30)',
+ boxShadow: '0px 2px 4px 2px rgba(23, 96, 171, 0.30)',
':hover': {
- boxShadow: '0px 3px 8px 3px rgba(23, 96, 171, 0.30)',
+ boxShadow: '0px 3px 5px 3px rgba(23, 96, 171, 0.30)',
},
textShadow: `1px 1px 1px ${globals.color.blue_stroke},
-1px -1px 1px ${globals.color.blue_stroke},
@@ -36,9 +36,9 @@ export const gradButton = recipe({
secondary: {
background:
'linear-gradient(180deg, rgba(255, 181, 38, 0.10) 0%, #FFB526 77.5%), #fff8ec',
- boxShadow: '0px 2px 8px 2px rgba(169, 121, 29, 0.30)',
+ boxShadow: '0px 2px 4px 2px rgba(169, 121, 29, 0.30)',
':hover': {
- boxShadow: '0px 3px 8px 3px rgba(169, 121, 29, 0.30)',
+ boxShadow: '0px 3px 5px 3px rgba(169, 121, 29, 0.30)',
},
textShadow: `1px 1px 1px ${globals.color.sub_stroke},
-1px -1px 1px ${globals.color.sub_stroke},
diff --git a/chatty-fe/src/app/_components/Modal/Modal.css.ts b/chatty-fe/src/app/_components/Modal/Modal.css.ts
index 277770468..2ed2277d5 100644
--- a/chatty-fe/src/app/_components/Modal/Modal.css.ts
+++ b/chatty-fe/src/app/_components/Modal/Modal.css.ts
@@ -52,3 +52,7 @@ export const closeButton = style({
fontSize: 16,
fontWeight: '400',
});
+
+export const closeIconButton = style({
+ cursor: 'pointer',
+});
diff --git a/chatty-fe/src/app/_components/Modal/Modal.tsx b/chatty-fe/src/app/_components/Modal/Modal.tsx
index d0a4e4230..6081588dc 100644
--- a/chatty-fe/src/app/_components/Modal/Modal.tsx
+++ b/chatty-fe/src/app/_components/Modal/Modal.tsx
@@ -73,7 +73,10 @@ const Modal = ({
{timer}
) : (
-