diff --git a/packages/client/src/app/routes/Router.tsx b/packages/client/src/app/routes/Router.tsx index b1066da4..0007b46d 100644 --- a/packages/client/src/app/routes/Router.tsx +++ b/packages/client/src/app/routes/Router.tsx @@ -6,7 +6,6 @@ import QuizCreatePage from '@/pages/quiz-create'; import GuestLayout from '@/app/layouts/GuestLayout'; import NotFound from '@/app/routes/NotFound'; import QuizSession from '@/pages/quiz-session'; -import QuizWait from '@/pages/quiz-wait'; import Nickname from '@/pages/nickname'; import QuizQuestion from '@/pages/quiz-question'; import QnA from '@/pages/qna'; @@ -14,6 +13,8 @@ import GuestQnA from '@/pages/guest-qna'; import QuizMasterSession from '@/pages/quiz-master-session'; import Leaderboard from '@/pages/leaderboard'; import QuizListPage from '@/pages/quiz-list'; +import QuizWaitPage from '@/pages/quiz-wait'; +import PreventGuestRouter from './PreventGuestRouter'; export default function Router() { return ( @@ -25,10 +26,11 @@ export default function Router() { } /> }> - } /> - } /> - } /> - + }> + } /> + } /> + } /> + } /> } /> diff --git a/packages/client/src/pages/main/index.tsx b/packages/client/src/pages/main/index.tsx index 24998c08..d575008b 100644 --- a/packages/client/src/pages/main/index.tsx +++ b/packages/client/src/pages/main/index.tsx @@ -1,5 +1,5 @@ import { toastController } from '@/features/toast/model/toastController'; -import { getPincodeExist } from '@/shared/api/games'; +import { getPincodeExist, checkPincodePossible } from '@/shared/api/games'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import FloatingSquare from './ui/FloatingSquare'; @@ -16,8 +16,15 @@ export default function MainPage() { return; } const response = await getPincodeExist(pinCode); + if (response.isExist) { - navigate(`/nickname/${pinCode}`); + const checkResponse = await checkPincodePossible(pinCode); + console.log(checkResponse); + if (checkResponse.isPossible) { + navigate(`/nickname/${pinCode}`); + } else { + toast.warning('방이 가득 찼습니다.'); + } return; } toast.error('잘못된 코드입니다.'); diff --git a/packages/client/src/pages/nickname/index.tsx b/packages/client/src/pages/nickname/index.tsx index 14f5ef93..0c77d343 100644 --- a/packages/client/src/pages/nickname/index.tsx +++ b/packages/client/src/pages/nickname/index.tsx @@ -1,31 +1,34 @@ +import { useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; + import TrophyIcon from '@/shared/assets/icons/tropyhy.svg?react'; import AvatarIcon from '@/shared/assets/icons/avatar.svg?react'; -import { useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { getCookie, setCookie } from '@/shared/utils/cookie'; +import { setCookie } from '@/shared/utils/cookie'; import { getQuizSocket } from '@/shared/utils/socket'; +import { emitEventWithAck } from '@/shared/utils/emitEventWithAck'; +import { toastController } from '@/features/toast/model/toastController'; const MAX_NICKNAME_LENGTH = 12; export default function Nickname() { const { pinCode } = useParams(); - const [nickname, setNickname] = useState(''); const navigate = useNavigate(); + const toast = toastController(); + const [nickname, setNickname] = useState(''); - const handleNicknameSubmit = (nickname: string) => { + const handleNicknameSubmit = async (nickname: string) => { const socket = getQuizSocket(); - const sid = getCookie('sid'); - if (sid) { - socket.emit('participant re-entry', { pinCode: pinCode, nickname: nickname, sid: sid }); - navigate(`/quiz/wait/${pinCode}`); - return; - } - socket.emit('participant entry', { pinCode: pinCode, nickname: nickname }); - - socket.on('session', (response) => { - setCookie('sid', response); + const sid = await emitEventWithAck(socket, 'session', { + pinCode: pinCode, + nickname: nickname, }); + if (!sid) { + toast.warning('방이 가득 찼습니다.'); + navigate(`/`); + } + setCookie('sid', sid); + socket.emit('participant notice', { pinCode: pinCode }); navigate(`/quiz/wait/${pinCode}`); }; diff --git a/packages/client/src/pages/quiz-wait/index.lazy.tsx b/packages/client/src/pages/quiz-wait/index.lazy.tsx new file mode 100644 index 00000000..c9c192e0 --- /dev/null +++ b/packages/client/src/pages/quiz-wait/index.lazy.tsx @@ -0,0 +1,122 @@ +import { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { Copy, Users, PlayCircle } from 'lucide-react'; + +import { getQuizSocket } from '@/shared/utils/socket'; +import { getCookie } from '@/shared/utils/cookie'; +import { toastController } from '@/features/toast/model/toastController'; +import { apiClient } from '@/shared/api'; +import UserGridView from './ui/UserGridView'; +import { useNickname } from './model/hooks/useNickname'; + +export interface Guest { + nickname: string; + character: number; + position: number; +} + +export default function QuizWaitLazyPage() { + const socket = getQuizSocket(); + const { pinCode } = useParams(); + const { + data: { participantList, myPosition }, + refetch, + } = useNickname(socket, pinCode ?? '', getCookie('sid') ?? ''); + const [userType, setUserType] = useState(''); + + const guestLink = `${import.meta.env.VITE_CLIENT_URL}/nickname/${pinCode}`; + const toast = toastController(); + + const navigate = useNavigate(); + + useEffect(() => { + const handleParticipantNotice = () => { + refetch(); + }; + socket.on('participant notice', handleParticipantNotice); + + const fetchUserType = async () => { + const response = await apiClient.get(`/games/${pinCode}/sid/${getCookie('sid')}`); + setUserType(response.type); + }; + + socket.on('start quiz', (response) => { + console.log('start quiz', response); + navigate(`/quiz/session/${pinCode}/1`); + }); + + fetchUserType(); + + return () => { + socket.off('participant notice', handleParticipantNotice); + }; + }, []); + + const handleCopyLink = () => { + try { + navigator.clipboard.writeText(guestLink); + toast.success('링크가 복사되었습니다.'); + } catch (error) { + if (error instanceof Error) { + const errorMessage = error.message; + toast.error(errorMessage); + } + } + }; + + const handleQuizStart = () => { + socket.emit('start quiz', { sid: getCookie('sid'), pinCode }); + navigate(`/quiz/session/host/${pinCode}/1`); + }; + + return ( +
+
+
+
+
+
+ Pin : + {pinCode} +
+
+ + 링크 복사하기 +
+
+ +

+ / 입력 시 채팅을 칠 수 있어요. +

+ +

+

+ + {participantList.length}명 +
+
+
+ 참가 대기중 +
+

+
+ +
+ {userType === 'master' && ( +
+ +
+ )} +
+
+ ); +} diff --git a/packages/client/src/pages/quiz-wait/index.tsx b/packages/client/src/pages/quiz-wait/index.tsx index df3df71b..a5fa59d8 100644 --- a/packages/client/src/pages/quiz-wait/index.tsx +++ b/packages/client/src/pages/quiz-wait/index.tsx @@ -1,129 +1,10 @@ -import { useEffect, useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { Copy, Users, PlayCircle } from 'lucide-react'; - -import { getQuizSocket } from '@/shared/utils/socket'; -import { getCookie } from '@/shared/utils/cookie'; -import { toastController } from '@/features/toast/model/toastController'; -import { apiClient } from '@/shared/api'; -import UserGridView from './ui/UserGridView'; - -export interface Guest { - nickname: string; - character: number; - position: number; -} - -export default function QuizWait() { - const { pinCode } = useParams(); - const [userType, setUserType] = useState(''); - const [guests, setGuests] = useState([]); - const [myPosition, setMyPosition] = useState(-1); - - const guestLink = `${import.meta.env.VITE_CLIENT_URL}/nickname/${pinCode}`; - const socket = getQuizSocket(); - const toast = toastController(); - - const navigate = useNavigate(); - - useEffect(() => { - const handleMyPosition = (response: any) => { - const { participantList, myPosition } = response; - setGuests(participantList); - setMyPosition(myPosition); - }; - - socket.on('my position', handleMyPosition); - - const handleNickname = (response: any) => { - const { participantList } = response; - setGuests(participantList); - }; - - socket.on('nickname', handleNickname); - - const fetchUserType = async () => { - const response = await apiClient.get(`/games/${pinCode}/sid/${getCookie('sid')}`); - setUserType(response.type); - }; - - socket.on('start quiz', (response) => { - console.log('start quiz', response); - navigate(`/quiz/session/${pinCode}/1`); - }); - - fetchUserType(); - - return () => { - socket.off('nickname', handleNickname); - }; - }, []); - - const handleCopyLink = () => { - try { - navigator.clipboard.writeText(guestLink); - toast.success('링크가 복사되었습니다.'); - } catch (error) { - if (error instanceof Error) { - const errorMessage = error.message; - toast.error(errorMessage); - } - } - }; - - const handleQuizStart = () => { - socket.emit('start quiz', { sid: getCookie('sid'), pinCode }); - navigate(`/quiz/session/host/${pinCode}/1`); - }; +import AsyncBoundary from '@/shared/boundary/AsyncBoundary'; +import QuizWaitLazyPage from './index.lazy'; +export default function QuizWaitPage() { return ( -
-
-
-
-
-
- Pin : - {pinCode} -
-
- - 링크 복사하기 -
-
- -

- / 입력 시 채팅을 칠 수 있어요. -

- -

-

- - {guests.length}명 -
-
-
- 참가 대기중 -
-

-
- -
- {userType === 'master' && ( -
- -
- )} -
-
+ + + ); } diff --git a/packages/client/src/pages/quiz-wait/model/hooks/useNickname.ts b/packages/client/src/pages/quiz-wait/model/hooks/useNickname.ts new file mode 100644 index 00000000..3f393f68 --- /dev/null +++ b/packages/client/src/pages/quiz-wait/model/hooks/useNickname.ts @@ -0,0 +1,17 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; + +import { emitEventWithAck } from '@/shared/utils/emitEventWithAck'; +import { Socket } from 'socket.io-client'; +import { Guest } from '../../index.lazy'; + +interface NicknameResponse { + participantList: Guest[]; + myPosition: number; +} + +export const useNickname = (socket: Socket, pinCode: string, sid: string) => { + return useSuspenseQuery({ + queryKey: ['participant info', pinCode], + queryFn: () => emitEventWithAck(socket, 'participant info', { pinCode, sid }), + }); +}; diff --git a/packages/client/src/pages/quiz-wait/ui/UserGridItem.tsx b/packages/client/src/pages/quiz-wait/ui/UserGridItem.tsx index 4b4f9fa4..fcb11b96 100644 --- a/packages/client/src/pages/quiz-wait/ui/UserGridItem.tsx +++ b/packages/client/src/pages/quiz-wait/ui/UserGridItem.tsx @@ -1,7 +1,7 @@ import { useRef, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { Guest } from '../index'; +import { Guest } from '../index.lazy'; import { getQuizSocket } from '@/shared/utils/socket'; import DogImage from '@/shared/assets/characters/강아지.png'; diff --git a/packages/client/src/pages/quiz-wait/ui/UserGridView.tsx b/packages/client/src/pages/quiz-wait/ui/UserGridView.tsx index 646bcc4a..3d3a21df 100644 --- a/packages/client/src/pages/quiz-wait/ui/UserGridView.tsx +++ b/packages/client/src/pages/quiz-wait/ui/UserGridView.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; -import { Guest } from '../index'; +import { Guest } from '../index.lazy'; import UserGridItem from './UserGridItem'; import { getQuizSocket } from '@/shared/utils/socket'; diff --git a/packages/client/src/shared/api/games/index.ts b/packages/client/src/shared/api/games/index.ts index 67619a66..e7740c54 100644 --- a/packages/client/src/shared/api/games/index.ts +++ b/packages/client/src/shared/api/games/index.ts @@ -3,3 +3,7 @@ import { apiClient } from '..'; export function getPincodeExist(pinCode: string) { return apiClient.get(`/games/${pinCode}`); } + +export function checkPincodePossible(pinCode: string) { + return apiClient.get(`/games/${pinCode}/check`); +}