Skip to content

Commit

Permalink
Merge pull request #125 from prgrms-web-devcourse-final-project/Feat/#โ€ฆ
Browse files Browse the repository at this point in the history
โ€ฆ118-InGameWS

Feat/#118 in game ws
  • Loading branch information
bluedog129 authored Dec 9, 2024
2 parents 4978a34 + bfd42cf commit 43d16e6
Show file tree
Hide file tree
Showing 18 changed files with 844 additions and 409 deletions.
65 changes: 65 additions & 0 deletions app/(nav)/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,68 @@ const handleRegularNotification = (notification: NotificationMessage) => {
}
);
};
// notification: NotificationMessage,
// router: AppRouterInstance,
// addMessageFn: (message: any) => void
// ) => {
// toast.custom(
// (t) => (
// <InviteToast
// message={`${notification.groupName}์—์„œ ${notification.senderNickName}๋‹˜์ด ${notification.action}`}
// onAccept={async () => {
// const stompClient = new Client({
// brokerURL: `${process.env.NEXT_PUBLIC_GROUP_WS_URL}/group-service/connect`,
// });

// // ์—ฐ๊ฒฐ ์‹œ๋„ ์ „์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์„ค์ •
// stompClient.onConnect = () => {
// console.log('WebSocket ์—ฐ๊ฒฐ ์„ฑ๊ณต');

// // ๊ตฌ๋… ์„ค์ •
// const subscription = stompClient.subscribe(
// `${process.env.NEXT_PUBLIC_GROUP_SUBSCRIBE}/${notification.groupId}`,
// (message: { body: string }) => {
// console.log('Received message:', message.body);
// const parsedMessage = JSON.parse(message.body);
// addMessageFn({
// ...parsedMessage,
// timestamp: Date.now(),
// });
// }
// );
// console.log('๊ตฌ๋… ์„ค์ • ์™„๋ฃŒ');
// };

// stompClient.onConnect = () => {
// stompClient.subscribe(
// `${process.env.NEXT_PUBLIC_GROUP_SUBSCRIBE}/${notification.groupId}`,
// (message: { body: string }) => {
// console.log('Received message:', message.body);
// const parsedMessage = JSON.parse(message.body);
// addMessageFn({
// ...parsedMessage,
// timestamp: Date.now(),
// });
// }
// );
// };
// stompClient.activate();
// router.push(`/game/${notification.groupId}/waitingRoom`);
// toast.dismiss(t.id);
// }}
// onReject={() => {
// toast.dismiss(t.id);
// }}
// toastId={t.id}
// />
// ),
// {
// position: 'top-center',
// duration: 30000,
// }
// );
// };
// ... existing code ...

const handleInvitation = (
notification: NotificationMessage,
Expand Down Expand Up @@ -72,6 +134,9 @@ const handleInvitation = (

const connectSSE = (userId: string) => {
const { setEventSource } = useSSEStore.getState();
// let retryCount = 0;
// const MAX_RETRIES = 3;
// let retryTimeout: NodeJS.Timeout;

try {
const url = `/api/notification-service/connect/${encodeURIComponent(userId)}`;
Expand Down
38 changes: 15 additions & 23 deletions app/game/[roomId]/[round]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import { useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import { motion } from 'framer-motion';
import colors from '@/styles/color/palette';
import SoundOn from '@/styles/Icon/SoundOn.svg';
import SoundOff from '@/styles/Icon/SoundOff.svg';

const InGameLayout = ({ children }: { children: React.ReactNode }) => {
const audioRef = useRef<HTMLAudioElement | null>(null);
Expand Down Expand Up @@ -91,12 +91,7 @@ const MusicSwitch = ({
onClick();
}}
>
<SwitchTrack isOn={isOn}>
<SwitchThumb
layout
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
/>
</SwitchTrack>
{isOn ? <SoundOnIcon /> : <SoundOffIcon />}
</SwitchContainer>
);
};
Expand All @@ -108,7 +103,7 @@ const Container = styled.div`
const SwitchContainer = styled.div`
position: absolute;
top: 0.5rem;
right: 0.5rem;
left: 1rem;
z-index: 50;
cursor: pointer;
margin: 0 auto;
Expand All @@ -119,23 +114,20 @@ const SwitchContainer = styled.div`
}
`;

const SwitchTrack = styled(motion.div)<{ isOn: boolean }>`
width: 50px;
const SoundOnIcon = styled.img.attrs({
src: SoundOn.src,
alt: 'Sound On',
})`
width: 24px;
height: 24px;
background-color: ${({ isOn }) =>
isOn ? colors.purple[60] : colors.gray[80]};
border-radius: 12px;
display: flex;
align-items: center;
justify-content: ${({ isOn }) => (isOn ? 'flex-end' : 'flex-start')};
padding: 2px;
`;

const SwitchThumb = styled(motion.div)`
width: 20px;
height: 20px;
background-color: white;
border-radius: 50%;
const SoundOffIcon = styled.img.attrs({
src: SoundOff.src,
alt: 'Sound Off',
})`
width: 24px;
height: 24px;
`;

export default InGameLayout;
146 changes: 42 additions & 104 deletions app/game/[roomId]/[round]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
'use client';
import { useRouter } from 'next/navigation';
import { useGameState } from '@/hooks/inGame/useGameState';

import useUserStore from '@/stores/useUserStore';
// import useUserStore from '@/stores/useUserStore';
import TopBar from '@/components/Common/TopBar/TopBar';
import Button from '@/components/Common/Button/Button';
import Timer from '@/components/Layout/Game/Timer';

import MapBottomSheet from '@/components/Layout/Game/MapBottomSheet';

import SwiperComponent from '@/components/Layout/Game/Swiper';
import MapComponent from '@/components/Layout/Game/GoogleMap';
import { useCallback, useState, useEffect } from 'react';
import { useWebSocket } from '@/hooks/inGame/useWebSocket';
import InGameWebSocket from '@/lib/websocket/gameWebsocket';
import useWebSocketStore, { RoundData } from '@/stores/useWebSocketStore';

import {
PageWrapper,
Expand All @@ -31,28 +29,43 @@ interface GamePageProps {

const GamePage = ({ params }: GamePageProps) => {
const router = useRouter();
const nickname = useUserStore(
(state: { nickname: string | null }) => state.nickname
);

// const { submitAnswer, isSubmitting } = useGameSubmit();
// const nickname = useUserStore((state) => state.nickname);
const [hasSubmitted, setHasSubmitted] = useState(false);

// WebSocket
const { gameState, submitAnswer, createRound } = useWebSocket(
Number(params.roomId)
);

const {
gameState,
currentRound,
isMapView,
showBackIcon,
currentSelectedCoordinate,
roundState,
setCurrentSelectedCoordinate,
handleBackClick,
} = useGameState(Number(params.round));

const messages = useWebSocketStore((state) => state.messages);

// roundState๋ฅผ ๋ณ„๋„์˜ state๋กœ ๊ด€๋ฆฌ
const [roundState, setRoundState] = useState<RoundData | null>(null);

// messages์™€ currentRound ๋ณ€๊ฒฝ ์‹œ roundState ์—…๋ฐ์ดํŠธ
useEffect(() => {
const roundStartMessage = messages
.filter((msg) => msg.eventType === 'ROUND_START')
.find((msg) => (msg.data as RoundData).roundNum === currentRound);

if (roundStartMessage) {
setRoundState(roundStartMessage.data as RoundData);
console.log(
'Round state updated for round',
currentRound,
':',
roundStartMessage.data
);
}
}, [messages, currentRound]);

const webSocket = InGameWebSocket.getInstance();

const handleNextRound = useCallback(() => {
router.push(`/game/${params.roomId}/${currentRound}/roundRank`);
setCurrentSelectedCoordinate(null);
Expand All @@ -65,78 +78,20 @@ const GamePage = ({ params }: GamePageProps) => {
setCurrentSelectedCoordinate(coordinate);
};

// TODO: ๋ฐฑ์—”๋“œ ์—ฐ๋™ ์‹œ ์‚ฌ์šฉ
// const handleSubmitAnswer = async () => {
// if (hasSubmitted || !currentSelectedCoordinate) {
// console.log('์ œ์ถœ ๋ถˆ๊ฐ€:', { hasSubmitted, currentSelectedCoordinate });
// return;
// }

// const submitData = {
// nickname: nickname || '',
// roundId: Number(params.round),
// coordinate: [
// currentSelectedCoordinate.lat,
// currentSelectedCoordinate.lng,
// ],
// };

// // ์ œ์ถœ ์‹œ์ž‘๊ณผ ๋™์‹œ์— ๋ฒ„ํŠผ ๋น„ํ™œ์„ฑํ™”
// setHasSubmitted(true);
// console.log('์ œ์ถœ ์‹œ์ž‘:', submitData);

// try {
// const success = await submitAnswer(submitData);
// console.log('์ œ์ถœ ๊ฒฐ๊ณผ:', success);

// if (!success) {
// console.warn('์ œ์ถœ ์‹คํŒจ');
// setHasSubmitted(false); // ์‹คํŒจ์‹œ์—๋งŒ ๋‹ค์‹œ ํ™œ์„ฑํ™”
// return;
// }

// setCurrentSelectedCoordinate(null);
// console.log('์ œ์ถœ ์™„๋ฃŒ');
// } catch (error) {
// console.error('์ œ์ถœ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ:', error);
// setHasSubmitted(false); // ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ์—๋„ ๋‹ค์‹œ ํ™œ์„ฑํ™”
// }
// };

// ํด๋ผ์ด์–ธํŠธ ํ…Œ์ŠคํŠธ ์šฉ

// ๋‹ต์•ˆ ์ œ์ถœ ํ•จ์ˆ˜ ์ˆ˜์ •
const handleSubmitAnswer = async () => {
if (hasSubmitted || !currentSelectedCoordinate) {
console.log('์ œ์ถœ ๋ถˆ๊ฐ€:', { hasSubmitted, currentSelectedCoordinate });
return;
}

const submitData = {
nickname: nickname || '',
roundId: Number(params.round),
coordinate: [
currentSelectedCoordinate.lat,
currentSelectedCoordinate.lng,
] as [number, number],
};

setHasSubmitted(true);

// API ํ˜ธ์ถœ ๋Œ€์‹  setTimeout์œผ๋กœ ํ…Œ์ŠคํŠธ
try {
await new Promise((resolve) => setTimeout(resolve, 1000)); // 1์ดˆ ๋”œ๋ ˆ์ด
const mockSuccess = true; // ํ…Œ์ŠคํŠธ์šฉ ์„ฑ๊ณต ์‘๋‹ต

console.log('์ œ์ถœ ๊ฒฐ๊ณผ:', mockSuccess);

if (!mockSuccess) {
console.warn('์ œ์ถœ ์‹คํŒจ');
setHasSubmitted(false);
return;
}

submitAnswer(submitData);

webSocket.submitAnswer(Number(params.round), [
currentSelectedCoordinate.lat,
currentSelectedCoordinate.lng,
]);
setCurrentSelectedCoordinate(null);
console.log('์ œ์ถœ ์™„๋ฃŒ');
} catch (error) {
Expand All @@ -156,19 +111,9 @@ const GamePage = ({ params }: GamePageProps) => {
}
};

useEffect(() => {
// ๋ผ์šด๋“œ ์‹œ์ž‘์‹œ ๋ผ์šด๋“œ ์ƒ์„ฑ ์š”์ฒญ
createRound(Number(params.round));
}, [createRound, params.round]);

// gameState์—์„œ ๋ผ์šด๋“œ ์ •๋ณด ํ™œ์šฉ
useEffect(() => {
if (gameState.roundState) {
// ๋ผ์šด๋“œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ์‹œ ์ฒ˜๋ฆฌ
console.log('์ƒˆ๋กœ์šด ๋ผ์šด๋“œ ์ •๋ณด:', gameState.roundState);
}
}, [gameState.roundState]);

// ๋””๋ฒ„๊น…์šฉ ์ฝ˜์†” ๋กœ๊ทธ ์ถ”๊ฐ€
console.log('ํ˜„์žฌ gameState:', gameState);
console.log('contentUrls:', gameState.roundState?.contentUrls);
return (
<>
<PageWrapper>
Expand All @@ -184,13 +129,6 @@ const GamePage = ({ params }: GamePageProps) => {
<Timer initialTime={60} onTimeEnd={handleNextRound} />

{/* ๊ธฐ๋ณธ ๋ทฐ (์Šค์™€์ดํผ์™€ ํžŒํŠธ) */}
<SwiperComponent />
{roundState.hint && (
<HintWrapper>
<HintIcon>ํžŒํŠธ</HintIcon>
<HintText> {roundState.hint}</HintText>
</HintWrapper>
)}
{isMapView ? (
<MapComponent
mode="game"
Expand All @@ -199,14 +137,14 @@ const GamePage = ({ params }: GamePageProps) => {
/>
) : (
<>
<SwiperComponent />
{/* TODO: ๋ฐฑ์—”๋“œ ์—ฐ๋™ ์‹œ ์‚ฌ์šฉ */}
{/* <SwiperComponent images={gameState.roundState?.contentUrls || []} /> */}
{roundState.hint && (
<SwiperComponent
images={roundState?.contentUrls || []}
key={roundState?.roundId}
/>
{roundState?.hint && (
<HintWrapper>
<HintIcon>ํžŒํŠธ</HintIcon>
<HintText> {roundState.hint}</HintText>
{/* <HintText>{gameState.roundState.hint}</HintText> */}
</HintWrapper>
)}
</>
Expand Down
Loading

0 comments on commit 43d16e6

Please sign in to comment.