Overnap
Builds for 1 pipeline passed in 7 minutes 51 seconds

Merge branch 'feature/ingame' into develop

...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
25 "scripts": { 25 "scripts": {
26 "start": "npm run twcss && set PORT=3001 && react-scripts start", 26 "start": "npm run twcss && set PORT=3001 && react-scripts start",
27 "build": "npm run twcss && react-scripts build", 27 "build": "npm run twcss && react-scripts build",
28 + "onlybuild": "react-scripts build",
28 "test": "react-scripts test", 29 "test": "react-scripts test",
29 "eject": "react-scripts eject", 30 "eject": "react-scripts eject",
30 "twcss": "tailwind build src/tailwind.css -c tailwind.config.js -o src/index.css" 31 "twcss": "tailwind build src/tailwind.css -c tailwind.config.js -o src/index.css"
......
...@@ -18,5 +18,15 @@ export const MessageType = { ...@@ -18,5 +18,15 @@ export const MessageType = {
18 ROOM_USER_UPDATE: "updateRoomUser", 18 ROOM_USER_UPDATE: "updateRoomUser",
19 ROOM_CHAT: "chat", 19 ROOM_CHAT: "chat",
20 ROOM_READY: "ready", 20 ROOM_READY: "ready",
21 - ROOM_START: "startGame" 21 + ROOM_START: "startGame",
22 + GAME_START: "startRound",
23 + GAME_WORDSET: "wordSet",
24 + GAME_CHOOSE: "chooseWord",
25 + GAME_WORD: "wordChosen",
26 + GAME_TIMER: "timer",
27 + GAME_ACCEPT: "answerAccepted",
28 + GAME_FINISH_ROUND: "finishRound",
29 + GAME_FINISH_GAME: "finishGame",
30 + DRAW_SET: "setBrush",
31 + DRAW_MOVE: "moveBrush",
22 } as const 32 } as const
...\ No newline at end of file ...\ No newline at end of file
......
1 -import React, { useCallback, useEffect, useRef, useState } from 'react'; 1 +import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
2 -import { Vector } from './types'; 2 +import SocketContext from '../../contexts/SocketContext';
3 +import { MessageType, RawMessage } from '../common/types';
4 +import { BrushData, Vector } from './types';
3 5
4 // 참고 : https://basketdeveloper.tistory.com/79 6 // 참고 : https://basketdeveloper.tistory.com/79
5 7
6 -export const Canvas: React.FC = () => { 8 +interface CanvasProps {
9 + isDrawer: boolean;
10 +}
11 +
12 +export const Canvas: React.FC<CanvasProps> = ({ isDrawer }) => {
13 + const socket = useContext(SocketContext);
7 const canvasRef = useRef<HTMLCanvasElement>(null); 14 const canvasRef = useRef<HTMLCanvasElement>(null);
8 15
9 const [mousePosition, setMousePosition] = useState<Vector>({ x:0, y:0 }); 16 const [mousePosition, setMousePosition] = useState<Vector>({ x:0, y:0 });
...@@ -52,6 +59,22 @@ export const Canvas: React.FC = () => { ...@@ -52,6 +59,22 @@ export const Canvas: React.FC = () => {
52 if (coordinates) { 59 if (coordinates) {
53 setIsPainting(true); 60 setIsPainting(true);
54 setMousePosition(coordinates); 61 setMousePosition(coordinates);
62 +
63 + const rawMessage: RawMessage = {
64 + type: MessageType.DRAW_MOVE,
65 + message: coordinates
66 + };
67 + socket.emit('msg', rawMessage, () => {});
68 +
69 + const nextRawMessage: RawMessage = {
70 + type: MessageType.DRAW_SET,
71 + message: {
72 + size: 5,
73 + color: '000000',
74 + drawing: true
75 + } as BrushData
76 + };
77 + socket.emit('msg', nextRawMessage, () => {});
55 } 78 }
56 }, []); 79 }, []);
57 80
...@@ -65,6 +88,13 @@ export const Canvas: React.FC = () => { ...@@ -65,6 +88,13 @@ export const Canvas: React.FC = () => {
65 const newMousePosition = getCoordinates(event); 88 const newMousePosition = getCoordinates(event);
66 if (mousePosition && newMousePosition) { 89 if (mousePosition && newMousePosition) {
67 drawLine(mousePosition, newMousePosition); 90 drawLine(mousePosition, newMousePosition);
91 +
92 + const rawMessage: RawMessage = {
93 + type: MessageType.DRAW_MOVE,
94 + message: newMousePosition
95 + };
96 + socket.emit('msg', rawMessage, () => {});
97 +
68 setMousePosition(newMousePosition); 98 setMousePosition(newMousePosition);
69 } 99 }
70 } 100 }
...@@ -73,11 +103,39 @@ export const Canvas: React.FC = () => { ...@@ -73,11 +103,39 @@ export const Canvas: React.FC = () => {
73 ); 103 );
74 104
75 const exitPaint = useCallback(() => { 105 const exitPaint = useCallback(() => {
106 + const rawMessage: RawMessage = {
107 + type: MessageType.DRAW_SET,
108 + message: {
109 + size: 5,
110 + color: '000000',
111 + drawing: false
112 + } as BrushData
113 + };
114 + socket.emit('msg', rawMessage, () => {});
115 +
76 setIsPainting(false); 116 setIsPainting(false);
77 }, []); 117 }, []);
78 118
119 + const handleDrawSet = useCallback((rawMessage: RawMessage) => {
120 + if (rawMessage.type === MessageType.DRAW_SET) {
121 + const data = rawMessage.message as BrushData;
122 + setIsPainting(data.drawing);
123 + }
124 + }, []);
125 +
126 + const handleDrawMove = useCallback((rawMessage: RawMessage) => {
127 + if (rawMessage.type === MessageType.DRAW_MOVE) {
128 + const data = rawMessage.message as Vector;
129 + if (isPainting) {
130 + drawLine(mousePosition, data);
131 + }
132 + setMousePosition(data);
133 + }
134 + }, [isPainting, mousePosition])
135 +
79 useEffect(() => { 136 useEffect(() => {
80 if (canvasRef.current) { 137 if (canvasRef.current) {
138 + if (isDrawer) {
81 const canvas: HTMLCanvasElement = canvasRef.current; 139 const canvas: HTMLCanvasElement = canvasRef.current;
82 140
83 canvas.addEventListener('mousedown', startPaint); 141 canvas.addEventListener('mousedown', startPaint);
...@@ -92,11 +150,39 @@ export const Canvas: React.FC = () => { ...@@ -92,11 +150,39 @@ export const Canvas: React.FC = () => {
92 canvas.removeEventListener('mouseleave', exitPaint); 150 canvas.removeEventListener('mouseleave', exitPaint);
93 }; 151 };
94 } 152 }
95 - }, [startPaint, paint, exitPaint]); 153 + }
154 + }, [isDrawer, startPaint, paint, exitPaint]);
155 +
156 + useEffect(() => {
157 + if (!isDrawer) {
158 + socket.on('msg', handleDrawSet);
159 + socket.on('msg', handleDrawMove);
160 +
161 + return () => {
162 + socket.off('msg', handleDrawSet);
163 + socket.off('msg', handleDrawMove);
164 + }
165 + }
166 + }, [isDrawer, handleDrawMove]);
167 +
168 + const handleClearWhenStart = useCallback((rawMessage: RawMessage) => {
169 + if (rawMessage.type === MessageType.GAME_START) {
170 + clearCanvas();
171 + setIsPainting(false);
172 + // TODO: 펜 굵기, 색 설정하게 되면 여기에 초기화 넣기
173 + }
174 + }, []);
175 +
176 + useEffect(() => {
177 + socket.on('msg', handleClearWhenStart);
178 + return () => {
179 + socket.off('msg', handleClearWhenStart);
180 + }
181 + }, []);
96 182
97 return ( 183 return (
98 <div className='mx-3 px-2 py-1 rounded shadow'> 184 <div className='mx-3 px-2 py-1 rounded shadow'>
99 - <canvas ref={canvasRef} width='512' height='384' /> 185 + <canvas ref={canvasRef} width='640' height='480' />
100 </div> 186 </div>
101 ); 187 );
102 } 188 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -23,9 +23,13 @@ export const Chat: React.FC<ChatProps> = (props) => { ...@@ -23,9 +23,13 @@ export const Chat: React.FC<ChatProps> = (props) => {
23 } 23 }
24 24
25 socket.on('msg', handleChatData); 25 socket.on('msg', handleChatData);
26 + socket.on('msg', handleAcceptMessage);
27 + socket.on('msg', handleFinishMessage);
26 28
27 return () => { 29 return () => {
28 socket.off('msg', handleChatData); 30 socket.off('msg', handleChatData);
31 + socket.off('msg', handleAcceptMessage);
32 + socket.on('msg', handleFinishMessage);
29 } 33 }
30 }, []); 34 }, []);
31 35
...@@ -47,9 +51,29 @@ export const Chat: React.FC<ChatProps> = (props) => { ...@@ -47,9 +51,29 @@ export const Chat: React.FC<ChatProps> = (props) => {
47 } 51 }
48 }, [input]); 52 }, [input]);
49 53
54 + const handleAcceptMessage = useCallback((rawMessage: RawMessage) => {
55 + if (rawMessage.type === MessageType.GAME_ACCEPT) {
56 + const message: ChatData = {
57 + sender: 'SYSTEM',
58 + message: 'That\'s correct!'
59 + };
60 + setChatLines(oldChatLines => [...oldChatLines, message]);
61 + }
62 + }, []);
63 +
64 + const handleFinishMessage = useCallback((rawMessage: RawMessage) => {
65 + if (rawMessage.type === MessageType.GAME_FINISH_ROUND) {
66 + const message: ChatData = {
67 + sender: 'SYSTEM',
68 + message: 'The round is over!'
69 + };
70 + setChatLines(oldChatLines => [...oldChatLines, message]);
71 + }
72 + }, []);
73 +
50 return ( 74 return (
51 <div className={props.w}> 75 <div className={props.w}>
52 - <div className={`${props.h} w-full rounded shadow flex flex-col overflow-y-scroll`}> 76 + <div className={`${props.h} w-full py-2 rounded shadow flex flex-col overflow-y-scroll`}>
53 {chatLines.map((line, i) => (<ChatLine key={16383+i} chatData={line}/>))} 77 {chatLines.map((line, i) => (<ChatLine key={16383+i} chatData={line}/>))}
54 <div ref={messageEndRef} /> 78 <div ref={messageEndRef} />
55 </div> 79 </div>
......
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 { Canvas } from './Canvas';
6 +import { RoundInfo } from './RoundInfo';
7 +import { Role, RoundData } from './types';
8 +import { Word } from './Word';
9 +
10 +interface GameBoardLocation {
11 + state: { username: string }
12 +}
13 +
14 +interface GameBoardProps {
15 + isInGame: boolean
16 +}
17 +
18 +export const GameBoard: React.FC<GameBoardProps> = ({ isInGame }) => {
19 + const socket = useContext(SocketContext);
20 + const location: GameBoardLocation = useLocation();
21 +
22 + const [ isDrawer, setIsDrawer ] = useState(false);
23 + const [ words, setWords ] = useState<string[]>([]);
24 + const [ wordChosen, setWordChosen ] = useState('');
25 + const [ round, setRound ] = useState(0);
26 +
27 + const handleWordSet = useCallback((rawMessage: RawMessage) => {
28 + if (rawMessage.type === MessageType.GAME_WORDSET) {
29 + console.log('단어 도착');
30 + const { words } = rawMessage.message as { words: string[] };
31 + setWords(words);
32 + }
33 + }, []);
34 +
35 + const handleStart = useCallback((rawMessage: RawMessage) => {
36 + if (rawMessage.type === MessageType.GAME_START) {
37 + setWords([]);
38 +
39 + const data = rawMessage.message as RoundData;
40 + console.log('테스트 location ', location.state.username);
41 + console.log('테스트 rolse ', data.roles);
42 + const index = data.roles.findIndex(x => x.username === location.state.username);
43 + setIsDrawer(data.roles[index].role === 'drawer');
44 + setWordChosen('');
45 + setRound(data.round);
46 + }
47 + }, []);
48 +
49 + const handleGetWordLength = useCallback((rawMessage: RawMessage) => {
50 + if (rawMessage.type === MessageType.GAME_WORD) {
51 + if (wordChosen === '') {
52 + const { length } = rawMessage.message as { length: number };
53 + setWordChosen('_' + ' _'.repeat(length-1));
54 + }
55 + }
56 + }, [wordChosen]);
57 +
58 + const handleAnswer = useCallback((rawMessage: RawMessage) => {
59 + if (rawMessage.type === MessageType.GAME_ACCEPT || rawMessage.type === MessageType.GAME_FINISH_ROUND) {
60 + const { answer } = rawMessage.message as { answer: string };
61 + setWordChosen(answer);
62 + }
63 + }, []);
64 +
65 + useEffect(() => {
66 + socket.on('msg', handleStart);
67 + socket.on('msg', handleGetWordLength);
68 + socket.on('msg', handleWordSet);
69 + socket.on('msg', handleAnswer);
70 +
71 + return () => {
72 + socket.off('msg', handleStart);
73 + socket.off('msg', handleGetWordLength);
74 + socket.off('msg', handleWordSet);
75 + socket.off('msg', handleAnswer);
76 + }
77 + }, []);
78 +
79 + return (
80 + <div className={`w-auto ${isInGame ? '' : 'hidden'}`}>
81 + <div className='w-full flex flex-col justify-center items-center'>
82 + {words.map((word, i) => (<Word key={word} index={i} word={word} setWordChosen={setWordChosen} setWords={setWords} />))}
83 + </div>
84 + <Canvas isDrawer={isDrawer && wordChosen !== ''} />
85 + <RoundInfo round={round} wordChosen={wordChosen} />
86 + </div>
87 + );
88 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -33,7 +33,7 @@ export const Ready: React.FC<ReadyProps> = ({ users }) => { ...@@ -33,7 +33,7 @@ export const Ready: React.FC<ReadyProps> = ({ users }) => {
33 const handleReady = useCallback(() => { 33 const handleReady = useCallback(() => {
34 if (isAdmin && isAllReady) { 34 if (isAdmin && isAllReady) {
35 const rawMessage: RawMessage = { 35 const rawMessage: RawMessage = {
36 - type: MessageType.ROOM_READY, 36 + type: MessageType.ROOM_START,
37 message: {} 37 message: {}
38 } 38 }
39 socket.emit('msg', rawMessage, () => {}); 39 socket.emit('msg', rawMessage, () => {});
......
...@@ -10,7 +10,7 @@ interface RoomInfoProps { ...@@ -10,7 +10,7 @@ interface RoomInfoProps {
10 10
11 export const RoomInfo: React.FC<RoomInfoProps> = ({ roomData }) => { 11 export const RoomInfo: React.FC<RoomInfoProps> = ({ roomData }) => {
12 return ( 12 return (
13 - <div className='m-3 mb-8 w-5/6 flex items-center place-content-between'> 13 + <div className='m-3 my-8 w-5/6 flex items-center place-content-between'>
14 <div>{roomData.name}</div> 14 <div>{roomData.name}</div>
15 <div>{roomData.users.length}/{roomData.maxUsers}</div> 15 <div>{roomData.users.length}/{roomData.maxUsers}</div>
16 </div> 16 </div>
......
1 +import React from 'react';
2 +import SocketContext from '../../contexts/SocketContext';
3 +import { Timer } from './Timer';
4 +import { RoundData } from './types';
5 +
6 +interface RoundInfoProps {
7 + round: number;
8 + wordChosen: string;
9 +}
10 +
11 +export const RoundInfo: React.FC<RoundInfoProps> = ({ round, wordChosen }) => {
12 + return (
13 + <div className='p-3 m-3 h-14 rounded shadow flex items-center place-content-between'>
14 + <Timer />
15 + <div>{wordChosen}</div>
16 + <div>Round {round}/5</div>
17 + </div>
18 + );
19 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import React, { useCallback, useContext, useEffect, useState } from 'react';
2 +import SocketContext from '../../contexts/SocketContext';
3 +import { MessageType, RawMessage } from '../common/types';
4 +
5 +interface timer {
6 + state: "started" | "stopped";
7 + time: number;
8 +};
9 +
10 +export const Timer: React.FC = () => {
11 + const socket = useContext(SocketContext);
12 +
13 + const [ time, setTime ] = useState(0);
14 + const [ isStop, setIsStop ] = useState(true);
15 +
16 + const handleTimeSet = useCallback((rawMessage: RawMessage) => {
17 + if (rawMessage.type === MessageType.GAME_TIMER) {
18 + const data = rawMessage.message as timer;
19 +
20 + console.log(data);
21 + if (data.state === 'started') {
22 + setIsStop(false);
23 + } else {
24 + setIsStop(true);
25 + }
26 +
27 + setTime(Math.floor(data.time));
28 + }
29 + }, []);
30 +
31 + useEffect(() => {
32 + if (!isStop) {
33 + const go = setInterval(() => {
34 + setTime(time-1);
35 + }, 1000);
36 +
37 + return () => clearInterval(go);
38 + } else {
39 + setTime(0);
40 + }
41 + }, [time, isStop]);
42 +
43 + useEffect(() => {
44 + socket.on('msg', handleTimeSet);
45 + return () => {
46 + socket.off('msg', handleTimeSet);
47 + }
48 + }, []);
49 +
50 + return (
51 + <div className={time < 10 ? 'text-red-500' : 'text-black'}>
52 + 🕒 {time}
53 + </div>
54 + );
55 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import React, { useCallback, useContext, useEffect, useState } from 'react';
2 +import SocketContext from '../../contexts/SocketContext';
3 +import { MessageType, RawMessage } from '../common/types';
4 +import { RoleData, RoundData } from './types';
5 +
6 +interface UserRoleProps {
7 + isInGame: boolean;
8 +}
9 +
10 +export const UserRole: React.FC<UserRoleProps> = ({ isInGame }) => {
11 + const socket = useContext(SocketContext);
12 + const [ roles, setRoles ] = useState<RoleData[]>([]);
13 +
14 + const handleRole = useCallback((rawMessage: RawMessage) => {
15 + if (rawMessage.type === MessageType.GAME_START) {
16 + const { roles } = rawMessage.message as RoundData;
17 + setRoles(roles);
18 + }
19 + }, []);
20 +
21 + useEffect(() => {
22 + socket.on('msg', handleRole);
23 + return () => {
24 + socket.off('msg', handleRole);
25 + }
26 + }, []);
27 +
28 + return (
29 + <div className={`w-40 h-140 rounded shadow flex flex-col items-center ${isInGame ? '' : 'hidden'}`}>
30 + <div className='mt-3' />
31 + {roles.map(x => (
32 + <div key={x.username} className={`my-5 ease-linear transition-all duration-100
33 + ${x.role === 'drawer' ? 'text-blue-500'
34 + : x.role === 'winner' ? 'text-green-500'
35 + : 'text-black'}`}>
36 + {x.nickname} {x.role === 'drawer' ? '🖌️' : x.role === 'spectator' ? '👻' : ''}
37 + </div>
38 + ))}
39 + </div>
40 + );
41 +}
1 +import React, { useCallback, useContext } from 'react';
2 +import { IndexType } from 'typescript';
3 +import SocketContext from '../../contexts/SocketContext';
4 +import { MessageResponse, MessageType, RawMessage } from '../common/types';
5 +
6 +interface WordProps {
7 + index: number;
8 + word: string;
9 + setWordChosen: (value: React.SetStateAction<string>) => void;
10 + setWords: (value: React.SetStateAction<string[]>) => void;
11 +}
12 +
13 +export const Word: React.FC<WordProps> = (props) => {
14 + const socket = useContext(SocketContext);
15 +
16 + const handleChoose = useCallback(() => {
17 + const rawMessage: RawMessage = {
18 + type: MessageType.GAME_CHOOSE,
19 + message: { word: props.word }
20 + };
21 + socket.emit('msg', rawMessage, (response: MessageResponse<undefined>) => {
22 + if (response.ok) {
23 + props.setWords([]);
24 + props.setWordChosen(props.word);
25 + }
26 + });
27 + }, [props.setWordChosen]);
28 +
29 + return (
30 + <button className={`bg-green-500 active:bg-green-600 fixed
31 + text-white font-bold ${'mt-' + 40*(props.index+2)} mt-40
32 + px-5 py-2 rounded shadow
33 + outline-none focus:outline-none hover:shadow-md
34 + ease-linear transition-all duration-100`}
35 + type="button"
36 + onClick={() => handleChoose()}>{props.word}</button>
37 + );
38 +}
...@@ -26,3 +26,23 @@ export interface Vector { ...@@ -26,3 +26,23 @@ export interface Vector {
26 x: number; 26 x: number;
27 y: number; 27 y: number;
28 } 28 }
29 +
30 +export type Role = "drawer" | "guesser" | "winner" | "spectator";
31 +
32 +export interface RoleData {
33 + username: string;
34 + nickname: string;
35 + role: Role;
36 +}
37 +
38 +export interface RoundData {
39 + round: number;
40 + duration: number;
41 + roles: RoleData[];
42 +};
43 +
44 +export interface BrushData {
45 + size: number;
46 + color: string;
47 + drawing: boolean;
48 +}
......
...@@ -4,10 +4,12 @@ import { Main } from '../components/common/Main'; ...@@ -4,10 +4,12 @@ import { Main } from '../components/common/Main';
4 import { MessageResponse, MessageType, RawMessage } from '../components/common/types'; 4 import { MessageResponse, MessageType, RawMessage } from '../components/common/types';
5 import { Canvas } from '../components/room/Canvas'; 5 import { Canvas } from '../components/room/Canvas';
6 import { Chat } from '../components/room/Chat'; 6 import { Chat } from '../components/room/Chat';
7 +import { GameBoard } from '../components/room/GameBoard';
7 import { Ready } from '../components/room/Ready'; 8 import { Ready } from '../components/room/Ready';
8 import { RoomInfo } from '../components/room/RoomInfo'; 9 import { RoomInfo } from '../components/room/RoomInfo';
9 import { RoomData, UpdateRoomUser } from '../components/room/types'; 10 import { RoomData, UpdateRoomUser } from '../components/room/types';
10 import { UserInfo } from '../components/room/UserInfo'; 11 import { UserInfo } from '../components/room/UserInfo';
12 +import { UserRole } from '../components/room/UserRole';
11 import { UserStatus } from '../components/room/UserStatus'; 13 import { UserStatus } from '../components/room/UserStatus';
12 import SocketContext from '../contexts/SocketContext'; 14 import SocketContext from '../contexts/SocketContext';
13 15
...@@ -29,6 +31,14 @@ export const Room: React.FC = () => { ...@@ -29,6 +31,14 @@ export const Room: React.FC = () => {
29 }); 31 });
30 const [ isInGame, setIsInGame ] = useState(false); 32 const [ isInGame, setIsInGame ] = useState(false);
31 33
34 + const handleInGame = useCallback((rawMessage: RawMessage) => {
35 + if (rawMessage.type === MessageType.GAME_START) {
36 + setIsInGame(true);
37 + } else if (rawMessage.type === MessageType.GAME_FINISH_GAME) {
38 + setIsInGame(false);
39 + }
40 + }, []);
41 +
32 const handleUpdateRoomUser = useCallback((rawMessage: RawMessage) => { 42 const handleUpdateRoomUser = useCallback((rawMessage: RawMessage) => {
33 if (rawMessage.type == MessageType.ROOM_USER_UPDATE) { 43 if (rawMessage.type == MessageType.ROOM_USER_UPDATE) {
34 const data = rawMessage.message as UpdateRoomUser; 44 const data = rawMessage.message as UpdateRoomUser;
...@@ -81,8 +91,11 @@ export const Room: React.FC = () => { ...@@ -81,8 +91,11 @@ export const Room: React.FC = () => {
81 } 91 }
82 92
83 setRoomData(location.state.roomData); 93 setRoomData(location.state.roomData);
94 + socket.on('msg', handleInGame);
84 95
85 return () => { 96 return () => {
97 + socket.off('msg', handleInGame);
98 +
86 const rawMessage: RawMessage = { 99 const rawMessage: RawMessage = {
87 type: MessageType.ROOM_LEAVE, 100 type: MessageType.ROOM_LEAVE,
88 message: '' 101 message: ''
...@@ -94,12 +107,13 @@ export const Room: React.FC = () => { ...@@ -94,12 +107,13 @@ export const Room: React.FC = () => {
94 return ( 107 return (
95 <Main> 108 <Main>
96 <RoomInfo roomData={roomData}/> 109 <RoomInfo roomData={roomData}/>
110 + <div className='w-full flex justify-center'>
111 + {/* 게임보드와 유저롤을 계속 살려둬서 리스너를 항상 열어놓도록 하자 */}
112 + <UserRole isInGame={isInGame} />
113 + <GameBoard isInGame={isInGame} />
97 { 114 {
98 isInGame ? ( 115 isInGame ? (
99 - <div className='w-full flex'> 116 + <Chat w='w-4/12' h='h-132' />
100 - <Canvas />
101 - <Chat w='w-4/12' h='h-80' />
102 - </div>
103 ) : ( 117 ) : (
104 <div className='w-full flex flex-col justify-center items-center'> 118 <div className='w-full flex flex-col justify-center items-center'>
105 <UserInfo users={roomData.users}/> 119 <UserInfo users={roomData.users}/>
...@@ -108,6 +122,7 @@ export const Room: React.FC = () => { ...@@ -108,6 +122,7 @@ export const Room: React.FC = () => {
108 </div> 122 </div>
109 ) 123 )
110 } 124 }
125 + </div>
111 </Main> 126 </Main>
112 ); 127 );
113 } 128 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -2,7 +2,18 @@ module.exports = { ...@@ -2,7 +2,18 @@ module.exports = {
2 purge: [], 2 purge: [],
3 darkMode: false, // or 'media' or 'class' 3 darkMode: false, // or 'media' or 'class'
4 theme: { 4 theme: {
5 - extend: {}, 5 + extend: {
6 + spacing: {
7 + 120: '30rem',
8 + 124: '31rem',
9 + 128: '32rem',
10 + 132: '33rem',
11 + 136: '34rem',
12 + 140: '35rem',
13 + 160: '40rem',
14 + 200: '50rem',
15 + },
16 + },
6 }, 17 },
7 variants: { 18 variants: {
8 extend: { 19 extend: {
......