From 57e11607504fec1faca80964f2033ff1cb6c4806 Mon Sep 17 00:00:00 2001 From: Cola Date: Sat, 26 Nov 2022 17:26:39 +0900 Subject: [PATCH 001/191] =?UTF-8?q?chore:=20immer=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package.json | 1 + yarn.lock | 57 +++++++-------------------------------------- 2 files changed, 10 insertions(+), 48 deletions(-) diff --git a/client/package.json b/client/package.json index b22b0ec2..091a000c 100644 --- a/client/package.json +++ b/client/package.json @@ -15,6 +15,7 @@ "@tanstack/react-query": "^4.16.1", "axios": "^1.1.3", "classnames": "^2.3.2", + "immer": "^9.0.16", "react": "^18.2.0", "react-custom-scrollbars-2": "^4.5.0", "react-dom": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index df7766c3..35d89cf4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2534,14 +2534,6 @@ multer "1.4.4-lts.1" tslib "2.4.1" -"@nestjs/platform-socket.io@^9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@nestjs/platform-socket.io/-/platform-socket.io-9.2.0.tgz#5194a13d4ef5c70b32b2bcc64379e07674ddf0ab" - integrity sha512-ttxXtqHV3Cpk5AfZOxfE8urILV5oLBpG21vdyqUHiL0YDuhHdc2tBz5GKSYAfsWefmVeQQiBAV9dqaa23Rf0nQ== - dependencies: - socket.io "4.5.3" - tslib "2.4.1" - "@nestjs/schematics@^9.0.0": version "9.0.3" resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-9.0.3.tgz#175218350fb3829c9a903e980046a11950310e24" @@ -2560,15 +2552,6 @@ dependencies: tslib "2.4.1" -"@nestjs/websockets@^9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@nestjs/websockets/-/websockets-9.2.0.tgz#cbe8d446eff653d9c63234ef396ccc1ea031e875" - integrity sha512-AbG4eN9p9O6QmNSOWsk0lrA+CtHkrdDkogcl1sGyTrg+LRd6IUlkaTu9fFK9Hl6o7bs2ieGgDmxAvl+Xd156Aw== - dependencies: - iterare "1.2.1" - object-hash "3.0.0" - tslib "2.4.1" - "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": version "2.1.8-no-fsevents.3" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" @@ -2804,11 +2787,6 @@ "@types/node" ">=12.0.0" axios "^0.21.4" -"@socket.io/component-emitter@~3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" - integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== - "@tanstack/match-sorter-utils@8.1.1": version "8.1.1" resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.1.1.tgz#895f407813254a46082a6bbafad9b39b943dc834" @@ -2991,11 +2969,6 @@ resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== -"@types/cors@^2.8.12": - version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== - "@types/debug@^4.1.7": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" @@ -3157,7 +3130,7 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== -"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=12.0.0", "@types/node@>=8.9.0": +"@types/node@*", "@types/node@>=12.0.0", "@types/node@>=8.9.0": version "18.11.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== @@ -3281,13 +3254,6 @@ dependencies: "@types/node" "*" -"@types/socket.io@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-3.0.2.tgz#606c9639e3f93bb8454cba8f5f0a283d47917759" - integrity sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ== - dependencies: - socket.io "*" - "@types/sockjs@^0.3.33": version "0.3.33" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" @@ -4132,11 +4098,6 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base64id@2.0.0, base64id@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" - integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== - batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -4723,7 +4684,7 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookie@^0.4.1, cookie@^0.4.2, cookie@~0.4.1: +cookie@^0.4.1, cookie@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== @@ -4762,7 +4723,7 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@2.8.5, cors@~2.8.5: +cors@2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -4972,7 +4933,7 @@ debug@2.6.9, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@4.x, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4, debug@4.x, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -6669,6 +6630,11 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +immer@^9.0.16: + version "9.0.16" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.16.tgz#8e7caab80118c2b54b37ad43e05758cdefad0198" + integrity sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -11624,11 +11590,6 @@ ws@^5.2.0: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@~8.2.3: - version "8.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" - integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== - xml-name-validator@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" From c00c943b18e2e1c31c96b2725eb4a496ae6f272e Mon Sep 17 00:00:00 2001 From: Cola Date: Sat, 26 Nov 2022 17:27:26 +0900 Subject: [PATCH 002/191] =?UTF-8?q?chore:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EC=83=9D=EC=84=B1=20Mock=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/handlers/Community.js | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 client/src/mocks/handlers/Community.js diff --git a/client/src/mocks/handlers/Community.js b/client/src/mocks/handlers/Community.js new file mode 100644 index 00000000..a8cd7e97 --- /dev/null +++ b/client/src/mocks/handlers/Community.js @@ -0,0 +1,47 @@ +import { API_URL } from '@constants/url'; +import { rest } from 'msw'; + +const BASE_URL = `${API_URL}/api`; + +// 커뮤니티 생성 +const createCommunity = rest.post( + `${BASE_URL}/community`, + async (req, res, ctx) => { + const ERROR = false; + const { name, description } = await req.json(); + + const successResponse = res( + ctx.status(201), + ctx.delay(500), + ctx.json({ + statusCode: 201, + result: { + name, + managerId: '6379beb15d4f08bbe0c940e9', + description, + profileUrl: 'request profileUrl', + createdAt: '2022-11-21T10:07:14.390Z', + updatedAt: '2022-11-21T10:07:14.390Z', + channels: [], + users: ['6379beb15d4f08bbe0c940e9'], + _id: '637b4dd7ec4ba00e3e288930', + __v: 0, + }, + }), + ); + + const errorResponse = res( + ctx.status(401), + ctx.delay(500), + ctx.json({ + statusCode: 400, + messages: '에러가 발생했습니다!', + error: '', + }), + ); + + return ERROR ? errorResponse : successResponse; + }, +); + +export default [createCommunity]; From 6dae002971991ab99650ebb24c03bda6c8dfe2cc Mon Sep 17 00:00:00 2001 From: Cola Date: Sat, 26 Nov 2022 17:28:14 +0900 Subject: [PATCH 003/191] =?UTF-8?q?feat:=20createCommunity=20api=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/community.ts | 39 ++++++++++++++++++++++++++++++ client/src/mocks/handlers/index.js | 2 ++ 2 files changed, 41 insertions(+) create mode 100644 client/src/apis/community.ts diff --git a/client/src/apis/community.ts b/client/src/apis/community.ts new file mode 100644 index 00000000..0b883dde --- /dev/null +++ b/client/src/apis/community.ts @@ -0,0 +1,39 @@ +import type { SuccessResponse } from '@@types/apis/response'; +import type { User } from '@apis/user'; + +import { tokenAxios } from '@utils/axios'; + +export interface CreateCommunityRequest { + name: string; + description: string; + profileUrl?: string; +} + +export interface CreateCommunityResult { + name: string; + managerId: string; + description: string; + profileUrl: string; + createdAt: string; + updatedAt: string; + channels: []; + users: Array; + _id: string; + __v: 0; +} + +export type CreateCommunity = ( + fields: CreateCommunityRequest, +) => Promise>; + +export const createCommunity: CreateCommunity = ({ + name, + description, + profileUrl = '', +}) => { + const endPoint = `/api/community`; + + return tokenAxios + .post(endPoint, { name, description, profileUrl }) + .then((response) => response.data); +}; diff --git a/client/src/mocks/handlers/index.js b/client/src/mocks/handlers/index.js index a411c0e4..28c2b2b9 100644 --- a/client/src/mocks/handlers/index.js +++ b/client/src/mocks/handlers/index.js @@ -1,4 +1,5 @@ import AuthHandlers from './Auth'; +import CommunityHandlers from './Community'; import DMHandlers from './DM'; import FriendHandlers from './Friend'; import UserHandlers from './User'; @@ -8,4 +9,5 @@ export const handlers = [ ...FriendHandlers, ...UserHandlers, ...DMHandlers, + ...CommunityHandlers, ]; From 90cd3c5bc2b5b8c67da13cbb2142536e8eec366b Mon Sep 17 00:00:00 2001 From: Cola Date: Sat, 26 Nov 2022 17:29:46 +0900 Subject: [PATCH 004/191] =?UTF-8?q?feat:=20creatCommunityModalSlice=20?= =?UTF-8?q?=EC=8A=AC=EB=9D=BC=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/stores/createCommunityModalSlice.ts | 30 +++++++++++++++++++ client/src/stores/modalSlice.ts | 15 ---------- client/src/stores/rootStore.ts | 10 +++---- 3 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 client/src/stores/createCommunityModalSlice.ts delete mode 100644 client/src/stores/modalSlice.ts diff --git a/client/src/stores/createCommunityModalSlice.ts b/client/src/stores/createCommunityModalSlice.ts new file mode 100644 index 00000000..892c3646 --- /dev/null +++ b/client/src/stores/createCommunityModalSlice.ts @@ -0,0 +1,30 @@ +import type { StateCreator } from 'zustand'; + +import { immer } from 'zustand/middleware/immer'; + +export interface CreateCommunityModalSlice { + createCommunityModal: { + isOpen: boolean; + }; + openCreateCommunityModal: () => void; + closeCreateCommunityModal: () => void; +} + +export const createCommunityModalSlice: StateCreator< + CreateCommunityModalSlice, + [], + [['zustand/immer', never], ...[]], + CreateCommunityModalSlice +> = immer((set) => ({ + createCommunityModal: { + isOpen: false, + }, + openCreateCommunityModal: () => + set((state) => { + state.createCommunityModal.isOpen = true; + }), + closeCreateCommunityModal: () => + set((state) => { + state.createCommunityModal.isOpen = false; + }), +})); diff --git a/client/src/stores/modalSlice.ts b/client/src/stores/modalSlice.ts deleted file mode 100644 index 4fca1f10..00000000 --- a/client/src/stores/modalSlice.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { StateCreator } from 'zustand'; - -export interface ModalSlice { - createCommunityModal: { - open: boolean; - }; -} - -export const createModalSlice: StateCreator = ( - set, -) => ({ - createCommunityModal: { - open: false, - }, -}); diff --git a/client/src/stores/rootStore.ts b/client/src/stores/rootStore.ts index f4421b24..9503dc7b 100644 --- a/client/src/stores/rootStore.ts +++ b/client/src/stores/rootStore.ts @@ -1,14 +1,14 @@ -import type { ModalSlice } from '@stores/modalSlice'; +import type { CreateCommunityModalSlice } from '@stores/createCommunityModalSlice'; import store from 'zustand'; import { devtools } from 'zustand/middleware'; -import { createModalSlice } from './modalSlice'; +import { createCommunityModalSlice } from './createCommunityModalSlice'; -export type Store = ModalSlice; +export type Store = CreateCommunityModalSlice; -export const useStore = store()( +export const useRootStore = store()( devtools((...a) => ({ - ...createModalSlice(...a), + ...createCommunityModalSlice(...a), })), ); From 74ff3dc51127c72b767e56bdfc36a78cf0269ec9 Mon Sep 17 00:00:00 2001 From: Cola Date: Sat, 26 Nov 2022 17:30:19 +0900 Subject: [PATCH 005/191] =?UTF-8?q?feat:=20createCommunityModal=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modals/CreateCommunityModal/index.tsx | 170 +++++++++++++++++- client/src/layouts/Gnb/index.tsx | 6 +- 2 files changed, 174 insertions(+), 2 deletions(-) diff --git a/client/src/components/Modals/CreateCommunityModal/index.tsx b/client/src/components/Modals/CreateCommunityModal/index.tsx index 75daa3d7..e81fd353 100644 --- a/client/src/components/Modals/CreateCommunityModal/index.tsx +++ b/client/src/components/Modals/CreateCommunityModal/index.tsx @@ -1,10 +1,178 @@ +import type { CSSProperties } from 'react'; + +import Button from '@components/Button'; +import ErrorMessage from '@components/ErrorMessage'; +import Input from '@components/Input'; +import SuccessMessage from '@components/SuccessMessage'; +import { useCreateCommunityMutation } from '@hooks/community'; +import { useRootStore } from '@stores/rootStore'; import React from 'react'; +import { useForm, Controller } from 'react-hook-form'; import ReactModal from 'react-modal'; +interface CreateCommunityFormFields { + communityName: string; + communityDescription: string; +} + +const createCommunityFormDefaultValue = { + communityName: '', + communityDescription: '', +}; + +const modalContentStyle: CSSProperties = { + width: 350, + height: 300, + borderRadius: 10, + padding: 20, + left: '50%', + top: '50%', + transform: 'translate3d(-50%, -50%, 0)', +}; + +const modalOverlayStyle: CSSProperties = { + background: 'rgba(0, 0, 0, 0.5)', +}; + interface Props {} +ReactModal.setAppElement('#root'); + const CreateCommunityModal: React.FC = () => { - return 커뮤니티 모달; + const { isOpen } = useRootStore((state) => state.createCommunityModal); + const closeCreateCommunityModal = useRootStore( + (state) => state.closeCreateCommunityModal, + ); + const createCommunityMutation = useCreateCommunityMutation({ + onSuccess: () => { + console.log('커뮤니티 생성 완료!'); + // eslint-disable-next-line no-use-before-define + handleCloseModal(); + }, + }); + + const { control, handleSubmit, reset } = useForm({ + mode: 'all', + defaultValues: createCommunityFormDefaultValue, + }); + + const handleCloseModal = () => { + if (createCommunityMutation.isLoading) return; // Form 제출 처리중엔 취소할 수 없음. + closeCreateCommunityModal(); + reset(); + }; + + const handleSubmitCreateCommunityForm = ( + fields: CreateCommunityFormFields, + ) => { + const { communityName: name, communityDescription: description } = fields; + + createCommunityMutation.mutate({ name, description }); + }; + + return ( + +
+
+

커뮤니티 만들기

+
+
+ + (value.length >= 2 && value.length <= 20) || + '커뮤니티 이름은 2자 이상, 20자 이하만 가능합니다!', + }, + }} + render={({ field, formState: { errors } }) => { + return ( +
+ +
+ {errors?.communityName ? ( + + {errors.communityName.message} + + ) : ( + field.value && ( + + 사용 가능한 이름입니다! + + ) + )} +
+
+ ); + }} + /> + + value.length <= 20 || '설명은 20자 이하만 가능합니다!', + }, + }} + render={({ field, formState: { errors } }) => { + return ( +
+ +
+ {errors?.communityDescription && ( + + {errors.communityDescription.message} + + )} +
+
+ ); + }} + /> +
+
+

액션 버튼 그룹

+ + +
+
+
+ ); }; export default CreateCommunityModal; diff --git a/client/src/layouts/Gnb/index.tsx b/client/src/layouts/Gnb/index.tsx index 847b8700..613a9b56 100644 --- a/client/src/layouts/Gnb/index.tsx +++ b/client/src/layouts/Gnb/index.tsx @@ -2,11 +2,15 @@ import Avatar from '@components/Avatar'; import GnbItemContainer from '@components/GnbItemContainer'; import { LOGO_IMG_URL } from '@constants/url'; import { PlusIcon } from '@heroicons/react/24/solid'; +import { useRootStore } from '@stores/rootStore'; import React from 'react'; import { Link, useLocation } from 'react-router-dom'; const Gnb = () => { const { pathname } = useLocation(); + const openCreateCommunityModal = useRootStore( + (state) => state.openCreateCommunityModal, + ); return (
@@ -37,7 +41,7 @@ const Gnb = () => { -
); From a65d18ff41f8f500503ed06c21478de3d8ab5f8d Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 03:20:07 +0900 Subject: [PATCH 022/191] =?UTF-8?q?feat:=20Community=20invalidateQueries?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 추가함에 따라 반환 타입이 달라져서 이 훅을 사용하는 컴포넌트 리팩토링 --- client/src/hooks/community.ts | 10 ++++++++-- client/src/layouts/CommunityNav/index.tsx | 2 +- client/src/layouts/Gnb/index.tsx | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/client/src/hooks/community.ts b/client/src/hooks/community.ts index e0c4db74..01c3ff7e 100644 --- a/client/src/hooks/community.ts +++ b/client/src/hooks/community.ts @@ -8,14 +8,20 @@ import type { UseMutationOptions } from '@tanstack/react-query'; import type { AxiosError } from 'axios'; import { createCommunity, getCommunities } from '@apis/community'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useCallback } from 'react'; import queryKeyCreator from 'src/queryKeyCreator'; export const useCommunitiesQuery = () => { + const queryClient = useQueryClient(); + const key = queryKeyCreator.community.all(); const query = useQuery(key, getCommunities); + const invalidate = useCallback(() => { + queryClient.invalidateQueries(key); + }, [queryClient, key]); - return query; + return { communitiesQuery: query, invalidateCommunitiesQuery: invalidate }; }; export const useCreateCommunityMutation = ( diff --git a/client/src/layouts/CommunityNav/index.tsx b/client/src/layouts/CommunityNav/index.tsx index 6f13ff3b..2e150ef1 100644 --- a/client/src/layouts/CommunityNav/index.tsx +++ b/client/src/layouts/CommunityNav/index.tsx @@ -4,7 +4,7 @@ import { useParams } from 'react-router-dom'; const CommunityNav = () => { const { communityId } = useParams(); - const communitiesQuery = useCommunitiesQuery(); + const { communitiesQuery } = useCommunitiesQuery(); const communitySummary = communitiesQuery.data?.find( ({ _id }) => _id === communityId, ); diff --git a/client/src/layouts/Gnb/index.tsx b/client/src/layouts/Gnb/index.tsx index b749970a..d7d9d1d0 100644 --- a/client/src/layouts/Gnb/index.tsx +++ b/client/src/layouts/Gnb/index.tsx @@ -14,7 +14,7 @@ const Gnb = () => { const openCreateCommunityModal = useRootStore( (state) => state.openCreateCommunityModal, ); - const communitiesQuery = useCommunitiesQuery(); + const { communitiesQuery } = useCommunitiesQuery(); return (
From d79ad1816ac6bc5a4b1cae0f8467b173bc750cc1 Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 03:22:46 +0900 Subject: [PATCH 023/191] =?UTF-8?q?feat:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EC=83=9D=EC=84=B1=EC=8B=9C=20Communities=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EA=B0=B1=EC=8B=A0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modals/CreateCommunityModal/index.tsx | 8 +++- client/src/mocks/handlers/Community.js | 39 +++++++++++++------ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/client/src/components/Modals/CreateCommunityModal/index.tsx b/client/src/components/Modals/CreateCommunityModal/index.tsx index b3d9fa64..b6306239 100644 --- a/client/src/components/Modals/CreateCommunityModal/index.tsx +++ b/client/src/components/Modals/CreateCommunityModal/index.tsx @@ -4,7 +4,10 @@ import Button from '@components/Button'; import ErrorMessage from '@components/ErrorMessage'; import Input from '@components/Input'; import SuccessMessage from '@components/SuccessMessage'; -import { useCreateCommunityMutation } from '@hooks/community'; +import { + useCommunitiesQuery, + useCreateCommunityMutation, +} from '@hooks/community'; import { useRootStore } from '@stores/rootStore'; import React from 'react'; import { useForm, Controller } from 'react-hook-form'; @@ -43,9 +46,10 @@ const CreateCommunityModal: React.FC = () => { const closeCreateCommunityModal = useRootStore( (state) => state.closeCreateCommunityModal, ); + const { invalidateCommunitiesQuery } = useCommunitiesQuery(); const createCommunityMutation = useCreateCommunityMutation({ onSuccess: () => { - console.log('커뮤니티 생성 완료!'); + invalidateCommunitiesQuery(); // eslint-disable-next-line no-use-before-define handleCloseModal(); }, diff --git a/client/src/mocks/handlers/Community.js b/client/src/mocks/handlers/Community.js index 424a632e..376cc7be 100644 --- a/client/src/mocks/handlers/Community.js +++ b/client/src/mocks/handlers/Community.js @@ -31,23 +31,38 @@ const CreateCommunity = rest.post( const ERROR = false; const { name, description } = await req.json(); + const newCommunity = { + name, + managerId: '6379beb15d4f08bbe0c940e9', + description, + profileUrl: '', + createdAt: '2022-11-21T10:07:14.390Z', + updatedAt: '2022-11-21T10:07:14.390Z', + channels: [], + users: ['6379beb15d4f08bbe0c940e9'], + _id: crypto.randomUUID(), + __v: 0, + }; + const successResponse = res( - ...createSuccessContext(ctx, 201, 500, { - name, - managerId: '6379beb15d4f08bbe0c940e9', - description, - profileUrl: 'request profileUrl', - createdAt: '2022-11-21T10:07:14.390Z', - updatedAt: '2022-11-21T10:07:14.390Z', - channels: [], - users: ['6379beb15d4f08bbe0c940e9'], - _id: '637b4dd7ec4ba00e3e288930', - __v: 0, - }), + ...createSuccessContext(ctx, 201, 500, newCommunity), ); const errorResponse = res(...createErrorContext(ctx)); + if (!ERROR) { + // eslint-disable-next-line no-shadow + const { name, _id, managerId, profileUrl, description } = newCommunity; + + communities.push({ + name, + _id, + managerId, + profileUrl, + description, + }); + } + return ERROR ? errorResponse : successResponse; }, ); From 34e6a24152acc7402b8e1ae67949caa079f2a411 Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 03:49:42 +0900 Subject: [PATCH 024/191] =?UTF-8?q?feat:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EC=83=9D=EC=84=B1=20=ED=9B=84=20=ED=95=B4=EB=8B=B9?= =?UTF-8?q?=20=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=A1=9C=20navigate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/community.ts | 2 +- .../Modals/CreateCommunityModal/index.tsx | 19 +++++++++++++++++-- client/src/hooks/community.ts | 5 ++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/client/src/apis/community.ts b/client/src/apis/community.ts index bd54dd52..98e21ae6 100644 --- a/client/src/apis/community.ts +++ b/client/src/apis/community.ts @@ -39,7 +39,7 @@ export interface CreateCommunityResult extends CommunitySummary { export type CreateCommunity = ( fields: CreateCommunityRequest, -) => Promise>; +) => Promise; export const createCommunity: CreateCommunity = ({ name, diff --git a/client/src/components/Modals/CreateCommunityModal/index.tsx b/client/src/components/Modals/CreateCommunityModal/index.tsx index b6306239..d41164c4 100644 --- a/client/src/components/Modals/CreateCommunityModal/index.tsx +++ b/client/src/components/Modals/CreateCommunityModal/index.tsx @@ -4,6 +4,7 @@ import Button from '@components/Button'; import ErrorMessage from '@components/ErrorMessage'; import Input from '@components/Input'; import SuccessMessage from '@components/SuccessMessage'; +import defaultErrorHandler from '@errors/defaultErrorHandler'; import { useCommunitiesQuery, useCreateCommunityMutation, @@ -12,6 +13,8 @@ import { useRootStore } from '@stores/rootStore'; import React from 'react'; import { useForm, Controller } from 'react-hook-form'; import ReactModal from 'react-modal'; +import { useNavigate } from 'react-router-dom'; +import { toast } from 'react-toastify'; interface CreateCommunityFormFields { communityName: string; @@ -46,13 +49,25 @@ const CreateCommunityModal: React.FC = () => { const closeCreateCommunityModal = useRootStore( (state) => state.closeCreateCommunityModal, ); + const navigate = useNavigate(); const { invalidateCommunitiesQuery } = useCommunitiesQuery(); const createCommunityMutation = useCreateCommunityMutation({ - onSuccess: () => { - invalidateCommunitiesQuery(); + onSuccess: ({ _id }) => { + invalidateCommunitiesQuery() + .then(() => { + navigate(`/communities/${_id}`); + }) + .catch((error) => { + console.error(error); + toast.error('커뮤니티는 생성되었지만, 불러오는데 실패했습니다.'); + }); + // eslint-disable-next-line no-use-before-define handleCloseModal(); }, + onError: (error) => { + defaultErrorHandler(error); + }, }); const { control, handleSubmit, reset } = useForm({ diff --git a/client/src/hooks/community.ts b/client/src/hooks/community.ts index 01c3ff7e..4d13ca39 100644 --- a/client/src/hooks/community.ts +++ b/client/src/hooks/community.ts @@ -1,4 +1,3 @@ -import type { SuccessResponse } from '@@types/apis/response'; import type { CreateCommunityResult, CreateCommunityRequest, @@ -18,7 +17,7 @@ export const useCommunitiesQuery = () => { const key = queryKeyCreator.community.all(); const query = useQuery(key, getCommunities); const invalidate = useCallback(() => { - queryClient.invalidateQueries(key); + return queryClient.invalidateQueries(key); }, [queryClient, key]); return { communitiesQuery: query, invalidateCommunitiesQuery: invalidate }; @@ -26,7 +25,7 @@ export const useCommunitiesQuery = () => { export const useCreateCommunityMutation = ( options: UseMutationOptions< - SuccessResponse, + CreateCommunityResult, unknown, CreateCommunityRequest >, From f6776b6002fa0da51d1539e52d1eed891c615659 Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 04:04:50 +0900 Subject: [PATCH 025/191] =?UTF-8?q?fix-design:=20=EC=95=84=EB=B0=94?= =?UTF-8?q?=ED=83=80=20img=20style=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Avatar/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Avatar/index.tsx b/client/src/components/Avatar/index.tsx index e04a5ed7..9930d27a 100644 --- a/client/src/components/Avatar/index.tsx +++ b/client/src/components/Avatar/index.tsx @@ -50,7 +50,7 @@ const Avatar: React.FC = ({ {!children && (url ? ( {`${name}의 From 929f6de518e57041829d4c59405553b779cce9b4 Mon Sep 17 00:00:00 2001 From: leegwae Date: Sun, 27 Nov 2022 16:19:40 +0900 Subject: [PATCH 026/191] =?UTF-8?q?feat:=20=EB=9E=9C=EB=8D=A4=20=EB=B6=88?= =?UTF-8?q?=20=EA=B0=92=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8A=94=20`getRando?= =?UTF-8?q?mBool`=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/utils/rand.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/mocks/utils/rand.js b/client/src/mocks/utils/rand.js index a942ffd8..514fb2b9 100644 --- a/client/src/mocks/utils/rand.js +++ b/client/src/mocks/utils/rand.js @@ -2,6 +2,8 @@ export const getRandomInt = (max) => { return Math.floor(Math.random() * max); }; +export const getRandomBool = () => Boolean(getRandomInt(2)); + /** * @param fn {Function} 실행할 함수 * @param percent {number} 0 ~ 100 사이의 숫자 From 760ac1e035ecce5fac922a3d7da031873450656a Mon Sep 17 00:00:00 2001 From: leegwae Date: Sun, 27 Nov 2022 16:23:03 +0900 Subject: [PATCH 027/191] =?UTF-8?q?feat:=20/GET=20`/api/user/community/:co?= =?UTF-8?q?mmunity/channels`=20mock=20API=20=EC=9E=91=EC=84=B1=20#151?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/data/channels.js | 15 +++++++++++++++ client/src/mocks/handlers/Channel.js | 27 +++++++++++++++++++++++++++ client/src/mocks/handlers/index.js | 2 ++ 3 files changed, 44 insertions(+) create mode 100644 client/src/mocks/data/channels.js create mode 100644 client/src/mocks/handlers/Channel.js diff --git a/client/src/mocks/data/channels.js b/client/src/mocks/data/channels.js new file mode 100644 index 00000000..7fa85f3d --- /dev/null +++ b/client/src/mocks/data/channels.js @@ -0,0 +1,15 @@ +import { faker } from '@faker-js/faker'; + +import { chancify, getRandomBool } from '../utils/rand'; + +export const createChannels = () => ({ + id: faker.datatype.uuid(), + managerId: faker.datatype.uuid(), + name: faker.lorem.word(), + users: [...Array(10)].map(() => faker.datatype.uuid()), + profileUrl: chancify(() => faker.image.avatar(), 50), + description: faker.lorem.sentence(1), + isPrivate: getRandomBool(), +}); + +export const channels = [...Array(10)].map(createChannels); diff --git a/client/src/mocks/handlers/Channel.js b/client/src/mocks/handlers/Channel.js new file mode 100644 index 00000000..34204d88 --- /dev/null +++ b/client/src/mocks/handlers/Channel.js @@ -0,0 +1,27 @@ +import { API_URL } from '@constants/url'; +import { rest } from 'msw'; + +import { channels } from '../data/channels'; +import { + createErrorContext, + createSuccessContext, +} from '../utils/createContext'; + +const BASE_URL = `${API_URL}/api`; + +// 채널 목록 가져오기 +const GetChannels = rest.get( + `${BASE_URL}/user/community/:communityId/channels`, + (req, res, ctx) => { + const ERROR = false; + + const errorResponse = res(...createErrorContext(ctx)); + const successResponse = res( + ...createSuccessContext(ctx, 200, 500, channels), + ); + + return ERROR ? errorResponse : successResponse; + }, +); + +export default [GetChannels]; diff --git a/client/src/mocks/handlers/index.js b/client/src/mocks/handlers/index.js index 28c2b2b9..ca5077fc 100644 --- a/client/src/mocks/handlers/index.js +++ b/client/src/mocks/handlers/index.js @@ -1,4 +1,5 @@ import AuthHandlers from './Auth'; +import ChannelHandlers from './Channel'; import CommunityHandlers from './Community'; import DMHandlers from './DM'; import FriendHandlers from './Friend'; @@ -10,4 +11,5 @@ export const handlers = [ ...UserHandlers, ...DMHandlers, ...CommunityHandlers, + ...ChannelHandlers, ]; From c700560908578869fc6557c8f1479477250906b9 Mon Sep 17 00:00:00 2001 From: leegwae Date: Sun, 27 Nov 2022 16:23:20 +0900 Subject: [PATCH 028/191] =?UTF-8?q?feat:=20=EC=B1=84=EB=84=90=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20API=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20#151?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/channel.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 client/src/apis/channel.ts diff --git a/client/src/apis/channel.ts b/client/src/apis/channel.ts new file mode 100644 index 00000000..60becb32 --- /dev/null +++ b/client/src/apis/channel.ts @@ -0,0 +1,26 @@ +import type { SuccessResponse } from '@@types/apis/response'; +import type { User } from '@apis/user'; + +import { tokenAxios } from '@utils/axios'; + +export interface Channel { + id: string; + managerId: User['_id']; + name: string; + users: Array; + profileUrl: string; + description: string; + isPrivate: boolean; +} + +export type GetChannelsResult = Channel[]; +export type GetChannelsResponse = SuccessResponse; +export type GetChannels = (communityId: string) => Promise; + +export const getChannels: GetChannels = (communityId: string) => { + const endPoint = `/api/user/community/${communityId}/channels`; + + return tokenAxios + .get(endPoint) + .then((response) => response.data.result); +}; From e2dd6b1962662d1f9232a30dbd74ecbeb74ff099 Mon Sep 17 00:00:00 2001 From: leegwae Date: Sun, 27 Nov 2022 16:23:43 +0900 Subject: [PATCH 029/191] =?UTF-8?q?feat:=20`useChannelsQuery`=20=ED=9B=85?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=20#151?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/channel.ts | 23 +++++++++++++++++++++++ client/src/queryKeyCreator.ts | 7 +++++++ 2 files changed, 30 insertions(+) create mode 100644 client/src/hooks/channel.ts diff --git a/client/src/hooks/channel.ts b/client/src/hooks/channel.ts new file mode 100644 index 00000000..55039f5d --- /dev/null +++ b/client/src/hooks/channel.ts @@ -0,0 +1,23 @@ +import type { GetChannelsResult } from '@apis/channel'; +import type { AxiosError } from 'axios'; + +import { getChannels } from '@apis/channel'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useCallback } from 'react'; + +import queryKeyCreator from '@/queryKeyCreator'; + +export const useChannelsQuery = (communityId: string) => { + const queryClient = useQueryClient(); + const key = queryKeyCreator.channel.list(communityId); + + const query = useQuery(key, () => + getChannels(communityId), + ); + const invalidate = useCallback( + () => queryClient.invalidateQueries(key), + [queryClient, key], + ); + + return { channelsQuery: query, invalidateChannelsQuery: invalidate }; +}; diff --git a/client/src/queryKeyCreator.ts b/client/src/queryKeyCreator.ts index 084c91aa..e70b090c 100644 --- a/client/src/queryKeyCreator.ts +++ b/client/src/queryKeyCreator.ts @@ -9,6 +9,12 @@ const communityQueryKey = { createCommunity: () => ['createCommunity'] as const, }; +const channelQueryKey = { + all: () => ['channels'] as const, + list: (communityId: string) => + [...channelQueryKey.all(), communityId] as const, +}; + const queryKeyCreator = { me: () => ['me'] as const, signUp: () => ['signUp'] as const, @@ -19,6 +25,7 @@ const queryKeyCreator = { userSearch: (filter: string) => ['userSearch', { filter }], directMessage: directMessageQueryKey, community: communityQueryKey, + channel: channelQueryKey, } as const; export default queryKeyCreator; From b2c840337c77b7e46e940eee9817bb193ae700b6 Mon Sep 17 00:00:00 2001 From: leegwae Date: Sun, 27 Nov 2022 16:23:58 +0900 Subject: [PATCH 030/191] =?UTF-8?q?feat:=20`ChannelItem`=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20#151?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/ChannelItem/index.tsx | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 client/src/components/ChannelItem/index.tsx diff --git a/client/src/components/ChannelItem/index.tsx b/client/src/components/ChannelItem/index.tsx new file mode 100644 index 00000000..73b7eb32 --- /dev/null +++ b/client/src/components/ChannelItem/index.tsx @@ -0,0 +1,31 @@ +import type { ComponentPropsWithoutRef } from 'react'; + +import { HashtagIcon, LockClosedIcon } from '@heroicons/react/20/solid'; +import React from 'react'; + +interface Props extends ComponentPropsWithoutRef<'li'> { + isPrivate?: boolean; + name: string; +} +const ChannelItem: React.FC = ({ isPrivate = true, name }) => { + return ( +
  • +
    + {isPrivate ? ( + <> + 비공개 채널 + + + ) : ( + <> + 공개 채널 + + + )} +
    +
    {name}
    +
  • + ); +}; + +export default ChannelItem; From 4b5d96c35ad425b617482a09be42f21a760a4546 Mon Sep 17 00:00:00 2001 From: leegwae Date: Sun, 27 Nov 2022 16:25:11 +0900 Subject: [PATCH 031/191] =?UTF-8?q?feat:=20`CommunityNav`=EC=97=90=20?= =?UTF-8?q?=ED=98=84=EC=9E=AC=20=EC=B0=B8=EC=97=AC=ED=95=9C=20=EC=B1=84?= =?UTF-8?q?=EB=84=90=20=EB=AA=A9=EB=A1=9D=20=EB=A0=8C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?#151?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/layouts/CommunityNav/index.tsx | 65 ++++++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/client/src/layouts/CommunityNav/index.tsx b/client/src/layouts/CommunityNav/index.tsx index 2e150ef1..8741bf34 100644 --- a/client/src/layouts/CommunityNav/index.tsx +++ b/client/src/layouts/CommunityNav/index.tsx @@ -1,6 +1,14 @@ +import ChannelItem from '@components/ChannelItem'; +import { + ChevronDownIcon, + ChevronRightIcon, + PlusIcon, +} from '@heroicons/react/20/solid'; +import { useChannelsQuery } from '@hooks/channel'; import { useCommunitiesQuery } from '@hooks/community'; -import React from 'react'; -import { useParams } from 'react-router-dom'; +import classNames from 'classnames'; +import React, { useState } from 'react'; +import { useParams, Link } from 'react-router-dom'; const CommunityNav = () => { const { communityId } = useParams(); @@ -9,11 +17,64 @@ const CommunityNav = () => { ({ _id }) => _id === communityId, ); + const { roomId } = useParams(); + const { channelsQuery } = useChannelsQuery(communityId as string); + const [visible, setVisible] = useState(true); + + const handleVisible = () => setVisible(!visible); + return ( ); }; From 56ba2d41ef54e51ee388d6a6ab1d12397ffafb9d Mon Sep 17 00:00:00 2001 From: leegwae Date: Sun, 27 Nov 2022 18:15:22 +0900 Subject: [PATCH 032/191] =?UTF-8?q?refactor:=20`ChannelItem`=EC=9D=84=20`l?= =?UTF-8?q?i`=EC=97=90=EC=84=9C=20`div`=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `ul` 자식은 `li`이어야 함 --- client/src/components/ChannelItem/index.tsx | 8 +++----- client/src/layouts/CommunityNav/index.tsx | 17 +++++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/client/src/components/ChannelItem/index.tsx b/client/src/components/ChannelItem/index.tsx index 73b7eb32..c439b31f 100644 --- a/client/src/components/ChannelItem/index.tsx +++ b/client/src/components/ChannelItem/index.tsx @@ -1,15 +1,13 @@ -import type { ComponentPropsWithoutRef } from 'react'; - import { HashtagIcon, LockClosedIcon } from '@heroicons/react/20/solid'; import React from 'react'; -interface Props extends ComponentPropsWithoutRef<'li'> { +interface Props { isPrivate?: boolean; name: string; } const ChannelItem: React.FC = ({ isPrivate = true, name }) => { return ( -
  • +
    {isPrivate ? ( <> @@ -24,7 +22,7 @@ const ChannelItem: React.FC = ({ isPrivate = true, name }) => { )}
    {name}
    -
  • +
    ); }; diff --git a/client/src/layouts/CommunityNav/index.tsx b/client/src/layouts/CommunityNav/index.tsx index 8741bf34..56638c39 100644 --- a/client/src/layouts/CommunityNav/index.tsx +++ b/client/src/layouts/CommunityNav/index.tsx @@ -56,21 +56,22 @@ const CommunityNav = () => { ) : (
      {channelsQuery.data?.map((channel) => ( - - - + + + + ))}
    )} From 1af2f6390086f95019a41d652cbc952796f5f054 Mon Sep 17 00:00:00 2001 From: leegwae Date: Sun, 27 Nov 2022 18:16:23 +0900 Subject: [PATCH 033/191] =?UTF-8?q?refactor:=20`classNames`=EB=A5=BC=20`cn?= =?UTF-8?q?`=EC=9C=BC=EB=A1=9C=20=EC=B6=95=EC=95=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/layouts/CommunityNav/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/layouts/CommunityNav/index.tsx b/client/src/layouts/CommunityNav/index.tsx index 56638c39..fe389584 100644 --- a/client/src/layouts/CommunityNav/index.tsx +++ b/client/src/layouts/CommunityNav/index.tsx @@ -6,7 +6,7 @@ import { } from '@heroicons/react/20/solid'; import { useChannelsQuery } from '@hooks/channel'; import { useCommunitiesQuery } from '@hooks/community'; -import classNames from 'classnames'; +import cn from 'classnames'; import React, { useState } from 'react'; import { useParams, Link } from 'react-router-dom'; @@ -58,7 +58,7 @@ const CommunityNav = () => { {channelsQuery.data?.map((channel) => (
  • Date: Mon, 28 Nov 2022 00:26:00 +0900 Subject: [PATCH 034/191] =?UTF-8?q?feat:=20/GET=20`/api/users/:userId`=20m?= =?UTF-8?q?ock=20API=20=EC=9E=91=EC=84=B1=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/handlers/User.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/client/src/mocks/handlers/User.js b/client/src/mocks/handlers/User.js index 358eae5e..8ec2d68f 100644 --- a/client/src/mocks/handlers/User.js +++ b/client/src/mocks/handlers/User.js @@ -2,6 +2,10 @@ import { API_URL } from '@constants/url'; import { rest } from 'msw'; import { users } from '../data/users'; +import { + createErrorContext, + createSuccessContext, +} from '../utils/createContext'; const GetFilteredUsers = rest.get(`${API_URL}/api/users`, (req, res, ctx) => { const search = req.url.searchParams.get('search').toUpperCase(); @@ -22,4 +26,13 @@ const GetFilteredUsers = rest.get(`${API_URL}/api/users`, (req, res, ctx) => { ); }); -export default [GetFilteredUsers]; +const GetUser = rest.get(`${API_URL}/api/users/:userId`, (req, res, ctx) => { + const ERROR = false; + + const errorResponse = res(...createErrorContext(ctx)); + const successResponse = res(...createSuccessContext(ctx, 200, 500, users[0])); + + return ERROR ? errorResponse : successResponse; +}); + +export default [GetFilteredUsers, GetUser]; From 260be7db2fa6181093dac67c0580cd9664010f24 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 00:39:21 +0900 Subject: [PATCH 035/191] =?UTF-8?q?feat:=20`useUserQuery`=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/user.ts | 13 +++++++++++++ client/src/hooks/user.ts | 23 +++++++++++++++++++++++ client/src/queryKeyCreator.ts | 6 ++++++ 3 files changed, 42 insertions(+) create mode 100644 client/src/hooks/user.ts diff --git a/client/src/apis/user.ts b/client/src/apis/user.ts index a2e5f6d0..0930f338 100644 --- a/client/src/apis/user.ts +++ b/client/src/apis/user.ts @@ -1,6 +1,7 @@ import type { SuccessResponse } from '@@types/apis/response'; import { API_URL } from '@constants/url'; +import { tokenAxios } from '@utils/axios'; import axios from 'axios'; export type UserStatus = 'online' | 'offline' | 'afk'; @@ -61,3 +62,15 @@ export type GetUsersResponse = SuccessResponse; export const GetUsers = (params: GetUsersParams): Promise => axios.get(`${API_URL}/api/users`, { params }).then((res) => res.data); + +export type GetUserResult = User; +export type GetUserResponse = SuccessResponse; +export type GetUser = (userId: string) => Promise; + +export const getUser: GetUser = (userId: string) => { + const endPoint = `${API_URL}/api/users/${userId}`; + + return tokenAxios + .get(endPoint) + .then((response) => response.data.result); +}; diff --git a/client/src/hooks/user.ts b/client/src/hooks/user.ts new file mode 100644 index 00000000..246ec94d --- /dev/null +++ b/client/src/hooks/user.ts @@ -0,0 +1,23 @@ +import { getUser } from '@apis/user'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useCallback } from 'react'; + +import queryKeyCreator from '@/queryKeyCreator'; + +export const useUserQuery = ( + userId: string, + options: { + enabled?: boolean; + }, +) => { + const queryClient = useQueryClient(); + const key = queryKeyCreator.user.detail(userId); + + const query = useQuery(key, () => getUser(userId), options); + const invalidate = useCallback( + () => queryClient.invalidateQueries(key), + [queryClient, key], + ); + + return { userQuery: query, invalidateUserQuery: invalidate }; +}; diff --git a/client/src/queryKeyCreator.ts b/client/src/queryKeyCreator.ts index e70b090c..b58ede53 100644 --- a/client/src/queryKeyCreator.ts +++ b/client/src/queryKeyCreator.ts @@ -1,3 +1,9 @@ +const userQueryKey = { + all: () => ['users'] as const, + detail: (userId: string) => + [...userQueryKey.all(), 'detail', userId] as const, +}; + const directMessageQueryKey = { all: ['directMessages'] as const, list: () => [...directMessageQueryKey.all] as const, From f520c4abb179dbf7de4615bc34bea25941533966 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 00:39:58 +0900 Subject: [PATCH 036/191] =?UTF-8?q?feat:=20/GET=20`/api/communities/:commu?= =?UTF-8?q?nityId`=20mock=20API=20=EC=9E=91=EC=84=B1=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/handlers/Community.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/client/src/mocks/handlers/Community.js b/client/src/mocks/handlers/Community.js index 376cc7be..3d016b1b 100644 --- a/client/src/mocks/handlers/Community.js +++ b/client/src/mocks/handlers/Community.js @@ -24,6 +24,26 @@ const GetCommunities = rest.get( }, ); +const GetCommunity = rest.get( + `${BASE_URL}/communities/:communityId`, + (req, res, ctx) => { + const { communityId } = req.params; + const ERROR = false; + + const errorResponse = res(...createErrorContext(ctx)); + const successResponse = res( + ...createSuccessContext( + ctx, + 200, + 500, + communities.find((community) => community._id === communityId), + ), + ); + + return ERROR ? errorResponse : successResponse; + }, +); + // 커뮤니티 생성 const CreateCommunity = rest.post( `${BASE_URL}/community`, @@ -67,4 +87,4 @@ const CreateCommunity = rest.post( }, ); -export default [GetCommunities, CreateCommunity]; +export default [GetCommunities, GetCommunity, CreateCommunity]; From 2d2ede45adf165c003c5b58bde41165a3581084e Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 00:53:35 +0900 Subject: [PATCH 037/191] =?UTF-8?q?feat:=20`useCommunityQuery`=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/community.ts | 11 +++++++++++ client/src/hooks/community.ts | 18 +++++++++++++++++- client/src/queryKeyCreator.ts | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/client/src/apis/community.ts b/client/src/apis/community.ts index 98e21ae6..14ad6c2c 100644 --- a/client/src/apis/community.ts +++ b/client/src/apis/community.ts @@ -23,6 +23,17 @@ export const getCommunities: GetCommunities = () => { .then((response) => response.data.result); }; +export type GetCommunityResult = CommunitySummary; +export type GetCommunity = (communityId: string) => Promise; +export type GetCommunityResponse = SuccessResponse; +export const getCommunity: GetCommunity = (communityId: string) => { + const endPoint = `/api/communities/${communityId}`; + + return tokenAxios + .get(endPoint) + .then((response) => response.data.result); +}; + export interface CreateCommunityRequest { name: string; description: string; diff --git a/client/src/hooks/community.ts b/client/src/hooks/community.ts index 4d13ca39..d0dfbcbd 100644 --- a/client/src/hooks/community.ts +++ b/client/src/hooks/community.ts @@ -2,11 +2,12 @@ import type { CreateCommunityResult, CreateCommunityRequest, GetCommunitiesResult, + GetCommunityResult, } from '@apis/community'; import type { UseMutationOptions } from '@tanstack/react-query'; import type { AxiosError } from 'axios'; -import { createCommunity, getCommunities } from '@apis/community'; +import { getCommunity, createCommunity, getCommunities } from '@apis/community'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; import queryKeyCreator from 'src/queryKeyCreator'; @@ -23,6 +24,21 @@ export const useCommunitiesQuery = () => { return { communitiesQuery: query, invalidateCommunitiesQuery: invalidate }; }; +export const useCommunityQuery = (communityId: string) => { + const queryClient = useQueryClient(); + + const key = queryKeyCreator.community.detail(communityId); + const query = useQuery(key, () => + getCommunity(communityId), + ); + const invalidate = useCallback( + () => queryClient.invalidateQueries(key), + [queryClient, key], + ); + + return { communityQuery: query, invalidateCommunityQuery: invalidate }; +}; + export const useCreateCommunityMutation = ( options: UseMutationOptions< CreateCommunityResult, diff --git a/client/src/queryKeyCreator.ts b/client/src/queryKeyCreator.ts index b58ede53..3fe360f4 100644 --- a/client/src/queryKeyCreator.ts +++ b/client/src/queryKeyCreator.ts @@ -13,6 +13,8 @@ const directMessageQueryKey = { const communityQueryKey = { all: () => ['communities'] as const, createCommunity: () => ['createCommunity'] as const, + detail: (communityId: string) => + [...communityQueryKey.all(), 'detail', communityId] as const, }; const channelQueryKey = { From 96e91d8f43f8fb69047659b4ef79910e7f0abb60 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 01:03:45 +0900 Subject: [PATCH 038/191] =?UTF-8?q?feat:=20/GET=20`/api/channels/:channelI?= =?UTF-8?q?d`=20mock=20API=20=EC=9E=91=EC=84=B1=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/handlers/Channel.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/client/src/mocks/handlers/Channel.js b/client/src/mocks/handlers/Channel.js index 34204d88..d8c3ea67 100644 --- a/client/src/mocks/handlers/Channel.js +++ b/client/src/mocks/handlers/Channel.js @@ -1,7 +1,9 @@ import { API_URL } from '@constants/url'; +import { faker } from '@faker-js/faker'; import { rest } from 'msw'; import { channels } from '../data/channels'; +import { users } from '../data/users'; import { createErrorContext, createSuccessContext, @@ -24,4 +26,27 @@ const GetChannels = rest.get( }, ); -export default [GetChannels]; +const GetChannel = rest.get( + `${BASE_URL}/channels/:channelId`, + (req, res, ctx) => { + const { channelId } = req.params; + const ERROR = false; + + const errorResponse = res(...createErrorContext(ctx)); + const successResponse = res( + ...createSuccessContext(ctx, 200, 500, { + ...channels.find((channel) => channel.id === channelId), + communityId: faker.datatype.uuid(), + type: 'Channel', + users: users.slice(3, 10).map((user) => user._id), + chatLists: [], + createdAt: '2022-11-25T17:32:09.085Z', + updatedAt: '2022-11-25T17:32:09.085Z', + }), + ); + + return ERROR ? errorResponse : successResponse; + }, +); + +export default [GetChannels, GetChannel]; From a2d0745ea777e5ed36072f06d5c7378d0b626749 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 01:04:19 +0900 Subject: [PATCH 039/191] =?UTF-8?q?feat:=20`useChannelQuery`=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/channel.ts | 25 +++++++++++++++++++++++-- client/src/hooks/channel.ts | 19 +++++++++++++++++-- client/src/queryKeyCreator.ts | 4 +++- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/client/src/apis/channel.ts b/client/src/apis/channel.ts index 60becb32..06cdf4bd 100644 --- a/client/src/apis/channel.ts +++ b/client/src/apis/channel.ts @@ -3,7 +3,7 @@ import type { User } from '@apis/user'; import { tokenAxios } from '@utils/axios'; -export interface Channel { +export interface ChannelSummary { id: string; managerId: User['_id']; name: string; @@ -13,7 +13,7 @@ export interface Channel { isPrivate: boolean; } -export type GetChannelsResult = Channel[]; +export type GetChannelsResult = ChannelSummary[]; export type GetChannelsResponse = SuccessResponse; export type GetChannels = (communityId: string) => Promise; @@ -24,3 +24,24 @@ export const getChannels: GetChannels = (communityId: string) => { .get(endPoint) .then((response) => response.data.result); }; + +export interface Channel extends ChannelSummary { + communityId: string; + type: 'Channel'; + users: Array; + chatLists: []; + createdAt: string; + updatedAt: string; +} + +export type GetChannelResult = Channel; +export type GetChannelResponse = SuccessResponse; +export type GetChannel = (channelId: string) => Promise; + +export const getChannel: GetChannel = (channelId: string) => { + const endPoint = `/api/channels/${channelId}`; + + return tokenAxios + .get(endPoint) + .then((response) => response.data.result); +}; diff --git a/client/src/hooks/channel.ts b/client/src/hooks/channel.ts index 55039f5d..bab287b0 100644 --- a/client/src/hooks/channel.ts +++ b/client/src/hooks/channel.ts @@ -1,7 +1,7 @@ -import type { GetChannelsResult } from '@apis/channel'; +import type { GetChannelResult, GetChannelsResult } from '@apis/channel'; import type { AxiosError } from 'axios'; -import { getChannels } from '@apis/channel'; +import { getChannel, getChannels } from '@apis/channel'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; @@ -21,3 +21,18 @@ export const useChannelsQuery = (communityId: string) => { return { channelsQuery: query, invalidateChannelsQuery: invalidate }; }; + +export const useChannelQuery = (channelId: string) => { + const queryClient = useQueryClient(); + const key = queryKeyCreator.channel.detail(channelId); + + const query = useQuery(key, () => + getChannel(channelId), + ); + const invalidate = useCallback( + () => queryClient.invalidateQueries(key), + [queryClient, key], + ); + + return { channelQuery: query, invalidateChannelQuery: invalidate }; +}; diff --git a/client/src/queryKeyCreator.ts b/client/src/queryKeyCreator.ts index 3fe360f4..1a8f17cb 100644 --- a/client/src/queryKeyCreator.ts +++ b/client/src/queryKeyCreator.ts @@ -20,7 +20,9 @@ const communityQueryKey = { const channelQueryKey = { all: () => ['channels'] as const, list: (communityId: string) => - [...channelQueryKey.all(), communityId] as const, + [...channelQueryKey.all(), 'list', communityId] as const, + detail: (channelId: string) => + [...channelQueryKey.all(), 'detail', channelId] as const, }; const queryKeyCreator = { From 2179294b05bc60863c52dde0a912943ad2779ff1 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 01:15:20 +0900 Subject: [PATCH 040/191] =?UTF-8?q?feat:=20`ChannelItem`=EC=9D=80=20?= =?UTF-8?q?=EC=BB=A8=ED=85=90=EC=B8=A0=EB=A5=BC=20=EA=B0=90=EC=8B=B8?= =?UTF-8?q?=EB=8A=94=20=EB=86=92=EC=9D=B4=EB=A5=BC=20=EA=B0=80=EC=A7=80?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/ChannelItem/index.tsx | 10 ++++++++-- client/src/layouts/CommunityNav/index.tsx | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/client/src/components/ChannelItem/index.tsx b/client/src/components/ChannelItem/index.tsx index c439b31f..242fecf2 100644 --- a/client/src/components/ChannelItem/index.tsx +++ b/client/src/components/ChannelItem/index.tsx @@ -4,10 +4,16 @@ import React from 'react'; interface Props { isPrivate?: boolean; name: string; + className?: string; } -const ChannelItem: React.FC = ({ isPrivate = true, name }) => { + +const ChannelItem: React.FC = ({ + isPrivate = true, + name, + className, +}) => { return ( -
    +
    {isPrivate ? ( <> diff --git a/client/src/layouts/CommunityNav/index.tsx b/client/src/layouts/CommunityNav/index.tsx index fe389584..91ca43ec 100644 --- a/client/src/layouts/CommunityNav/index.tsx +++ b/client/src/layouts/CommunityNav/index.tsx @@ -58,7 +58,7 @@ const CommunityNav = () => { {channelsQuery.data?.map((channel) => (
  • Date: Mon, 28 Nov 2022 01:15:36 +0900 Subject: [PATCH 041/191] =?UTF-8?q?feat:=20`ChannelMetadata`=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=9E=91=EC=84=B1=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ChannelMetadata/index.tsx | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 client/src/components/ChannelMetadata/index.tsx diff --git a/client/src/components/ChannelMetadata/index.tsx b/client/src/components/ChannelMetadata/index.tsx new file mode 100644 index 00000000..78befa7c --- /dev/null +++ b/client/src/components/ChannelMetadata/index.tsx @@ -0,0 +1,58 @@ +import type { FC } from 'react'; + +import Avatar from '@components/Avatar'; +import ChannelItem from '@components/ChannelItem'; +import React from 'react'; + +const formatDate = (str: string) => { + const d = new Date(str); + + return { year: d.getFullYear(), month: d.getMonth() + 1, date: d.getDate() }; +}; + +interface Props { + profileUrl: string; + channelName: string; + isPrivate: boolean; + createdAt: string; + managerName: string; +} + +const ChannelMetadata: FC = ({ + profileUrl, + managerName, + channelName, + isPrivate, + createdAt, +}) => { + const { year, month, date } = formatDate(createdAt); + + return ( +
    +
    + +
    +
    +
    + + 의 시작이에요. +
    +
    + @{managerName}님이 이 채널을 {year} + 년 {month}월 {date}일에 생성했습니다. +
    +
    +
    + ); +}; + +export default ChannelMetadata; From 14d0b913a49d443ef2183fda73ce5f8527ed2673 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 01:16:00 +0900 Subject: [PATCH 042/191] =?UTF-8?q?feat:=20`Channel`=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EC=97=90=20`ChannelMetadata`=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/Channel/index.tsx | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/client/src/pages/Channel/index.tsx b/client/src/pages/Channel/index.tsx index e9e72a9d..e35163fe 100644 --- a/client/src/pages/Channel/index.tsx +++ b/client/src/pages/Channel/index.tsx @@ -1,7 +1,35 @@ +import Avatar from '@components/Avatar'; +import ChannelMetadata from '@components/ChannelMetadata'; +import { useChannelQuery } from '@hooks/channel'; +import { useCommunityQuery } from '@hooks/community'; +import { useUserQuery } from '@hooks/user'; import React from 'react'; +import { useParams } from 'react-router-dom'; const Channel = () => { - return
    Channel
    ; + const { communityId, roomId } = useParams(); + const { communityQuery } = useCommunityQuery(communityId as string); + const { channelQuery } = useChannelQuery(roomId as string); + const { userQuery } = useUserQuery(channelQuery.data?.managerId as string, { + enabled: !!channelQuery.data?.managerId, + }); + + if (channelQuery.isLoading || communityQuery.isLoading) + return
    loading...
    ; + + return ( +
    + {channelQuery.data && userQuery.data && communityQuery.data && ( + + )} +
    + ); }; export default Channel; From 20d1ed6cb4c8af9d3ffe5f676cf356afcf0da754 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 01:20:50 +0900 Subject: [PATCH 043/191] =?UTF-8?q?fix:=20=EB=88=84=EB=9D=BD=EB=90=9C=20`u?= =?UTF-8?q?serQueryKey`=EB=A5=BC=20queryKeyCreator=EC=97=90=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/queryKeyCreator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/queryKeyCreator.ts b/client/src/queryKeyCreator.ts index 1a8f17cb..a87f8662 100644 --- a/client/src/queryKeyCreator.ts +++ b/client/src/queryKeyCreator.ts @@ -36,6 +36,7 @@ const queryKeyCreator = { directMessage: directMessageQueryKey, community: communityQueryKey, channel: channelQueryKey, + user: userQueryKey, } as const; export default queryKeyCreator; From 6748baebb91be233e95b31ac8fe9138475ba738c Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 01:26:01 +0900 Subject: [PATCH 044/191] =?UTF-8?q?fix:=20`ChannelMetadata`=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0=EA=B0=80=20?= =?UTF-8?q?=EC=95=84=EB=8B=8C=20=EC=B1=84=EB=84=90=EC=9D=98=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=82=AC=EC=A7=84=EC=9D=84=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20#15?= =?UTF-8?q?6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/Channel/index.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/client/src/pages/Channel/index.tsx b/client/src/pages/Channel/index.tsx index e35163fe..c3281f8d 100644 --- a/client/src/pages/Channel/index.tsx +++ b/client/src/pages/Channel/index.tsx @@ -1,27 +1,23 @@ -import Avatar from '@components/Avatar'; import ChannelMetadata from '@components/ChannelMetadata'; import { useChannelQuery } from '@hooks/channel'; -import { useCommunityQuery } from '@hooks/community'; import { useUserQuery } from '@hooks/user'; import React from 'react'; import { useParams } from 'react-router-dom'; const Channel = () => { - const { communityId, roomId } = useParams(); - const { communityQuery } = useCommunityQuery(communityId as string); + const { roomId } = useParams(); const { channelQuery } = useChannelQuery(roomId as string); const { userQuery } = useUserQuery(channelQuery.data?.managerId as string, { enabled: !!channelQuery.data?.managerId, }); - if (channelQuery.isLoading || communityQuery.isLoading) - return
    loading...
    ; + if (channelQuery.isLoading) return
    loading...
    ; return (
    - {channelQuery.data && userQuery.data && communityQuery.data && ( + {channelQuery.data && userQuery.data && ( Date: Mon, 28 Nov 2022 10:25:59 +0900 Subject: [PATCH 045/191] =?UTF-8?q?refactor:=20`Date.toLocaleDateString`?= =?UTF-8?q?=EC=9D=84=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=20=EB=AC=B8=EC=9E=90=EC=97=B4=EC=9D=84=20format?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20#156?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/ChannelMetadata/index.tsx | 13 +++---------- client/src/utils/date.ts | 6 ++++++ 2 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 client/src/utils/date.ts diff --git a/client/src/components/ChannelMetadata/index.tsx b/client/src/components/ChannelMetadata/index.tsx index 78befa7c..2928ba37 100644 --- a/client/src/components/ChannelMetadata/index.tsx +++ b/client/src/components/ChannelMetadata/index.tsx @@ -2,14 +2,9 @@ import type { FC } from 'react'; import Avatar from '@components/Avatar'; import ChannelItem from '@components/ChannelItem'; +import { formatDate } from '@utils/date'; import React from 'react'; -const formatDate = (str: string) => { - const d = new Date(str); - - return { year: d.getFullYear(), month: d.getMonth() + 1, date: d.getDate() }; -}; - interface Props { profileUrl: string; channelName: string; @@ -25,8 +20,6 @@ const ChannelMetadata: FC = ({ isPrivate, createdAt, }) => { - const { year, month, date } = formatDate(createdAt); - return (
    @@ -47,8 +40,8 @@ const ChannelMetadata: FC = ({ 의 시작이에요.
    - @{managerName}님이 이 채널을 {year} - 년 {month}월 {date}일에 생성했습니다. + @{managerName}님이 이 채널을{' '} + {formatDate(createdAt)}에 생성했습니다.
    diff --git a/client/src/utils/date.ts b/client/src/utils/date.ts new file mode 100644 index 00000000..fa2f6ca5 --- /dev/null +++ b/client/src/utils/date.ts @@ -0,0 +1,6 @@ +export const formatDate = (str: string) => + new Date(str).toLocaleDateString('ko', { + year: 'numeric', + month: 'short', + day: 'numeric', + }); From 927009aac19022f13ee877543fb98326dbc6e459 Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 21:44:11 +0900 Subject: [PATCH 046/191] =?UTF-8?q?feat:=20contextMenuModalSlice=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 모달을 열 때 커뮤니티 혹은 채널의 id, x좌표, y좌표, type, isOpen을 전달받음. --- client/src/stores/contextMenuModalSlice.ts | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 client/src/stores/contextMenuModalSlice.ts diff --git a/client/src/stores/contextMenuModalSlice.ts b/client/src/stores/contextMenuModalSlice.ts new file mode 100644 index 00000000..405402b0 --- /dev/null +++ b/client/src/stores/contextMenuModalSlice.ts @@ -0,0 +1,56 @@ +import type { StateCreator } from 'zustand'; + +import { immer } from 'zustand/middleware/immer'; + +export type ContextMenuType = 'community' | 'channel' | null; + +export interface ContextMenuModal { + id: string | null; + x: number; + y: number; + type: ContextMenuType; + isOpen: boolean; +} +export type OpenContextMenuModal = ({ + id, + x, + y, + type, +}: Omit) => void; + +export interface ContextMenuModalSlice { + contextMenuModal: ContextMenuModal; + openContextMenuModal: OpenContextMenuModal; + closeContextMenuModal: () => void; +} + +const initialContextMenuModalValue = { + id: null, + type: null, + isOpen: false, + x: 0, + y: 0, +}; + +export const contextMenuModalSlice: StateCreator< + ContextMenuModalSlice, + [], + [['zustand/immer', never], ...[]], + ContextMenuModalSlice +> = immer((set) => ({ + contextMenuModal: initialContextMenuModalValue, + openContextMenuModal: ({ id, x, y, type }) => + set((state) => { + state.contextMenuModal = { + id, + x, + y, + type, + isOpen: true, + }; + }), + closeContextMenuModal: () => + set((state) => { + state.contextMenuModal = initialContextMenuModalValue; + }), +})); From c3e61818fb94ee60dd3f592eff0f3ad17b451f47 Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 21:46:23 +0900 Subject: [PATCH 047/191] =?UTF-8?q?feat:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EC=BB=A8=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EB=A7=88=ED=81=AC=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/CommunityContextMenu/index.tsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 client/src/components/CommunityContextMenu/index.tsx diff --git a/client/src/components/CommunityContextMenu/index.tsx b/client/src/components/CommunityContextMenu/index.tsx new file mode 100644 index 00000000..099e861d --- /dev/null +++ b/client/src/components/CommunityContextMenu/index.tsx @@ -0,0 +1,48 @@ +import type { MouseEventHandler } from 'react'; + +import { + UserPlusIcon, + Cog6ToothIcon, + ArrowRightOnRectangleIcon, +} from '@heroicons/react/20/solid'; +import React, { useCallback } from 'react'; + +interface Props {} + +const CommunityContextMenu: React.FC = () => { + const handleRightClickContextMenu: MouseEventHandler = + useCallback((e) => { + e.preventDefault(); + }, []); + + return ( +
    +

    커뮤니티 컨텍스트 메뉴

    +
      +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    + ); +}; + +export default CommunityContextMenu; From a09884a76a9d4d03083014e39920e487904bd04b Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 21:47:03 +0900 Subject: [PATCH 048/191] =?UTF-8?q?feat:=20=EC=BB=A8=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A9=94=EB=89=B4=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 커뮤니티 컨텍스트 메뉴 혹은 채널 컨텍스트 메뉴를 렌더링 --- .../Modals/ContextMenuModal/index.tsx | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 client/src/components/Modals/ContextMenuModal/index.tsx diff --git a/client/src/components/Modals/ContextMenuModal/index.tsx b/client/src/components/Modals/ContextMenuModal/index.tsx new file mode 100644 index 00000000..6772c93b --- /dev/null +++ b/client/src/components/Modals/ContextMenuModal/index.tsx @@ -0,0 +1,53 @@ +import type { CSSProperties } from 'react'; + +import CommunityContextMenu from '@components/CommunityContextMenu'; +import { useRootStore } from '@stores/rootStore'; +import React from 'react'; +import ReactModal from 'react-modal'; + +const modalOverlayStyle: CSSProperties = { + background: 'transparent', +}; + +interface Props {} + +ReactModal.setAppElement('#root'); + +const ContextMenuModal: React.FC = () => { + const { x, y, isOpen } = useRootStore((state) => state.contextMenuModal); + const closeContextMenuModal = useRootStore( + (state) => state.closeContextMenuModal, + ); + + const modalContentStyle: CSSProperties = { + width: 'max-content', + height: 'max-content', + left: x, + top: y, + borderRadius: 10, + padding: 0, + }; + + return ( + { + if (!ref) return; + ref.addEventListener('mousedown', (e) => { + if (e.target === ref) { + closeContextMenuModal(); + } + }); + ref.addEventListener('contextmenu', (e) => { + e.preventDefault(); + }); + }} + > + + + ); +}; + +export default ContextMenuModal; From 1b9c77f36b6fb73ed74823790fc9c19c2e1774d3 Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 21:47:32 +0900 Subject: [PATCH 049/191] =?UTF-8?q?feat:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EB=A7=81=ED=81=AC=20=EC=BB=A8=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A9=94=EB=89=B4=20=ED=95=B8=EB=93=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/layouts/Gnb/index.tsx | 63 ++++++++++++++++++++++++-------- client/src/pages/Home/index.tsx | 2 + 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/client/src/layouts/Gnb/index.tsx b/client/src/layouts/Gnb/index.tsx index d7d9d1d0..37768aa1 100644 --- a/client/src/layouts/Gnb/index.tsx +++ b/client/src/layouts/Gnb/index.tsx @@ -1,23 +1,51 @@ +import type { MouseEventHandler } from 'react'; + import Avatar from '@components/Avatar'; import GnbItemContainer from '@components/GnbItemContainer'; import { LOGO_IMG_URL } from '@constants/url'; import { PlusIcon } from '@heroicons/react/24/solid'; import { useCommunitiesQuery } from '@hooks/community'; import { useRootStore } from '@stores/rootStore'; -import React, { memo } from 'react'; +import React, { memo, useCallback } from 'react'; import { Link, useLocation, useParams } from 'react-router-dom'; const Gnb = () => { const { pathname } = useLocation(); const params = useParams(); + const openContextMenuModal = useRootStore( + (state) => state.openContextMenuModal, + ); const openCreateCommunityModal = useRootStore( (state) => state.openCreateCommunityModal, ); const { communitiesQuery } = useCommunitiesQuery(); + const handleRightClickGnb: MouseEventHandler = useCallback( + (e) => { + e.preventDefault(); + }, + [], + ); + + const handleRightClickCommunityLink: ( + id: string, + ) => MouseEventHandler = (id: string) => (e) => { + e.preventDefault(); + + openContextMenuModal({ + x: e.clientX, + y: e.clientY, + type: 'community', + id, + }); + }; + return ( -
    +
    @@ -39,19 +67,21 @@ const Gnb = () => {
    로딩중
    ) : ( communitiesQuery.data?.map(({ _id, name, profileUrl }) => ( - - - - - +
  • + + + + + +
  • )) )} @@ -68,6 +98,9 @@ const Gnb = () => { + + {/*
    */} + {/*
    */} ); }; diff --git a/client/src/pages/Home/index.tsx b/client/src/pages/Home/index.tsx index aeb911e0..2a23f9e7 100644 --- a/client/src/pages/Home/index.tsx +++ b/client/src/pages/Home/index.tsx @@ -1,3 +1,4 @@ +import ContextMenuModal from '@components/Modals/ContextMenuModal'; import CreateCommunityModal from '@components/Modals/CreateCommunityModal'; import Gnb from '@layouts/Gnb'; import Sidebar from '@layouts/Sidebar'; @@ -11,6 +12,7 @@ const Home = () => { + ); }; From a9a813c0d4050c86b285b127ed3d5d6b94ba759a Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 21:48:11 +0900 Subject: [PATCH 050/191] =?UTF-8?q?feat:=20rootStore=EC=97=90=20=EC=BB=A8?= =?UTF-8?q?=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EB=A9=94=EB=89=B4=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20slice=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/stores/rootStore.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/stores/rootStore.ts b/client/src/stores/rootStore.ts index 9503dc7b..92e04ae2 100644 --- a/client/src/stores/rootStore.ts +++ b/client/src/stores/rootStore.ts @@ -1,14 +1,16 @@ +import type { ContextMenuModalSlice } from '@stores/contextMenuModalSlice'; import type { CreateCommunityModalSlice } from '@stores/createCommunityModalSlice'; +import { contextMenuModalSlice } from '@stores/contextMenuModalSlice'; +import { createCommunityModalSlice } from '@stores/createCommunityModalSlice'; import store from 'zustand'; import { devtools } from 'zustand/middleware'; -import { createCommunityModalSlice } from './createCommunityModalSlice'; - -export type Store = CreateCommunityModalSlice; +export type Store = CreateCommunityModalSlice & ContextMenuModalSlice; export const useRootStore = store()( devtools((...a) => ({ ...createCommunityModalSlice(...a), + ...contextMenuModalSlice(...a), })), ); From 663d74bc9475ca4db6c99365b855f8d59e19c7e3 Mon Sep 17 00:00:00 2001 From: Cola Date: Sun, 27 Nov 2022 22:34:25 +0900 Subject: [PATCH 051/191] =?UTF-8?q?feat:=20alertModalSlice=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/stores/alertModalSlice.ts | 51 ++++++++++++++++++++++++++++ client/src/stores/rootStore.ts | 7 +++- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 client/src/stores/alertModalSlice.ts diff --git a/client/src/stores/alertModalSlice.ts b/client/src/stores/alertModalSlice.ts new file mode 100644 index 00000000..1316b78f --- /dev/null +++ b/client/src/stores/alertModalSlice.ts @@ -0,0 +1,51 @@ +import type { StateCreator } from 'zustand'; + +import { immer } from 'zustand/middleware/immer'; + +export interface AlertModal { + description: string; + isOpen: boolean; + onCancel: (() => void) | null; + onSubmit: (() => void) | null; +} + +type OpenAlertModal = ({ + description, + onCancel, + onSubmit, +}: Omit) => void; + +export interface AlertModalSlice { + alertModal: AlertModal; + openAlertModal: OpenAlertModal; + closeAlertModal: () => void; +} + +const initialAlertModalValue = { + isOpen: false, + description: '', + onSubmit: null, + onCancel: null, +}; + +export const alertModalSlice: StateCreator< + AlertModalSlice, + [], + [['zustand/immer', never], ...[]], + AlertModalSlice +> = immer((set) => ({ + alertModal: initialAlertModalValue, + openAlertModal: ({ description, onCancel, onSubmit }) => + set((state) => { + state.alertModal = { + description, + onCancel, + onSubmit, + isOpen: true, + }; + }), + closeAlertModal: () => + set((state) => { + state.alertModal = initialAlertModalValue; + }), +})); diff --git a/client/src/stores/rootStore.ts b/client/src/stores/rootStore.ts index 92e04ae2..f0b0d665 100644 --- a/client/src/stores/rootStore.ts +++ b/client/src/stores/rootStore.ts @@ -1,16 +1,21 @@ +import type { AlertModalSlice } from '@stores/alertModalSlice'; import type { ContextMenuModalSlice } from '@stores/contextMenuModalSlice'; import type { CreateCommunityModalSlice } from '@stores/createCommunityModalSlice'; +import { alertModalSlice } from '@stores/alertModalSlice'; import { contextMenuModalSlice } from '@stores/contextMenuModalSlice'; import { createCommunityModalSlice } from '@stores/createCommunityModalSlice'; import store from 'zustand'; import { devtools } from 'zustand/middleware'; -export type Store = CreateCommunityModalSlice & ContextMenuModalSlice; +export type Store = CreateCommunityModalSlice & + ContextMenuModalSlice & + AlertModalSlice; export const useRootStore = store()( devtools((...a) => ({ ...createCommunityModalSlice(...a), ...contextMenuModalSlice(...a), + ...alertModalSlice(...a), })), ); From d7eb7d2e996ee7e245b22b37967b8e4c18df6177 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 01:13:17 +0900 Subject: [PATCH 052/191] =?UTF-8?q?refactor:=20contextMenuModal=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=83=80=EC=9E=85=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 컨텍스트 메뉴로 어떤 액션을 수행할 때 필요한 data를 전달받을 수 있도록 data타입을 추가함. 현재 계획한 컨텍스트 메뉴는 Gnb의 커뮤니티 버튼과 채널 사이드바의 채널 버튼밖에 없기 때문에 data타입은 커뮤니티 혹은 채널 데이터가 되도록 union으로 타입 분리함. --- .../Modals/ContextMenuModal/index.tsx | 15 ++++++- client/src/stores/contextMenuModalSlice.ts | 40 ++++++++++++++----- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/client/src/components/Modals/ContextMenuModal/index.tsx b/client/src/components/Modals/ContextMenuModal/index.tsx index 6772c93b..34be68ad 100644 --- a/client/src/components/Modals/ContextMenuModal/index.tsx +++ b/client/src/components/Modals/ContextMenuModal/index.tsx @@ -14,7 +14,10 @@ interface Props {} ReactModal.setAppElement('#root'); const ContextMenuModal: React.FC = () => { - const { x, y, isOpen } = useRootStore((state) => state.contextMenuModal); + const { x, y, isOpen, data, type } = useRootStore( + (state) => state.contextMenuModal, + ); + const closeContextMenuModal = useRootStore( (state) => state.closeContextMenuModal, ); @@ -28,6 +31,14 @@ const ContextMenuModal: React.FC = () => { padding: 0, }; + let ContextMenu; + + if (type === 'community') + ContextMenu = ; + else if (type === 'channel') + // TODO: ChannelContextMenu로 바꿔야함. + ContextMenu = ; + return ( = () => { }); }} > - + {ContextMenu} ); }; diff --git a/client/src/stores/contextMenuModalSlice.ts b/client/src/stores/contextMenuModalSlice.ts index 405402b0..1c2d1ee5 100644 --- a/client/src/stores/contextMenuModalSlice.ts +++ b/client/src/stores/contextMenuModalSlice.ts @@ -1,22 +1,40 @@ +import type { CommunitySummary } from '@apis/community'; import type { StateCreator } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -export type ContextMenuType = 'community' | 'channel' | null; - -export interface ContextMenuModal { - id: string | null; +export interface ContextMenuModalBase { x: number; y: number; - type: ContextMenuType; isOpen: boolean; } + +export interface CommunityContextMenuModal extends ContextMenuModalBase { + data: CommunitySummary; + type: 'community'; +} + +export interface ChannelContextMenuModal extends ContextMenuModalBase { + data: CommunitySummary; // TODO: 채널 모달 타입으로 바꿔야 함. + type: 'channel'; +} + +export interface InitialContextMenuModal extends ContextMenuModalBase { + data: null; + type: null; +} + +export type ContextMenuModal = + | InitialContextMenuModal + | ChannelContextMenuModal + | CommunityContextMenuModal; + export type OpenContextMenuModal = ({ - id, + data, x, y, type, -}: Omit) => void; +}: Omit) => void; export interface ContextMenuModalSlice { contextMenuModal: ContextMenuModal; @@ -25,12 +43,12 @@ export interface ContextMenuModalSlice { } const initialContextMenuModalValue = { - id: null, + data: null, type: null, isOpen: false, x: 0, y: 0, -}; +} as const; export const contextMenuModalSlice: StateCreator< ContextMenuModalSlice, @@ -39,10 +57,10 @@ export const contextMenuModalSlice: StateCreator< ContextMenuModalSlice > = immer((set) => ({ contextMenuModal: initialContextMenuModalValue, - openContextMenuModal: ({ id, x, y, type }) => + openContextMenuModal: ({ data, x, y, type }) => set((state) => { state.contextMenuModal = { - id, + data, x, y, type, From 77b615c38789fa32de844066a8a879a91afc5f8c Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 01:16:01 +0900 Subject: [PATCH 053/191] =?UTF-8?q?refactor:=20contextMenuModal=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=83=80=EC=9E=85=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 컨텍스트 메뉴로 어떤 액션을 수행할 때 필요한 data를 전달받을 수 있도록 data타입을 추가함. 현재 계획한 컨텍스트 메뉴는 Gnb의 커뮤니티 버튼과 채널 사이드바의 채널 버튼밖에 없기 때문에 data타입은 커뮤니티 혹은 채널 데이터가 되도록 union으로 타입 분리함. --- client/src/layouts/Gnb/index.tsx | 46 ++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/client/src/layouts/Gnb/index.tsx b/client/src/layouts/Gnb/index.tsx index 37768aa1..e11b26d9 100644 --- a/client/src/layouts/Gnb/index.tsx +++ b/client/src/layouts/Gnb/index.tsx @@ -1,3 +1,4 @@ +import type { CommunitySummary } from '@apis/community'; import type { MouseEventHandler } from 'react'; import Avatar from '@components/Avatar'; @@ -29,15 +30,15 @@ const Gnb = () => { ); const handleRightClickCommunityLink: ( - id: string, - ) => MouseEventHandler = (id: string) => (e) => { + community: CommunitySummary, + ) => MouseEventHandler = (community) => (e) => { e.preventDefault(); openContextMenuModal({ x: e.clientX, y: e.clientY, type: 'community', - id, + data: community, }); }; @@ -66,23 +67,27 @@ const Gnb = () => { {communitiesQuery.isLoading ? (
    로딩중
    ) : ( - communitiesQuery.data?.map(({ _id, name, profileUrl }) => ( -
  • - - - - - -
  • - )) + communitiesQuery.data?.map((community) => { + const { _id, name, profileUrl } = community; + + return ( +
  • + + + + + +
  • + ); + }) )} @@ -99,6 +104,7 @@ const Gnb = () => { + {/* TODO: 툴팁 만들기 */} {/*
    */} {/*
    */} From 7e6db70bec2620acdbbb692a916287345421d5c0 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 01:17:11 +0900 Subject: [PATCH 054/191] =?UTF-8?q?feat:=20=EC=BB=A8=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A9=94=EB=89=B4=EC=9D=98=20=EA=B5=AC=EB=B6=84?= =?UTF-8?q?=EC=84=A0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/CommunityContextMenu/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/components/CommunityContextMenu/index.tsx b/client/src/components/CommunityContextMenu/index.tsx index 099e861d..2131a7b7 100644 --- a/client/src/components/CommunityContextMenu/index.tsx +++ b/client/src/components/CommunityContextMenu/index.tsx @@ -35,6 +35,9 @@ const CommunityContextMenu: React.FC = () => { +
    +
    +
    + +
    + + ); +}; + +export default AlertBox; From b5e445cd60d64b61713b9a1e679964558d851b39 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 02:03:10 +0900 Subject: [PATCH 062/191] =?UTF-8?q?feat:=20AlertModal=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Modals/AlertModal/index.tsx | 50 +++++++++++++++++++ client/src/pages/Home/index.tsx | 2 + 2 files changed, 52 insertions(+) create mode 100644 client/src/components/Modals/AlertModal/index.tsx diff --git a/client/src/components/Modals/AlertModal/index.tsx b/client/src/components/Modals/AlertModal/index.tsx new file mode 100644 index 00000000..09caede1 --- /dev/null +++ b/client/src/components/Modals/AlertModal/index.tsx @@ -0,0 +1,50 @@ +import type { CSSProperties } from 'react'; + +import AlertBox from '@components/AlertBox'; +import { useRootStore } from '@stores/rootStore'; +import React from 'react'; +import ReactModal from 'react-modal'; + +const modalOverlayStyle: CSSProperties = { + background: 'rgba(0, 0, 0, 0.5)', +}; + +const modalContentStyle: CSSProperties = { + width: 'max-content', + height: 'max-content', + borderRadius: 10, + padding: 0, + inset: '50% 50%', + transform: 'translate3d(-50%, -50%, 0)', +}; + +const AlertModal = () => { + const { isOpen, description, onCancel, onSubmit, disabled } = useRootStore( + (state) => state.alertModal, + ); + + const handleCloseAlertModal = () => { + if (!disabled && onCancel) { + onCancel(); + } + }; + + return ( + + {onSubmit && onCancel && ( + + )} + + ); +}; + +export default AlertModal; diff --git a/client/src/pages/Home/index.tsx b/client/src/pages/Home/index.tsx index 2a23f9e7..8917245d 100644 --- a/client/src/pages/Home/index.tsx +++ b/client/src/pages/Home/index.tsx @@ -1,3 +1,4 @@ +import AlertModal from '@components/Modals/AlertModal'; import ContextMenuModal from '@components/Modals/ContextMenuModal'; import CreateCommunityModal from '@components/Modals/CreateCommunityModal'; import Gnb from '@layouts/Gnb'; @@ -13,6 +14,7 @@ const Home = () => { + ); }; From fbfee0e5fd74c871abd5f558e015b60ff1777c63 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 03:10:10 +0900 Subject: [PATCH 063/191] =?UTF-8?q?feat:=20useSetCommunitiesQuery=20?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=20=ED=9B=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/community.ts | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/client/src/hooks/community.ts b/client/src/hooks/community.ts index 468ff0cb..8a102f2e 100644 --- a/client/src/hooks/community.ts +++ b/client/src/hooks/community.ts @@ -32,6 +32,42 @@ export const useCommunitiesQuery = () => { return { communitiesQuery: query, invalidateCommunitiesQuery: invalidate }; }; +interface SetCommunities { + ( + callback: ( + communities?: GetCommunitiesResult, + ) => GetCommunitiesResult | undefined, + ): void; + (communities: GetCommunitiesResult): void; +} + +/** + * ### 커뮤니티 쿼리 응답 데이터의 Setter를 반환하는 Custom Hook + * - useState hook의 setState처럼 사용하면 됩니다. + * ```tsx + * 사용 예시 + * setComms( + * (prevComms) => prevComms.filter( + * (prevComm) => prevComm.id !== id + * ) + * ) + * ``` + */ +export const useSetCommunitiesQuery = () => { + const queryClient = useQueryClient(); + + const key = queryKeyCreator.community.all(); + + const setCommunities: SetCommunities = (cb) => { + queryClient.setQueryData(key, (communities) => { + if (typeof cb === 'function') return cb(communities); + return cb; + }); + }; + + return setCommunities; +}; + export const useCommunityQuery = (communityId: string) => { const queryClient = useQueryClient(); From 9b95493727b77a00f12cca965ee7c256b3ca8172 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 03:10:49 +0900 Subject: [PATCH 064/191] =?UTF-8?q?feat:=20CommunityContextMenu=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0=20=ED=87=B4?= =?UTF-8?q?=EC=9E=A5=20=ED=95=B8=EB=93=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/CommunityContextMenu/index.tsx | 66 ++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/client/src/components/CommunityContextMenu/index.tsx b/client/src/components/CommunityContextMenu/index.tsx index 2131a7b7..e4a4e936 100644 --- a/client/src/components/CommunityContextMenu/index.tsx +++ b/client/src/components/CommunityContextMenu/index.tsx @@ -1,20 +1,77 @@ +import type { CommunitySummary } from '@apis/community'; import type { MouseEventHandler } from 'react'; +import defaultErrorHandler from '@errors/defaultErrorHandler'; import { UserPlusIcon, Cog6ToothIcon, ArrowRightOnRectangleIcon, } from '@heroicons/react/20/solid'; +import { + useLeaveCommunityMutation, + useSetCommunitiesQuery, +} from '@hooks/community'; +import { useRootStore } from '@stores/rootStore'; import React, { useCallback } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; -interface Props {} +interface Props { + community: CommunitySummary; +} -const CommunityContextMenu: React.FC = () => { +const CommunityContextMenu: React.FC = ({ community }) => { + const params = useParams(); + const navigate = useNavigate(); const handleRightClickContextMenu: MouseEventHandler = useCallback((e) => { e.preventDefault(); }, []); + const leaveCommunityMutation = useLeaveCommunityMutation(); + const setCommunities = useSetCommunitiesQuery(); + + const openAlertModal = useRootStore((state) => state.openAlertModal); + const closeAlertModal = useRootStore((state) => state.closeAlertModal); + const disableAlertModal = useRootStore((state) => state.disableAlertModal); + const enableAlertModal = useRootStore((state) => state.enableAlertModal); + + const closeContextMenuModal = useRootStore( + (state) => state.closeContextMenuModal, + ); + + const handleSubmitAlert = () => { + disableAlertModal(); + leaveCommunityMutation + .mutateAsync(community._id) + .then(() => { + setCommunities((prevCommunities) => + prevCommunities?.filter( + (prevCommunity) => prevCommunity._id !== community._id, + ), + ); + + if (params?.communityId === community._id) { + navigate('/dms'); + } + closeAlertModal(); + }) + .catch((error) => { + defaultErrorHandler(error); + }) + .finally(() => { + enableAlertModal(); + }); + }; + + const handleClickCommunityLeaveButton = () => { + closeContextMenuModal(); + openAlertModal({ + description: `정말로 ${community.name} 커뮤니티에서 나가시겠습니까?`, + onCancel: closeAlertModal, + onSubmit: handleSubmitAlert, + }); + }; + return (
    = () => {
    - From 129735c4d4b71107c0512efb23659b15ed9b94d5 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 03:11:06 +0900 Subject: [PATCH 065/191] =?UTF-8?q?test:=20Mock=20API=20=ED=87=B4=EC=9E=A5?= =?UTF-8?q?=20=EC=84=B1=EA=B3=B5=20=EB=A1=9C=EA=B9=85=20=EB=94=9C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/handlers/Community.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/client/src/mocks/handlers/Community.js b/client/src/mocks/handlers/Community.js index 8532b7ec..aa7d5b03 100644 --- a/client/src/mocks/handlers/Community.js +++ b/client/src/mocks/handlers/Community.js @@ -93,18 +93,24 @@ export const LeaveCommunity = rest.delete( (req, res, ctx) => { const { id } = req.params; - colorLog(`커뮤니티 ID ${id}에서 퇴장하였습니다.`); - const ERROR = false; + const successDelay = 500; const successResponse = res( - ...createSuccessContext(ctx, 201, 500, { + ...createSuccessContext(ctx, 201, successDelay, { message: '커뮤니티 퇴장 성공~', }), ); const errorResponse = res(...createErrorContext(ctx)); + if (!ERROR) { + setTimeout( + () => colorLog(`커뮤니티 ID ${id}에서 퇴장하였습니다.`), + successDelay, + ); + } + return ERROR ? errorResponse : successResponse; }, ); From 4fe7e690fba7d13dc85117e87fae6097e286f8d0 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 10:41:43 +0900 Subject: [PATCH 066/191] =?UTF-8?q?test:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=ED=87=B4=EC=9E=A5=EC=8B=9C=20mock=20data=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/handlers/Community.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/src/mocks/handlers/Community.js b/client/src/mocks/handlers/Community.js index aa7d5b03..4c660522 100644 --- a/client/src/mocks/handlers/Community.js +++ b/client/src/mocks/handlers/Community.js @@ -105,10 +105,12 @@ export const LeaveCommunity = rest.delete( const errorResponse = res(...createErrorContext(ctx)); if (!ERROR) { - setTimeout( - () => colorLog(`커뮤니티 ID ${id}에서 퇴장하였습니다.`), - successDelay, - ); + setTimeout(() => { + colorLog(`커뮤니티 ID ${id}에서 퇴장하였습니다.`); + const targetIdx = communities.findIndex(({ _id }) => _id === id); + + communities.splice(targetIdx, 1); + }, successDelay); } return ERROR ? errorResponse : successResponse; From 1b4690fef1e3cc53d2fee2b702b0e345dfbfde63 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 15:39:02 +0900 Subject: [PATCH 067/191] =?UTF-8?q?refactor:=20`ChannelMetadata`=EC=97=90?= =?UTF-8?q?=EC=84=9C=20`RoomMetadata`=20=EA=B3=B5=ED=86=B5=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC=20#167?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ChannelMetadata/index.tsx | 16 +++------ client/src/components/RoomMetadata/index.tsx | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 client/src/components/RoomMetadata/index.tsx diff --git a/client/src/components/ChannelMetadata/index.tsx b/client/src/components/ChannelMetadata/index.tsx index 2928ba37..dd8c0f3d 100644 --- a/client/src/components/ChannelMetadata/index.tsx +++ b/client/src/components/ChannelMetadata/index.tsx @@ -1,12 +1,12 @@ import type { FC } from 'react'; -import Avatar from '@components/Avatar'; import ChannelItem from '@components/ChannelItem'; +import RoomMetadata from '@components/RoomMetadata'; import { formatDate } from '@utils/date'; import React from 'react'; interface Props { - profileUrl: string; + profileUrl?: string; channelName: string; isPrivate: boolean; createdAt: string; @@ -21,15 +21,7 @@ const ChannelMetadata: FC = ({ createdAt, }) => { return ( -
    -
    - -
    +
    = ({ {formatDate(createdAt)}에 생성했습니다.
    -
    + ); }; diff --git a/client/src/components/RoomMetadata/index.tsx b/client/src/components/RoomMetadata/index.tsx new file mode 100644 index 00000000..084b09bd --- /dev/null +++ b/client/src/components/RoomMetadata/index.tsx @@ -0,0 +1,33 @@ +import type { FC, ReactNode } from 'react'; + +import Avatar from '@components/Avatar'; +import { LOGO_IMG_URL } from '@constants/url'; +import React from 'react'; + +interface Props { + profileUrl?: string; + channelName: string; + children?: ReactNode; +} + +const RoomMetadata: FC = ({ + profileUrl = LOGO_IMG_URL, + children, + channelName, +}) => { + return ( +
    +
    + +
    + {children} +
    + ); +}; + +export default RoomMetadata; From c87a79fd2fd70d8063530243aefe5baa1e70e326 Mon Sep 17 00:00:00 2001 From: leegwae Date: Mon, 28 Nov 2022 15:39:28 +0900 Subject: [PATCH 068/191] =?UTF-8?q?feat:=20`DMMetadata`=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=9E=91=EC=84=B1=20#167?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/DMMetadata/index.tsx | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 client/src/components/DMMetadata/index.tsx diff --git a/client/src/components/DMMetadata/index.tsx b/client/src/components/DMMetadata/index.tsx new file mode 100644 index 00000000..89347255 --- /dev/null +++ b/client/src/components/DMMetadata/index.tsx @@ -0,0 +1,39 @@ +import type { FC } from 'react'; + +import ChannelItem from '@components/ChannelItem'; +import RoomMetadata from '@components/RoomMetadata'; +import { formatDate } from '@utils/date'; +import React from 'react'; + +interface Props { + friendProfileUrl?: string; + channelName: string; + isPrivate: boolean; + createdAt: string; + friendName: string; + creatorName: string; +} + +const DMMetadata: FC = ({ + friendProfileUrl, + friendName, + creatorName, + createdAt, +}) => { + return ( + +
    +
    + @{friendName}님과 나눈 다이렉트 + 메시지의 첫 부분이에요. +
    +
    + @{creatorName}님이 이 채널을{' '} + {formatDate(createdAt)}에 생성했습니다. +
    +
    +
    + ); +}; + +export default DMMetadata; From 66c60480ca141c240983851265249b42198a7725 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 14:40:51 +0900 Subject: [PATCH 069/191] =?UTF-8?q?refactor:=20JoinedChannel=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/channel.ts | 11 +++++++++++ client/src/apis/community.ts | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/client/src/apis/channel.ts b/client/src/apis/channel.ts index 06cdf4bd..6b34f03f 100644 --- a/client/src/apis/channel.ts +++ b/client/src/apis/channel.ts @@ -3,6 +3,17 @@ import type { User } from '@apis/user'; import { tokenAxios } from '@utils/axios'; +export interface JoinedChannel { + _id: string; + managerId: User['_id']; + name: string; + type: string; // TODO: DM or Channel -> DM 구현할 때 타입 구체화 + isPrivate: boolean; + profileUrl: string; + description: string; + lastRead: boolean; +} + export interface ChannelSummary { id: string; managerId: User['_id']; diff --git a/client/src/apis/community.ts b/client/src/apis/community.ts index a77ae3d4..048b2e19 100644 --- a/client/src/apis/community.ts +++ b/client/src/apis/community.ts @@ -1,4 +1,5 @@ import type { SuccessResponse } from '@@types/apis/response'; +import type { JoinedChannel } from '@apis/channel'; import type { User } from '@apis/user'; import { tokenAxios } from '@utils/axios'; @@ -9,6 +10,7 @@ export interface CommunitySummary { profileUrl: string; description: string; managerId: string; + channels: JoinedChannel[]; } export type GetCommunitiesResult = CommunitySummary[]; @@ -45,7 +47,7 @@ export interface CreateCommunityResult extends CommunitySummary { createdAt: string; updatedAt: string; channels: []; - users: Array; + users: Array; } export type CreateCommunity = ( From d7b3810f82c3e5bf4ec677b3962e78e1445b4f25 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 14:41:14 +0900 Subject: [PATCH 070/191] =?UTF-8?q?refactor:=20defaultHandler=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Modals/AlertModal/index.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/src/components/Modals/AlertModal/index.tsx b/client/src/components/Modals/AlertModal/index.tsx index 09caede1..467e0681 100644 --- a/client/src/components/Modals/AlertModal/index.tsx +++ b/client/src/components/Modals/AlertModal/index.tsx @@ -18,6 +18,8 @@ const modalContentStyle: CSSProperties = { transform: 'translate3d(-50%, -50%, 0)', }; +const defaultHandler = () => {}; + const AlertModal = () => { const { isOpen, description, onCancel, onSubmit, disabled } = useRootStore( (state) => state.alertModal, @@ -35,14 +37,12 @@ const AlertModal = () => { style={{ content: modalContentStyle, overlay: modalOverlayStyle }} onRequestClose={handleCloseAlertModal} > - {onSubmit && onCancel && ( - - )} + ); }; From 8468146a23252269a4513d8535fcb7e9eca6010e Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 14:55:24 +0900 Subject: [PATCH 071/191] =?UTF-8?q?refactor:=20JoinedChannel=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=8D=BC=ED=8B=B0=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ChannelSummary와 겹치지 않는 프로퍼티는 아래 2개이다. --- client/src/apis/channel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/apis/channel.ts b/client/src/apis/channel.ts index 6b34f03f..f189eb33 100644 --- a/client/src/apis/channel.ts +++ b/client/src/apis/channel.ts @@ -7,11 +7,11 @@ export interface JoinedChannel { _id: string; managerId: User['_id']; name: string; - type: string; // TODO: DM or Channel -> DM 구현할 때 타입 구체화 isPrivate: boolean; profileUrl: string; description: string; lastRead: boolean; + type: string; // TODO: DM or Channel -> DM 구현할 때 타입 구체화 } export interface ChannelSummary { From 36a317e6e88b47c9cc0f3df7163a4c85e743fe8a Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 14:55:40 +0900 Subject: [PATCH 072/191] =?UTF-8?q?test:=20GetCommunities=20Mock=20Data=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/data/communities.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/src/mocks/data/communities.js b/client/src/mocks/data/communities.js index fb00e4f5..8b68b9ce 100644 --- a/client/src/mocks/data/communities.js +++ b/client/src/mocks/data/communities.js @@ -2,12 +2,26 @@ import { faker } from '@faker-js/faker'; import { chancify } from '../utils/rand'; +export const createMockChannel = () => ({ + _id: faker.datatype.uuid(), + managerId: faker.datatype.uuid(), + name: faker.name.jobType(), + isPrivate: chancify(() => true, 50, false), + profileUrl: chancify(() => faker.image.avatar(), 50), + description: faker.lorem.sentence(1), + lastRead: chancify(() => true, 50, false), + type: 'Channel', +}); + +const channels = [...Array(10)].map(createMockChannel); + export const createMockCommunities = () => ({ _id: faker.datatype.uuid(), name: faker.name.jobType(), managerId: faker.datatype.uuid(), profileUrl: chancify(() => faker.image.avatar(), 50), description: faker.lorem.sentence(1), + channels, }); export const communities = [...Array(15)].map(createMockCommunities); From 81a6ea09e1169c3255c5e374fb8945f02ed9d147 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 15:47:06 +0900 Subject: [PATCH 073/191] =?UTF-8?q?refactor:=20api=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20hooks=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/channel.ts | 31 +++++-------------------------- client/src/hooks/channel.ts | 19 ++----------------- 2 files changed, 7 insertions(+), 43 deletions(-) diff --git a/client/src/apis/channel.ts b/client/src/apis/channel.ts index f189eb33..ff5e9e30 100644 --- a/client/src/apis/channel.ts +++ b/client/src/apis/channel.ts @@ -3,9 +3,11 @@ import type { User } from '@apis/user'; import { tokenAxios } from '@utils/axios'; +type UserUID = User['_id']; + export interface JoinedChannel { _id: string; - managerId: User['_id']; + managerId: UserUID; name: string; isPrivate: boolean; profileUrl: string; @@ -14,32 +16,9 @@ export interface JoinedChannel { type: string; // TODO: DM or Channel -> DM 구현할 때 타입 구체화 } -export interface ChannelSummary { - id: string; - managerId: User['_id']; - name: string; - users: Array; - profileUrl: string; - description: string; - isPrivate: boolean; -} - -export type GetChannelsResult = ChannelSummary[]; -export type GetChannelsResponse = SuccessResponse; -export type GetChannels = (communityId: string) => Promise; - -export const getChannels: GetChannels = (communityId: string) => { - const endPoint = `/api/user/community/${communityId}/channels`; - - return tokenAxios - .get(endPoint) - .then((response) => response.data.result); -}; - -export interface Channel extends ChannelSummary { +export interface Channel extends JoinedChannel { communityId: string; - type: 'Channel'; - users: Array; + users: Array; chatLists: []; createdAt: string; updatedAt: string; diff --git a/client/src/hooks/channel.ts b/client/src/hooks/channel.ts index bab287b0..d6051350 100644 --- a/client/src/hooks/channel.ts +++ b/client/src/hooks/channel.ts @@ -1,27 +1,12 @@ -import type { GetChannelResult, GetChannelsResult } from '@apis/channel'; +import type { GetChannelResult } from '@apis/channel'; import type { AxiosError } from 'axios'; -import { getChannel, getChannels } from '@apis/channel'; +import { getChannel } from '@apis/channel'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; import queryKeyCreator from '@/queryKeyCreator'; -export const useChannelsQuery = (communityId: string) => { - const queryClient = useQueryClient(); - const key = queryKeyCreator.channel.list(communityId); - - const query = useQuery(key, () => - getChannels(communityId), - ); - const invalidate = useCallback( - () => queryClient.invalidateQueries(key), - [queryClient, key], - ); - - return { channelsQuery: query, invalidateChannelsQuery: invalidate }; -}; - export const useChannelQuery = (channelId: string) => { const queryClient = useQueryClient(); const key = queryKeyCreator.channel.detail(channelId); From 03af06a9bca0268acc9b0aa9f78acecd96bc0bda Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 15:47:25 +0900 Subject: [PATCH 074/191] =?UTF-8?q?feat:=20useJoinedChannelsQuery=20?= =?UTF-8?q?=ED=9B=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/community.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/client/src/hooks/community.ts b/client/src/hooks/community.ts index 8a102f2e..87c7b5d5 100644 --- a/client/src/hooks/community.ts +++ b/client/src/hooks/community.ts @@ -1,3 +1,4 @@ +import type { JoinedChannel } from '@apis/channel'; import type { CreateCommunityResult, CreateCommunityRequest, @@ -32,6 +33,25 @@ export const useCommunitiesQuery = () => { return { communitiesQuery: query, invalidateCommunitiesQuery: invalidate }; }; +/** + * @param id 접속한 커뮤니티의 id + * 접속한 커뮤니티에서 참여하고 있는 채널 목록 + */ +export const useJoinedChannelsQuery = (id: string) => { + const key = queryKeyCreator.community.all(); + const query = useQuery( + key, + getCommunities, + { + select: (data) => { + return data.find((community) => community._id === id)?.channels || []; + }, + }, + ); + + return { joinedChannelsQuery: query }; +}; + interface SetCommunities { ( callback: ( From 6245fe2023ac3e207372d15df31224a5fc56c5e5 Mon Sep 17 00:00:00 2001 From: Cola Date: Mon, 28 Nov 2022 15:48:24 +0900 Subject: [PATCH 075/191] =?UTF-8?q?design:=20=ED=97=A4=EB=8D=94=EC=97=90?= =?UTF-8?q?=20letter-spacing=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/layouts/DmNav/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/src/layouts/DmNav/index.tsx b/client/src/layouts/DmNav/index.tsx index 281e2d2b..de9c18ea 100644 --- a/client/src/layouts/DmNav/index.tsx +++ b/client/src/layouts/DmNav/index.tsx @@ -8,8 +8,10 @@ const DmNav = () => { return (
    From f1f0bb4b476f1fdd2fc02cab632901815edd68d4 Mon Sep 17 00:00:00 2001 From: leegwae Date: Thu, 1 Dec 2022 23:09:57 +0900 Subject: [PATCH 186/191] =?UTF-8?q?feat:=20=ED=8C=94=EB=A1=9C=EC=9A=B0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20#227?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/FollowerUserItem/index.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/src/components/FollowerUserItem/index.tsx b/client/src/components/FollowerUserItem/index.tsx index ae17b2d7..0ca0cbb0 100644 --- a/client/src/components/FollowerUserItem/index.tsx +++ b/client/src/components/FollowerUserItem/index.tsx @@ -3,19 +3,29 @@ import type { FC } from 'react'; import UserItem from '@components/UserItem'; import { EllipsisHorizontalIcon } from '@heroicons/react/20/solid'; +import useFollowingMutation from '@hooks/useFollowingMutation'; import React, { memo } from 'react'; interface Props { user: User; } +// TODO: 팔로잉한 사용자는 팔로우할 수 없도록 +// TODO: 팔로워, 팔로잉 정보 띄워주도록 +// TODO: 사용자 아이디 띄워주도록 const FollowerUserItem: FC = ({ user }) => { + const followingMutation = useFollowingMutation(user._id); + return ( - From bb1b5142e7a6346d8736279885fe0f0f858f8a17 Mon Sep 17 00:00:00 2001 From: leegwae Date: Thu, 1 Dec 2022 23:20:41 +0900 Subject: [PATCH 187/191] =?UTF-8?q?fix:=20=EC=83=9D=EC=84=B1=EC=9D=BC?= =?UTF-8?q?=EA=B3=BC=20=EC=88=98=EC=A0=95=EC=9D=BC=EC=9D=B4=20=EA=B0=99?= =?UTF-8?q?=EC=9C=BC=EB=A9=B4=20`(=EC=88=98=EC=A0=95=EB=90=A8)`=20=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20=EB=9D=84=EC=9B=8C=EC=A3=BC=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=ED=95=A8=20#227?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/ChatItem/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/components/ChatItem/index.tsx b/client/src/components/ChatItem/index.tsx index a2e7aca6..2c41df38 100644 --- a/client/src/components/ChatItem/index.tsx +++ b/client/src/components/ChatItem/index.tsx @@ -52,7 +52,9 @@ const ChatItem: FC = ({ {chat?.deletedAt?.length ? ( (삭제됨) ) : chat?.updatedAt.length ? ( - (수정됨) + chat.createdAt !== chat.updatedAt && ( + (수정됨) + ) ) : ( '' )} From f5f8e042d77cb67d8bdad632da5ca73e5ba12ae8 Mon Sep 17 00:00:00 2001 From: Cola Date: Thu, 1 Dec 2022 21:11:14 +0900 Subject: [PATCH 188/191] =?UTF-8?q?build:=20dev:proxy=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=BD=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/config/webpack.dev.proxy.ts | 31 ++++++++++++++++++++++++++++++ client/package.json | 1 + client/src/index.tsx | 10 +++++----- 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 client/config/webpack.dev.proxy.ts diff --git a/client/config/webpack.dev.proxy.ts b/client/config/webpack.dev.proxy.ts new file mode 100644 index 00000000..8fdb831e --- /dev/null +++ b/client/config/webpack.dev.proxy.ts @@ -0,0 +1,31 @@ +import 'webpack-dev-server'; + +import type { Configuration } from 'webpack'; + +import { merge } from 'webpack-merge'; + +import common from './webpack.common'; + +const config: Configuration = { + devtool: 'inline-source-map', + mode: 'development', + module: { + rules: [ + { + test: /\.css$/, + use: ['style-loader', 'css-loader', 'postcss-loader'], + exclude: /node_modules/, + }, + ], + }, + devServer: { + hot: true, + open: true, + historyApiFallback: true, + proxy: { + '/': 'http://49.50.167.202/', + }, + }, +}; + +export default merge(common, config); diff --git a/client/package.json b/client/package.json index 091a000c..bcefa1a0 100644 --- a/client/package.json +++ b/client/package.json @@ -5,6 +5,7 @@ "license": "MIT", "scripts": { "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.ts --progress", + "dev:proxy": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.proxy.ts --progress", "dev:https": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.https.ts --progress", "build": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.ts --progress", "analysis": "cross-env NODE_ENV=production webpack --config ./config/webpack.analysis.ts --progress", diff --git a/client/src/index.tsx b/client/src/index.tsx index ac339abd..8c32843a 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -8,11 +8,11 @@ import { injectStyle } from 'react-toastify/dist/inject-style'; import App from './App'; import './index.css'; -if (process.env.NODE_ENV === 'development') { - const { worker } = require('./mocks/browsers'); - - worker.start(); -} +// if (process.env.NODE_ENV === 'development') { +// const { worker } = require('./mocks/browsers'); +// +// worker.start(); +// } const queryClient = new QueryClient(); const root = ReactDOM.createRoot( From 6c97893c0a74b86cb3771b4edaf383639ab71216 Mon Sep 17 00:00:00 2001 From: Cola Date: Thu, 1 Dec 2022 21:39:45 +0900 Subject: [PATCH 189/191] =?UTF-8?q?refactor:=20css=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/layouts/CommunityNav/index.tsx | 8 ++++---- client/src/layouts/DmNav/index.tsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/layouts/CommunityNav/index.tsx b/client/src/layouts/CommunityNav/index.tsx index 19d65bb0..e4fe8745 100644 --- a/client/src/layouts/CommunityNav/index.tsx +++ b/client/src/layouts/CommunityNav/index.tsx @@ -52,15 +52,15 @@ const CommunityNav = () => { }; return ( -