Skip to content

Commit 1a16ffb

Browse files
authored
🔀 Merge pull request #41 from GleamingStar/dev/1.0.3
1.0.3 배포
2 parents ae30b16 + 5b3c940 commit 1a16ffb

42 files changed

Lines changed: 972 additions & 253 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
#### 캐치마인드 웹 게임 프로젝트
44

5-
## 👉 [AWS 배포 링크](http://52.79.242.136/)
5+
## 🚀 [AWS 배포 링크](http://52.79.242.136/)
66

77
### 🎨 Preview
8+
89
<img src="https://user-images.githubusercontent.com/70461368/159423207-dd813ec6-cd7e-43c9-9793-20cac7c3df96.gif" alt="desktop gif">
910
<img src="https://user-images.githubusercontent.com/70461368/159423334-9f85ae12-f498-4b01-9b39-cf1fb9cdaaf2.png" style="width:200px;" alt="mobile">
1011

@@ -21,11 +22,14 @@
2122
<img src="https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&logo=TypeScript&logoColor=white"/> <img src="https://img.shields.io/badge/React-61DAFB?style=flat-square&logo=React&logoColor=white"/> <img src="https://img.shields.io/badge/styled--components-DB7093?style=flat-square&logo=styled-components&logoColor=white"/> <img src="https://img.shields.io/badge/express-000000?style=flat-square&logo=express&logoColor=white" /> <img src="https://img.shields.io/badge/Socket.IO-010101?style=flat-square&logo=Socket.io&logoColor=white" />
2223
<img src="https://img.shields.io/badge/Webpack-8DD6F9?style=flat-square&logo=Webpack&logoColor=white" /> <img src="https://img.shields.io/badge/Babel-F9DC3E?style=flat-square&logo=Babel&logoColor=white" />
2324

24-
### 🚀 To Do
25+
### 🏗️ To Do
2526

2627
- [ ] 문제 목록 추가
27-
- [ ] [`<Entrance />`](https://github.com/GleamingStar/web-catchmind/issues/13) 기능 및 디자인 개선
28-
- [ ] [`<Lobby />`](https://github.com/GleamingStar/web-catchmind/issues/11) 디자인 개선
29-
- [ ] [최적화](https://github.com/GleamingStar/web-catchmind/issues/15)
28+
- [ ] [1.0.4 업데이트](https://github.com/GleamingStar/web-catchmind/issues/39)
29+
- 👷 GitHub Action 자동배포 적용
30+
- ⚡️ `draw` 소켓이벤트 최적화
31+
- ⬆️ React 18 적용
3032
- [ ] 리팩토링
3133
- [ ] README 개선
34+
35+
### 🌱 What I Learned

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "catch-mind",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"description": "drawing and guessing game web application",
55
"scripts": {
66
"start": "webpack --mode development --config ./webpack.server.js && node dist/server.js",
@@ -10,12 +10,10 @@
1010
"author": "GleamingStar ([email protected])",
1111
"license": "MIT",
1212
"dependencies": {
13-
"axios": "^0.25.0",
1413
"dotenv": "^16.0.0",
1514
"express": "^4.17.2",
1615
"react": "^17.0.2",
1716
"react-dom": "^17.0.2",
18-
"react-icons": "^4.3.1",
1917
"recoil": "^0.6.1",
2018
"socket.io": "^4.4.1",
2119
"socket.io-client": "^4.4.1",

src/client/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const Main = lazy(() => import('./components/Main'));
66
const App = () => (
77
<RecoilRoot>
88
<GlobalStyle />
9-
<Suspense fallback={<></>}>
9+
<Suspense fallback={null}>
1010
<Main />
1111
</Suspense>
1212
</RecoilRoot>

src/client/atom/accountAtom.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,35 @@
1-
import { atom } from 'recoil';
1+
import { atom, selector } from 'recoil';
22
import socket from 'client/config/socket';
3+
import { PROFILE_IMAGE_SIZE } from 'shared/constant';
34
import { TUser } from 'shared/types';
5+
import { makeHash } from 'shared/util';
6+
7+
export const userNameAtom = atom({
8+
key: 'userName',
9+
default: '',
10+
});
11+
12+
export const imageUrlAtom = atom({
13+
key: 'imageUrl',
14+
default: makeHash(32),
15+
});
16+
17+
export const userImageSelector = selector({
18+
key: 'userImage',
19+
get: ({ get }) => `http://gravatar.com/avatar/${get(imageUrlAtom)}?d=identicon&s=${PROFILE_IMAGE_SIZE}`,
20+
});
421

522
export const accountAtom = atom<TUser>({
623
key: 'account',
724
default: null,
825
effects: [
926
({ setSelf, resetSelf }) => {
1027
socket.on('login/success', setSelf);
11-
socket.on('disconnect', resetSelf)
12-
28+
socket.on('disconnect', resetSelf);
29+
1330
return () => {
1431
socket.off('login/success', setSelf);
15-
socket.off('disconnect', resetSelf)
32+
socket.off('disconnect', resetSelf);
1633
};
1734
},
1835
],

src/client/atom/canvasAtom.tsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { atom, selector } from 'recoil';
22
import socket from 'client/config/socket';
3-
import { CANVAS_SIZE } from 'shared/constant';
4-
import { TCanvas, TColor } from 'shared/types';
3+
import { CANVAS_SIZE, LANDSCAPE_WIDTH, PORTRAIT_WIDTH } from 'shared/constant';
4+
import { TCanvas } from 'shared/types';
55

66
const isMobileChrome = /Chrome/g.test(navigator.userAgent) && /Mobile Safari/g.test(navigator.userAgent);
77

@@ -29,7 +29,7 @@ export const contextAtom = atom<CanvasRenderingContext2D>({
2929

3030
const reset = () => ctx.clearRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
3131

32-
const MobileChromeReset = () => {
32+
const mobileChromeReset = () => {
3333
reset();
3434
ctx.beginPath();
3535
ctx.moveTo(0, 0);
@@ -39,7 +39,7 @@ export const contextAtom = atom<CanvasRenderingContext2D>({
3939
ctx.closePath();
4040
};
4141

42-
const resetHandler = isMobileChrome ? MobileChromeReset : reset;
42+
const resetHandler = isMobileChrome ? mobileChromeReset : reset;
4343

4444
socket.on('canvas/reset', resetHandler);
4545

@@ -56,7 +56,8 @@ export const contextAtom = atom<CanvasRenderingContext2D>({
5656
});
5757

5858
const width = window.innerWidth;
59-
const defaultLeftSpace = width > 800 ? (width - 800) / 2 : width > 500 ? (width - 500) / 2 : 0;
59+
const defaultLeftSpace =
60+
width > LANDSCAPE_WIDTH ? (width - LANDSCAPE_WIDTH) / 2 : width > PORTRAIT_WIDTH ? (width - PORTRAIT_WIDTH) / 2 : 0;
6061

6162
export const leftSpaceAtom = atom({
6263
key: 'leftSpace',
@@ -68,18 +69,20 @@ export const toolAtom = atom<'pencil' | 'eraser'>({
6869
default: 'pencil',
6970
});
7071

71-
export const thicknessAtom = atom<number>({
72+
export const thicknessAtom = atom({
7273
key: 'thickness',
7374
default: 2,
7475
});
7576

76-
export const colorAtom = atom<TColor>({
77+
export const colorAtom = atom({
7778
key: 'color',
78-
default: 'black',
79+
default: '#000000',
7980
});
8081

81-
const pencilInlineSVG = (currentColor: string) =>
82-
`data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='${currentColor}' class='bi bi-pencil' viewBox='0 0 16 16'%3E%3Cpath d='M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z'/%3E%3C/svg%3E`;
82+
const pencilInlineSVG = (color: string) =>
83+
`data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%23${color.slice(
84+
1
85+
)}' class='bi bi-pencil' viewBox='0 0 16 16'%3E%3Cpath d='M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z'/%3E%3C/svg%3E`;
8386
const eraserInlineSVG =
8487
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' class='bi bi-eraser' viewBox='0 0 16 16'%3E%3Cpath d='M8.086 2.207a2 2 0 0 1 2.828 0l3.879 3.879a2 2 0 0 1 0 2.828l-5.5 5.5A2 2 0 0 1 7.879 15H5.12a2 2 0 0 1-1.414-.586l-2.5-2.5a2 2 0 0 1 0-2.828l6.879-6.879zm2.121.707a1 1 0 0 0-1.414 0L4.16 7.547l5.293 5.293 4.633-4.633a1 1 0 0 0 0-1.414l-3.879-3.879zM8.746 13.547 3.453 8.254 1.914 9.793a1 1 0 0 0 0 1.414l2.5 2.5a1 1 0 0 0 .707.293H7.88a1 1 0 0 0 .707-.293l.16-.16z'/%3E%3C/svg%3E";
8588

src/client/atom/gameAtom.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { atom, selector } from 'recoil';
22
import socket from 'client/config/socket';
33
import { MAX_SET_TIMER } from 'shared/constant';
44
import { TGame } from 'shared/types';
5+
import { debounce } from 'shared/util';
56
import { accountAtom } from './accountAtom';
67

78
export const gameAtom = atom<TGame>({
@@ -78,23 +79,20 @@ export const isEndAtom = atom({
7879
default: false,
7980
effects: [
8081
({ setSelf, resetSelf }) => {
81-
const RESET_TIMER = 5000;
82-
83-
let timer;
82+
const debouncedReset = debounce(resetSelf, 5000);
8483

8584
const endHandler = () => {
86-
clearTimeout(timer);
8785
setSelf(true);
88-
timer = setTimeout(resetSelf, RESET_TIMER);
86+
debouncedReset();
8987
};
9088

9189
socket.on('game/end', endHandler);
92-
socket.on('room/leave', resetSelf)
90+
socket.on('room/leave', resetSelf);
9391
socket.on('game/set/start', resetSelf);
94-
92+
9593
return () => {
9694
socket.off('game/end', endHandler);
97-
socket.off('room/leave', resetSelf)
95+
socket.off('room/leave', resetSelf);
9896
socket.off('game/set/start', resetSelf);
9997
};
10098
},

src/client/atom/miscAtom.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { atom } from 'recoil';
22
import socket from 'client/config/socket';
3-
import { LOGIN_ALERT_MESSAGE, ROOM_ALERT_MESSAGE } from 'shared/constant';
3+
import { LANDSCAPE_WIDTH, LOGIN_ALERT_MESSAGE, ROOM_ALERT_MESSAGE } from 'shared/constant';
4+
import { debounce } from 'shared/util';
45

56
export const loginAlertAtom = atom({
67
key: 'loginAlert',
@@ -70,6 +71,8 @@ export const zoomOutAlertAtom = atom({
7071
default: visualViewport.width < 499,
7172
effects: [
7273
({ setSelf }) => {
74+
if (visualViewport.width > 499) return;
75+
7376
const evCache: Array<PointerEvent> = [];
7477
let prevDiff = -1;
7578

@@ -130,14 +133,11 @@ export const zoomOutAlertAtom = atom({
130133

131134
export const isPortraitAtom = atom({
132135
key: 'isPortrait',
133-
default: window.innerWidth < 800,
136+
default: window.innerWidth < LANDSCAPE_WIDTH,
134137
effects: [
135138
({ setSelf }) => {
136-
let timer;
137-
const resizeHandler = () => {
138-
clearTimeout(timer);
139-
timer = setTimeout(() => setSelf(window.innerWidth < 800), 100);
140-
};
139+
const resizeHandler = debounce(() => setSelf(window.innerWidth < LANDSCAPE_WIDTH), 100);
140+
141141
window.addEventListener('resize', resizeHandler);
142142

143143
return () => {

src/client/components/Main.tsx

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import styled from 'styled-components';
2-
import { lazy, Suspense, useEffect } from 'react';
2+
import { lazy, Suspense, useCallback, useEffect } from 'react';
33
import { useRecoilValue, useSetRecoilState } from 'recoil';
44
import { accountAtom } from 'client/atom/accountAtom';
55
import { leftSpaceAtom } from 'client/atom/canvasAtom';
66
import { currentRoomIndexAtom } from 'client/atom/roomAtom';
7+
import { LANDSCAPE_WIDTH, PORTRAIT_WIDTH } from 'shared/constant';
8+
import { debounce, throttle } from 'shared/util';
79
import Entrance from './entrance/Entrance';
810
const Lobby = lazy(() => import('./lobby/Lobby'));
911
const Game = lazy(() => import('./game/Game'));
1012

1113
const MainWrapper = styled.div`
1214
position: relative;
1315
14-
width: 800px;
16+
width: ${LANDSCAPE_WIDTH}px;
1517
height: 600px;
1618
17-
@media screen and (max-width: 800px) {
18-
width: 500px;
19+
@media screen and (max-width: ${LANDSCAPE_WIDTH}px) {
20+
width: ${PORTRAIT_WIDTH}px;
1921
min-height: 700px;
2022
height: calc(var(--vh, 1vh) * 100);
2123
}
@@ -26,36 +28,30 @@ const MainWrapper = styled.div`
2628
user-select: none;
2729
`;
2830

29-
const throttle = (callback, delay) => {
30-
let previousCall = new Date().getTime();
31-
32-
return (...args) => {
33-
const time = new Date().getTime();
34-
35-
if (time - previousCall >= delay) {
36-
previousCall = time;
37-
callback(...args);
38-
}
39-
};
40-
};
41-
4231
const Main = () => {
4332
const isLogined = useRecoilValue(accountAtom) !== null;
4433
const isInRoom = useRecoilValue(currentRoomIndexAtom) !== null;
4534
const setLeftSpace = useSetRecoilState(leftSpaceAtom);
4635

47-
useEffect(() => {
48-
let timer;
36+
const setHeight = useCallback(
37+
throttle(() => document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`), 20),
38+
[]
39+
);
40+
41+
const setLeft = useCallback(
42+
debounce(() => {
43+
const width = window.innerWidth;
44+
if (width > LANDSCAPE_WIDTH) setLeftSpace((width - LANDSCAPE_WIDTH) / 2);
45+
else if (width > PORTRAIT_WIDTH) setLeftSpace((width - PORTRAIT_WIDTH) / 2);
46+
}, 250),
47+
[]
48+
);
4949

50-
const resizeHandler = throttle(() => {
51-
document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
52-
clearTimeout(timer);
53-
timer = setTimeout(() => {
54-
const width = window.innerWidth;
55-
if (width > 800) setLeftSpace((width - 800) / 2);
56-
else if (width > 500) setLeftSpace((width - 500) / 2);
57-
}, 250);
58-
}, 100);
50+
useEffect(() => {
51+
const resizeHandler = () => {
52+
setHeight();
53+
setLeft();
54+
};
5955

6056
window.addEventListener('resize', resizeHandler);
6157

@@ -66,7 +62,7 @@ const Main = () => {
6662

6763
return (
6864
<MainWrapper>
69-
<Suspense fallback={<></>}>{isLogined ? isInRoom ? <Game /> : <Lobby /> : <Entrance />}</Suspense>
65+
<Suspense fallback={null}>{isLogined ? isInRoom ? <Game /> : <Lobby /> : <Entrance />}</Suspense>
7066
</MainWrapper>
7167
);
7268
};

src/client/components/common/Playing.tsx

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import styled from 'styled-components';
2-
import { BsBrush, BsEasel } from 'react-icons/bs';
32

43
const PlayingWrapper = styled.div`
54
display: flex;
65
justify-content: center;
76
align-items: center;
87
`;
9-
const Brush = styled.div`
8+
const BrushWrapper = styled.div`
109
position: absolute;
1110
top: -5px;
1211
left: 5px;
@@ -37,11 +36,37 @@ const Brush = styled.div`
3736

3837
const Playing = ({ size }: { size: number }) => (
3938
<PlayingWrapper title="게임중">
40-
<BsEasel size={size} />
41-
<Brush>
42-
<BsBrush size={size * 0.75} />
43-
</Brush>
39+
<Easel size={size} />
40+
<BrushWrapper>
41+
<Brush size={size * 0.75} />
42+
</BrushWrapper>
4443
</PlayingWrapper>
4544
);
4645

46+
const Easel = ({ size }: { size: number }) => (
47+
<svg
48+
xmlns="http://www.w3.org/2000/svg"
49+
width={size}
50+
height={size}
51+
fill="currentColor"
52+
className="bi bi-easel"
53+
viewBox="0 0 16 16"
54+
>
55+
<path d="M8 0a.5.5 0 0 1 .473.337L9.046 2H14a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-1.85l1.323 3.837a.5.5 0 1 1-.946.326L11.092 11H8.5v3a.5.5 0 0 1-1 0v-3H4.908l-1.435 4.163a.5.5 0 1 1-.946-.326L3.85 11H2a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h4.954L7.527.337A.5.5 0 0 1 8 0zM2 3v7h12V3H2z" />
56+
</svg>
57+
);
58+
59+
const Brush = ({ size }: { size: number }) => (
60+
<svg
61+
xmlns="http://www.w3.org/2000/svg"
62+
width={size}
63+
height={size}
64+
fill="currentColor"
65+
className="bi bi-brush"
66+
viewBox="0 0 16 16"
67+
>
68+
<path d="M15.825.12a.5.5 0 0 1 .132.584c-1.53 3.43-4.743 8.17-7.095 10.64a6.067 6.067 0 0 1-2.373 1.534c-.018.227-.06.538-.16.868-.201.659-.667 1.479-1.708 1.74a8.118 8.118 0 0 1-3.078.132 3.659 3.659 0 0 1-.562-.135 1.382 1.382 0 0 1-.466-.247.714.714 0 0 1-.204-.288.622.622 0 0 1 .004-.443c.095-.245.316-.38.461-.452.394-.197.625-.453.867-.826.095-.144.184-.297.287-.472l.117-.198c.151-.255.326-.54.546-.848.528-.739 1.201-.925 1.746-.896.126.007.243.025.348.048.062-.172.142-.38.238-.608.261-.619.658-1.419 1.187-2.069 2.176-2.67 6.18-6.206 9.117-8.104a.5.5 0 0 1 .596.04zM4.705 11.912a1.23 1.23 0 0 0-.419-.1c-.246-.013-.573.05-.879.479-.197.275-.355.532-.5.777l-.105.177c-.106.181-.213.362-.32.528a3.39 3.39 0 0 1-.76.861c.69.112 1.736.111 2.657-.12.559-.139.843-.569.993-1.06a3.122 3.122 0 0 0 .126-.75l-.793-.792zm1.44.026c.12-.04.277-.1.458-.183a5.068 5.068 0 0 0 1.535-1.1c1.9-1.996 4.412-5.57 6.052-8.631-2.59 1.927-5.566 4.66-7.302 6.792-.442.543-.795 1.243-1.042 1.826-.121.288-.214.54-.275.72v.001l.575.575zm-4.973 3.04.007-.005a.031.031 0 0 1-.007.004zm3.582-3.043.002.001h-.002z" />
69+
</svg>
70+
);
71+
4772
export default Playing;

0 commit comments

Comments
 (0)