Builds for
1 pipeline
passed
in
7 minutes 53 seconds
Merge branch 'feature/room' into develop
Showing
13 changed files
with
454 additions
and
8 deletions
... | @@ -2,6 +2,7 @@ import React from 'react'; | ... | @@ -2,6 +2,7 @@ import React from 'react'; |
2 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; | 2 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; |
3 | import { socket, SocketProvider } from './contexts/SocketContext'; | 3 | import { socket, SocketProvider } from './contexts/SocketContext'; |
4 | import { Login } from './pages/Login'; | 4 | import { Login } from './pages/Login'; |
5 | +import { Room } from './pages/Room'; | ||
5 | import { Rooms } from './pages/Rooms'; | 6 | import { Rooms } from './pages/Rooms'; |
6 | 7 | ||
7 | const App: React.FC = () => { | 8 | const App: React.FC = () => { |
... | @@ -11,7 +12,7 @@ const App: React.FC = () => { | ... | @@ -11,7 +12,7 @@ const App: React.FC = () => { |
11 | <Switch> | 12 | <Switch> |
12 | <Route exact path='/' component={Login}/> | 13 | <Route exact path='/' component={Login}/> |
13 | <Route path='/rooms' component={Rooms}></Route> | 14 | <Route path='/rooms' component={Rooms}></Route> |
14 | - <Route path='/:roomId'></Route> | 15 | + <Route path='/:roomId' component={Room}></Route> |
15 | </Switch> | 16 | </Switch> |
16 | </Router> | 17 | </Router> |
17 | </SocketProvider> | 18 | </SocketProvider> | ... | ... |
... | @@ -13,7 +13,9 @@ export const MessageType = { | ... | @@ -13,7 +13,9 @@ export const MessageType = { |
13 | LOGIN: "login", | 13 | LOGIN: "login", |
14 | ROOM_LIST_REQUEST: "roomList", | 14 | ROOM_LIST_REQUEST: "roomList", |
15 | ROOM_JOIN: "joinRoom", | 15 | ROOM_JOIN: "joinRoom", |
16 | - ROOM_LEAVE: "room_leave", | 16 | + ROOM_LEAVE: "leaveRoom", |
17 | - ROOM_USER_UPDATE: "room_user_update", | 17 | + ROOM_USER_UPDATE: "updateRoomUser", |
18 | - ROOM_CHAT: "room_chat", | 18 | + ROOM_CHAT: "chat", |
19 | + ROOM_READY: "ready", | ||
20 | + ROOM_START: "startGame" | ||
19 | } as const | 21 | } as const |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
web/src/components/room/Canvas.tsx
0 → 100644
1 | +import React, { useCallback, useEffect, useRef, useState } from 'react'; | ||
2 | +import { Vector } from './types'; | ||
3 | + | ||
4 | +// 참고 : https://basketdeveloper.tistory.com/79 | ||
5 | + | ||
6 | +export const Canvas: React.FC = () => { | ||
7 | + const canvasRef = useRef<HTMLCanvasElement>(null); | ||
8 | + | ||
9 | + const [mousePosition, setMousePosition] = useState<Vector>({ x:0, y:0 }); | ||
10 | + const [isPainting, setIsPainting] = useState(false); | ||
11 | + | ||
12 | + const getCoordinates = useCallback((event: MouseEvent): Vector | undefined => { | ||
13 | + if (!canvasRef.current) { | ||
14 | + return; | ||
15 | + } else { | ||
16 | + return { | ||
17 | + x: event.pageX - canvasRef.current.offsetLeft, | ||
18 | + y: event.pageY - canvasRef.current.offsetTop | ||
19 | + }; | ||
20 | + } | ||
21 | + }, []); | ||
22 | + | ||
23 | + const drawLine = useCallback((prev: Vector, current: Vector) => { | ||
24 | + if (canvasRef.current) { | ||
25 | + const context = canvasRef.current!.getContext('2d'); | ||
26 | + if (context) { | ||
27 | + context.strokeStyle = 'black'; | ||
28 | + context.lineJoin = 'round'; | ||
29 | + context.lineWidth = 5; | ||
30 | + | ||
31 | + context.beginPath(); | ||
32 | + context.moveTo(prev.x, prev.y); | ||
33 | + context.lineTo(current.x, current.y); | ||
34 | + context.closePath(); | ||
35 | + | ||
36 | + context.stroke(); | ||
37 | + } | ||
38 | + } | ||
39 | + }, []); | ||
40 | + | ||
41 | + const clearCanvas = useCallback(() => { | ||
42 | + if (canvasRef.current) { | ||
43 | + const context = canvasRef.current.getContext('2d'); | ||
44 | + if (context) { | ||
45 | + context.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); | ||
46 | + } | ||
47 | + } | ||
48 | + }, []); | ||
49 | + | ||
50 | + const startPaint = useCallback((event: MouseEvent) => { | ||
51 | + const coordinates = getCoordinates(event); | ||
52 | + if (coordinates) { | ||
53 | + setIsPainting(true); | ||
54 | + setMousePosition(coordinates); | ||
55 | + } | ||
56 | + }, []); | ||
57 | + | ||
58 | + const paint = useCallback( | ||
59 | + (event: MouseEvent) => { | ||
60 | + // 드래그 방지 | ||
61 | + event.preventDefault(); | ||
62 | + event.stopPropagation(); | ||
63 | + | ||
64 | + if (isPainting) { | ||
65 | + const newMousePosition = getCoordinates(event); | ||
66 | + if (mousePosition && newMousePosition) { | ||
67 | + drawLine(mousePosition, newMousePosition); | ||
68 | + setMousePosition(newMousePosition); | ||
69 | + } | ||
70 | + } | ||
71 | + }, | ||
72 | + [isPainting, mousePosition] | ||
73 | + ); | ||
74 | + | ||
75 | + const exitPaint = useCallback(() => { | ||
76 | + setIsPainting(false); | ||
77 | + }, []); | ||
78 | + | ||
79 | + useEffect(() => { | ||
80 | + if (canvasRef.current) { | ||
81 | + const canvas: HTMLCanvasElement = canvasRef.current; | ||
82 | + | ||
83 | + canvas.addEventListener('mousedown', startPaint); | ||
84 | + canvas.addEventListener('mousemove', paint); | ||
85 | + canvas.addEventListener('mouseup', exitPaint); | ||
86 | + canvas.addEventListener('mouseleave', exitPaint); | ||
87 | + | ||
88 | + return () => { | ||
89 | + canvas.removeEventListener('mousedown', startPaint); | ||
90 | + canvas.removeEventListener('mousemove', paint); | ||
91 | + canvas.removeEventListener('mouseup', exitPaint); | ||
92 | + canvas.removeEventListener('mouseleave', exitPaint); | ||
93 | + }; | ||
94 | + } | ||
95 | + }, [startPaint, paint, exitPaint]); | ||
96 | + | ||
97 | + return ( | ||
98 | + <div className='mx-3 px-2 py-1 rounded shadow'> | ||
99 | + <canvas ref={canvasRef} width='512' height='384' /> | ||
100 | + </div> | ||
101 | + ); | ||
102 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/room/Chat.tsx
0 → 100644
1 | +import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; | ||
2 | +import SocketContext from '../../contexts/SocketContext'; | ||
3 | +import { MessageType, RawMessage } from '../common/types'; | ||
4 | +import { ChatLine } from './ChatLine'; | ||
5 | +import { ChatData } from './types'; | ||
6 | + | ||
7 | +interface ChatProps { | ||
8 | + w: string; | ||
9 | + h: string; | ||
10 | +} | ||
11 | + | ||
12 | +export const Chat: React.FC<ChatProps> = (props) => { | ||
13 | + const socket = useContext(SocketContext); | ||
14 | + const [ input, setInput ] = useState(''); | ||
15 | + const [ chatLines, setChatLines ] = useState<ChatData[]>([]); | ||
16 | + const messageEndRef = useRef<HTMLDivElement | null>(null); | ||
17 | + | ||
18 | + useEffect(() => { | ||
19 | + const handleChatData = (message: RawMessage) => { | ||
20 | + if (message.type === MessageType.ROOM_CHAT) { | ||
21 | + setChatLines(oldChatLines => [...oldChatLines, message.message as ChatData]); | ||
22 | + } | ||
23 | + } | ||
24 | + | ||
25 | + socket.on('msg', handleChatData); | ||
26 | + | ||
27 | + return () => { | ||
28 | + socket.off('msg', handleChatData); | ||
29 | + } | ||
30 | + }, []); | ||
31 | + | ||
32 | + const handleAutoScroll = useCallback(() => { | ||
33 | + messageEndRef.current?.scrollIntoView({ behavior: 'smooth' }); | ||
34 | + }, []); | ||
35 | + | ||
36 | + useEffect(handleAutoScroll, [chatLines]) | ||
37 | + | ||
38 | + const handleEnter = useCallback((e: React.KeyboardEvent) => { | ||
39 | + if (e.key === 'Enter') { | ||
40 | + // setChatLines([...chatLines, { sender: 'me', message: input }]); | ||
41 | + | ||
42 | + const rawMessage: RawMessage = { | ||
43 | + type: MessageType.ROOM_CHAT, | ||
44 | + message: { message: input } | ||
45 | + } | ||
46 | + socket.emit('msg', rawMessage, () => {}); | ||
47 | + | ||
48 | + setInput(''); | ||
49 | + } | ||
50 | + }, [input]); | ||
51 | + | ||
52 | + return ( | ||
53 | + <div className={props.w}> | ||
54 | + <div className={`${props.h} w-full rounded shadow flex flex-col overflow-y-scroll`}> | ||
55 | + {chatLines.map((line, i) => (<ChatLine key={16383+i} chatData={line}/>))} | ||
56 | + <div ref={messageEndRef} /> | ||
57 | + </div> | ||
58 | + <input className='w-full px-3 py-2 bg-white | ||
59 | + placeholder-gray-400 text-gray-700 text-sm | ||
60 | + rounded shadow outline-none focus:outline-none' | ||
61 | + placeholder='Enter the answer' | ||
62 | + onChange={e => setInput(e.target.value)} | ||
63 | + value={input} | ||
64 | + onKeyPress={handleEnter}></input> | ||
65 | + </div> | ||
66 | + ); | ||
67 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/room/ChatLine.tsx
0 → 100644
1 | +import React from 'react'; | ||
2 | +import { ChatData } from './types'; | ||
3 | + | ||
4 | +interface ChatLineProps { | ||
5 | + chatData: ChatData; | ||
6 | +} | ||
7 | + | ||
8 | +export const ChatLine: React.FC<ChatLineProps> = ({ chatData }) => { | ||
9 | + return ( | ||
10 | + <div className='w-full px-3 py-1.5 bg-white | ||
11 | + text-gray-700 text-sm'>{chatData.sender} : {chatData.message}</div> | ||
12 | + ); | ||
13 | +} |
web/src/components/room/Ready.tsx
0 → 100644
1 | +import React, { useCallback, useContext, useEffect, useState } from 'react'; | ||
2 | +import { useLocation } from 'react-router'; | ||
3 | +import SocketContext from '../../contexts/SocketContext'; | ||
4 | +import { MessageType, RawMessage } from '../common/types'; | ||
5 | +import { User } from './types'; | ||
6 | + | ||
7 | +interface ReadyLocation { | ||
8 | + state: { username: string } | ||
9 | +} | ||
10 | + | ||
11 | +interface ReadyProps { | ||
12 | + users: User[]; | ||
13 | +} | ||
14 | + | ||
15 | +export const Ready: React.FC<ReadyProps> = ({ users }) => { | ||
16 | + const socket = useContext(SocketContext); | ||
17 | + const location: ReadyLocation = useLocation(); | ||
18 | + | ||
19 | + const [ isAdmin, setIsAdmin ] = useState(false); | ||
20 | + const [ isReady, setIsReady ] = useState(false); | ||
21 | + const [ isAllReady, setIsAllReady ] = useState(false); | ||
22 | + | ||
23 | + useEffect(() => { | ||
24 | + const me = users.find(x => x.username === location.state.username); | ||
25 | + setIsAdmin(me?.admin || false); | ||
26 | + setIsReady(me?.ready || false); | ||
27 | + | ||
28 | + const test = true; | ||
29 | + users.forEach(x => test && x.ready); | ||
30 | + setIsAllReady(test); | ||
31 | + }); | ||
32 | + | ||
33 | + const handleReady = useCallback(() => { | ||
34 | + if (isAdmin && isAllReady) { | ||
35 | + const rawMessage: RawMessage = { | ||
36 | + type: MessageType.ROOM_READY, | ||
37 | + message: {} | ||
38 | + } | ||
39 | + socket.emit('msg', rawMessage, () => {}); | ||
40 | + } else { | ||
41 | + const rawMessage: RawMessage = { | ||
42 | + type: MessageType.ROOM_READY, | ||
43 | + message: { ready: !isReady } | ||
44 | + } | ||
45 | + socket.emit('msg', rawMessage, () => {}); | ||
46 | + } | ||
47 | + }, [isAdmin, isReady, isAllReady]); | ||
48 | + | ||
49 | + return ( | ||
50 | + <button className={`${isAdmin ? isAllReady ? 'bg-green-500' : 'bg-gray-400' | ||
51 | + : isReady ? 'bg-green-600' | ||
52 | + : 'bg-green-500 active:bg-green-600'} | ||
53 | + text-white font-bold uppercase | ||
54 | + px-7 py-3 m-8 rounded shadow | ||
55 | + outline-none focus:outline-none hover:shadow-md | ||
56 | + ease-linear transition-all duration-100`} | ||
57 | + type="button" | ||
58 | + onClick={() => handleReady()}>{isAdmin ? 'Start' : 'Ready'}</button> | ||
59 | + ); | ||
60 | +} |
web/src/components/room/RoomInfo.tsx
0 → 100644
1 | +import React, { useCallback, useContext, useEffect, useState } from 'react'; | ||
2 | +import { useHistory, useLocation } from 'react-router'; | ||
3 | +import SocketContext from '../../contexts/SocketContext'; | ||
4 | +import { MessageResponse, MessageType, RawMessage } from '../common/types'; | ||
5 | +import { RoomData, UpdateRoomUser } from './types'; | ||
6 | + | ||
7 | +interface RoomInfoProps { | ||
8 | + roomData: RoomData; | ||
9 | +} | ||
10 | + | ||
11 | +export const RoomInfo: React.FC<RoomInfoProps> = ({ roomData }) => { | ||
12 | + return ( | ||
13 | + <div className='m-3 mb-8 w-5/6 flex items-center place-content-between'> | ||
14 | + <div>{roomData.name}</div> | ||
15 | + <div>{roomData.users.length}/{roomData.maxUsers}</div> | ||
16 | + </div> | ||
17 | + ); | ||
18 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/room/UserInfo.tsx
0 → 100644
1 | +import React from 'react'; | ||
2 | +import { User } from './types'; | ||
3 | +import { UserStatus } from './UserStatus'; | ||
4 | + | ||
5 | +interface UserInfoProps { | ||
6 | + users: User[]; | ||
7 | +} | ||
8 | + | ||
9 | +export const UserInfo: React.FC<UserInfoProps> = ({ users }) => { | ||
10 | + return ( | ||
11 | + <div className='w-7/12 h-60 flex justify-center'> | ||
12 | + {users.map((user) => (<UserStatus key={user.username} user={user} />))} | ||
13 | + </div> | ||
14 | + ); | ||
15 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/src/components/room/UserStatus.tsx
0 → 100644
1 | +import React from 'react'; | ||
2 | +import { User } from './types'; | ||
3 | + | ||
4 | +interface UserStatusProps { | ||
5 | + user: User; | ||
6 | +} | ||
7 | + | ||
8 | +export const UserStatus: React.FC<UserStatusProps> = ({ user }) => { | ||
9 | + return ( | ||
10 | + <div className='p-3 h-12 m-4 rounded-lg shadow'> | ||
11 | + <div className={`${user.admin ? 'text-blue-500' : | ||
12 | + user.ready ? 'text-green-500' : 'text-black'} | ||
13 | + text-lg text-center align-middle | ||
14 | + ease-linear transition-all duration-100`}> | ||
15 | + {user.username}</div> | ||
16 | + </div> | ||
17 | + ) | ||
18 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -2,5 +2,30 @@ export interface RoomData { | ... | @@ -2,5 +2,30 @@ export interface RoomData { |
2 | uuid: string; | 2 | uuid: string; |
3 | name: string; | 3 | name: string; |
4 | maxUsers: number; | 4 | maxUsers: number; |
5 | - users: string[]; | 5 | + users: User[]; |
6 | +} | ||
7 | + | ||
8 | +export interface User { | ||
9 | + username: string; | ||
10 | + admin: boolean; | ||
11 | + ready: boolean; | ||
12 | +} | ||
13 | + | ||
14 | +export interface UpdateRoomUser { | ||
15 | + state: "added" | "updated" | "removed"; | ||
16 | + user: { | ||
17 | + username: string; | ||
18 | + admin: boolean; | ||
19 | + ready: boolean; | ||
20 | + }; | ||
21 | +} | ||
22 | + | ||
23 | +export interface ChatData { | ||
24 | + sender: string; | ||
25 | + message: string; | ||
26 | +} | ||
27 | + | ||
28 | +export interface Vector { | ||
29 | + x: number; | ||
30 | + y: number; | ||
6 | } | 31 | } | ... | ... |
1 | import React, { useCallback, useContext } from 'react'; | 1 | import React, { useCallback, useContext } from 'react'; |
2 | -import { useHistory } from 'react-router'; | 2 | +import { useHistory, useLocation } from 'react-router'; |
3 | import SocketContext from '../../contexts/SocketContext'; | 3 | import SocketContext from '../../contexts/SocketContext'; |
4 | import { MessageResponse, MessageType, RawMessage } from '../common/types'; | 4 | import { MessageResponse, MessageType, RawMessage } from '../common/types'; |
5 | import { RoomData } from '../room/types'; | 5 | import { RoomData } from '../room/types'; |
6 | import { Room } from './types'; | 6 | import { Room } from './types'; |
7 | 7 | ||
8 | +interface RoomBlockLocation { | ||
9 | + state: { username: string } | ||
10 | +} | ||
11 | + | ||
8 | interface RoomBlockProps { | 12 | interface RoomBlockProps { |
9 | room: Room | 13 | room: Room |
10 | } | 14 | } |
... | @@ -12,6 +16,8 @@ interface RoomBlockProps { | ... | @@ -12,6 +16,8 @@ interface RoomBlockProps { |
12 | export const RoomBlock: React.FC<RoomBlockProps> = ({ room }) => { | 16 | export const RoomBlock: React.FC<RoomBlockProps> = ({ room }) => { |
13 | const history = useHistory(); | 17 | const history = useHistory(); |
14 | const socket = useContext(SocketContext); | 18 | const socket = useContext(SocketContext); |
19 | + const location: RoomBlockLocation = useLocation(); | ||
20 | + | ||
15 | const joinRoom = useCallback(() => { | 21 | const joinRoom = useCallback(() => { |
16 | if (room.currentUsers < room.maxUsers) { | 22 | if (room.currentUsers < room.maxUsers) { |
17 | const rawMessage: RawMessage = { | 23 | const rawMessage: RawMessage = { |
... | @@ -22,7 +28,10 @@ export const RoomBlock: React.FC<RoomBlockProps> = ({ room }) => { | ... | @@ -22,7 +28,10 @@ export const RoomBlock: React.FC<RoomBlockProps> = ({ room }) => { |
22 | if (response.ok) { | 28 | if (response.ok) { |
23 | history.push({ | 29 | history.push({ |
24 | pathname: '/' + room.uuid, | 30 | pathname: '/' + room.uuid, |
25 | - state: {roomData: response.result!} | 31 | + state: { |
32 | + username: location.state.username, | ||
33 | + roomData: response.result! | ||
34 | + } | ||
26 | }); | 35 | }); |
27 | } else { | 36 | } else { |
28 | //TODO: 에러 MODAL을 어케띄우지? 하위컴포넌트에서 훅을 쓰면 어떻게 되는지 확인 | 37 | //TODO: 에러 MODAL을 어케띄우지? 하위컴포넌트에서 훅을 쓰면 어떻게 되는지 확인 | ... | ... |
... | @@ -16,7 +16,10 @@ export const Login: React.FC = () => { | ... | @@ -16,7 +16,10 @@ export const Login: React.FC = () => { |
16 | } | 16 | } |
17 | socket.emit('msg', rawMessage, (response : MessageResponse<undefined>) => { | 17 | socket.emit('msg', rawMessage, (response : MessageResponse<undefined>) => { |
18 | if (response.ok) { | 18 | if (response.ok) { |
19 | - history.push('/rooms'); | 19 | + history.push({ |
20 | + pathname: '/rooms', | ||
21 | + state: { username: username } | ||
22 | + }); | ||
20 | } else { | 23 | } else { |
21 | console.error('login error!'); // TODO: 팝업 에러? | 24 | console.error('login error!'); // TODO: 팝업 에러? |
22 | } | 25 | } | ... | ... |
web/src/pages/Room.tsx
0 → 100644
1 | +import React, { useCallback, useContext, useEffect, useState } from 'react'; | ||
2 | +import { useHistory, useLocation } from 'react-router'; | ||
3 | +import { Main } from '../components/common/Main'; | ||
4 | +import { MessageResponse, MessageType, RawMessage } from '../components/common/types'; | ||
5 | +import { Canvas } from '../components/room/Canvas'; | ||
6 | +import { Chat } from '../components/room/Chat'; | ||
7 | +import { Ready } from '../components/room/Ready'; | ||
8 | +import { RoomInfo } from '../components/room/RoomInfo'; | ||
9 | +import { RoomData, UpdateRoomUser } from '../components/room/types'; | ||
10 | +import { UserInfo } from '../components/room/UserInfo'; | ||
11 | +import { UserStatus } from '../components/room/UserStatus'; | ||
12 | +import SocketContext from '../contexts/SocketContext'; | ||
13 | + | ||
14 | +interface RoomLocation { | ||
15 | + state: { roomData: RoomData } | ||
16 | +} | ||
17 | + | ||
18 | +export const Room: React.FC = () => { | ||
19 | + const history = useHistory(); | ||
20 | + const socket = useContext(SocketContext); | ||
21 | + const location: RoomLocation = useLocation(); | ||
22 | + | ||
23 | + const [ roomData, setRoomData ] = useState<RoomData>({ | ||
24 | + // 기본값 | ||
25 | + uuid: '0', | ||
26 | + name: 'loading...', | ||
27 | + maxUsers: 9, | ||
28 | + users: [] | ||
29 | + }); | ||
30 | + const [ isInGame, setIsInGame ] = useState(false); | ||
31 | + | ||
32 | + const handleUpdateRoomUser = useCallback((rawMessage: RawMessage) => { | ||
33 | + if (rawMessage.type == MessageType.ROOM_USER_UPDATE) { | ||
34 | + const data = rawMessage.message as UpdateRoomUser; | ||
35 | + if (data.state == 'removed') { | ||
36 | + const newUsers = roomData.users; | ||
37 | + const index = newUsers.indexOf(data.user); | ||
38 | + if (index < 0) { | ||
39 | + console.log('존재하지 않는 유저를 제거 시도'); | ||
40 | + } else { | ||
41 | + newUsers.splice(index, 1); | ||
42 | + } | ||
43 | + setRoomData({ | ||
44 | + ...roomData, | ||
45 | + users: newUsers | ||
46 | + }); | ||
47 | + } else if (data.state === 'updated') { | ||
48 | + const newUsers = roomData.users; | ||
49 | + const index = newUsers.findIndex(x => x.username === data.user.username); | ||
50 | + if (index < 0) { | ||
51 | + console.log('존재하지 않는 유저를 업데이트 시도'); | ||
52 | + } else { | ||
53 | + newUsers[index] = data.user; | ||
54 | + } | ||
55 | + setRoomData({ | ||
56 | + ...roomData, | ||
57 | + users: newUsers | ||
58 | + }) | ||
59 | + } else { | ||
60 | + setRoomData({ | ||
61 | + ...roomData, | ||
62 | + users: [data.user, ...roomData.users] | ||
63 | + }); | ||
64 | + } | ||
65 | + } | ||
66 | + }, [roomData]); | ||
67 | + | ||
68 | + useEffect(() => { | ||
69 | + socket.on('msg', handleUpdateRoomUser); | ||
70 | + | ||
71 | + return () => { | ||
72 | + socket.off('msg', handleUpdateRoomUser); | ||
73 | + } | ||
74 | + }, [roomData]); | ||
75 | + | ||
76 | + useEffect(() => { | ||
77 | + // 비정상적인 루트로 방을 들어오면 로그인 화면으로 푸시 | ||
78 | + if (location.state === undefined) { | ||
79 | + history.push('/'); | ||
80 | + return; | ||
81 | + } | ||
82 | + | ||
83 | + setRoomData(location.state.roomData); | ||
84 | + | ||
85 | + return () => { | ||
86 | + const rawMessage: RawMessage = { | ||
87 | + type: MessageType.ROOM_LEAVE, | ||
88 | + message: '' | ||
89 | + } | ||
90 | + socket.emit('msg', rawMessage, (response : MessageResponse<undefined>) => {}); | ||
91 | + } | ||
92 | + }, []) | ||
93 | + | ||
94 | + return ( | ||
95 | + <Main> | ||
96 | + <RoomInfo roomData={roomData}/> | ||
97 | + { | ||
98 | + isInGame ? ( | ||
99 | + <div className='w-full flex'> | ||
100 | + <Canvas /> | ||
101 | + <Chat w='w-4/12' h='h-80' /> | ||
102 | + </div> | ||
103 | + ) : ( | ||
104 | + <div className='w-full flex flex-col justify-center items-center'> | ||
105 | + <UserInfo users={roomData.users}/> | ||
106 | + <Ready users={roomData.users} /> | ||
107 | + <Chat w='w-7/12' h='h-96' /> | ||
108 | + </div> | ||
109 | + ) | ||
110 | + } | ||
111 | + </Main> | ||
112 | + ); | ||
113 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment