From 8c55b34ced6f4450437efdc8596e1cb31c73fff7 Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Wed, 7 Dec 2022 06:03:33 +0900 Subject: [PATCH 01/28] =?UTF-8?q?feat(FE):=20Post=20Body=20Content=20?= =?UTF-8?q?=EB=8D=94=EB=B3=B4=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#222)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/main/PostScroll/Post/Post.scss | 8 +- .../PostScroll/Post/PostBody/PostBody.scss | 19 ++++- .../PostScroll/Post/PostBody/PostBody.tsx | 40 +++++++++- .../Post/PostFooter/PostFooter.scss | 4 +- .../Post/PostImageSlider/PostImageSlider.scss | 6 +- .../PostScroll/Post/PostTitle/PostTitle.scss | 4 +- client/src/constants/code.ts | 3 + client/src/mocks/datasource/mockConstants.ts | 79 +++++++++++++++++++ client/src/mocks/datasource/mockDataSource.ts | 3 +- 9 files changed, 148 insertions(+), 18 deletions(-) create mode 100644 client/src/mocks/datasource/mockConstants.ts diff --git a/client/src/components/main/PostScroll/Post/Post.scss b/client/src/components/main/PostScroll/Post/Post.scss index 687670bc..9fba7662 100644 --- a/client/src/components/main/PostScroll/Post/Post.scss +++ b/client/src/components/main/PostScroll/Post/Post.scss @@ -3,11 +3,9 @@ .post { box-sizing: border-box; - // 반응형 레이아웃 - display: grid; - grid-template-rows: 12fr 100fr 28fr 15fr; // 가로:세로 = 1:1.54 비율 기준 - width: 100%; - height: $main-post-height; + display: flex; + flex-direction: column; + width: 100%; // 54rem background-color: $weview-off-white; border: 1px solid $line-color; diff --git a/client/src/components/main/PostScroll/Post/PostBody/PostBody.scss b/client/src/components/main/PostScroll/Post/PostBody/PostBody.scss index 2f5e3b17..35d7337a 100644 --- a/client/src/components/main/PostScroll/Post/PostBody/PostBody.scss +++ b/client/src/components/main/PostScroll/Post/PostBody/PostBody.scss @@ -6,11 +6,9 @@ @include flex-start; box-sizing: border-box; flex-direction: column; - - grid-row: 3; gap: 0.35rem; width: 100%; - height: 100%; + min-height: 14rem; padding: $post-inline-padding; overflow: hidden; background-color: white; @@ -37,6 +35,21 @@ } &--content { + display: flex; + flex-direction: column; + flex-grow: 1; + align-items: flex-start; + width: 100%; + min-height: 8rem; font-size: $font-medium; + + &--more { + color: $placeholder-color-dark; + + &:hover { + font-weight: 700; + cursor: pointer; + } + } } } diff --git a/client/src/components/main/PostScroll/Post/PostBody/PostBody.tsx b/client/src/components/main/PostScroll/Post/PostBody/PostBody.tsx index e5f1687c..94ae0501 100644 --- a/client/src/components/main/PostScroll/Post/PostBody/PostBody.tsx +++ b/client/src/components/main/PostScroll/Post/PostBody/PostBody.tsx @@ -1,10 +1,46 @@ -import React, { useContext } from "react"; +import React, { MouseEventHandler, useContext, useState } from "react"; import { PostContext } from "@/components/main/PostScroll/Post/Post"; import TimeStamp from "@/components/commons/TimeStamp/TimeStamp"; +import { + MAXIMUM_CONTENT_ENTER, + MAXIMUM_CONTENT_LENGTH, +} from "@/constants/code"; import "./PostBody.scss"; +interface PostContentProps { + content: string; +} + +const PostContent = ({ content }: PostContentProps): JSX.Element => { + const contentSlice = content.slice(0, MAXIMUM_CONTENT_LENGTH); + const contentSliceEnterCount = contentSlice.split("\n").length - 1; + + const [isOpened, setIsOpened] = useState( + contentSlice.length < MAXIMUM_CONTENT_LENGTH && // 더보기로 나누지 않을 만큼 내용이 충분히 짧음 + contentSliceEnterCount < MAXIMUM_CONTENT_ENTER // 더보기로 나누지 않을 만큼 개행이 적음 + ); + + const handleOpenContent: MouseEventHandler = () => { + if (isOpened) { + return; + } + setIsOpened(true); + }; + + return ( +
+ {isOpened ? content : content.slice(0, MAXIMUM_CONTENT_LENGTH)} + {!isOpened && ( +

+ 더보기.. +

+ )} +
+ ); +}; + const PostBody = (): JSX.Element => { const { title, content, updatedAt } = useContext(PostContext); @@ -17,7 +53,7 @@ const PostBody = (): JSX.Element => { className={"post__body__title--time-stamp"} /> -
{content}
+ ); }; diff --git a/client/src/components/main/PostScroll/Post/PostFooter/PostFooter.scss b/client/src/components/main/PostScroll/Post/PostFooter/PostFooter.scss index 41501323..eddcab87 100644 --- a/client/src/components/main/PostScroll/Post/PostFooter/PostFooter.scss +++ b/client/src/components/main/PostScroll/Post/PostFooter/PostFooter.scss @@ -2,11 +2,11 @@ $post-inline-padding: 0.75rem; // TODO 전역 분리 box-sizing: border-box; display: flex; - grid-row: 4; + flex-shrink: 0; align-items: center; justify-content: space-between; width: 100%; - height: 100%; + height: 7.5rem; padding-inline: $post-inline-padding $post-inline-padding; overflow: hidden; diff --git a/client/src/components/main/PostScroll/Post/PostImageSlider/PostImageSlider.scss b/client/src/components/main/PostScroll/Post/PostImageSlider/PostImageSlider.scss index c3980fed..81cc3707 100644 --- a/client/src/components/main/PostScroll/Post/PostImageSlider/PostImageSlider.scss +++ b/client/src/components/main/PostScroll/Post/PostImageSlider/PostImageSlider.scss @@ -1,7 +1,7 @@ .post__image-slider { - grid-row: 2; + flex-shrink: 0; width: 100%; - height: 100%; + height: 51rem; overflow: hidden; background-color: black; @@ -10,4 +10,4 @@ height: inherit; object-fit: cover; // 가로-세로 비율 맞춰서 꽉 차게 } -} \ No newline at end of file +} diff --git a/client/src/components/main/PostScroll/Post/PostTitle/PostTitle.scss b/client/src/components/main/PostScroll/Post/PostTitle/PostTitle.scss index 73e1115a..2d538d15 100644 --- a/client/src/components/main/PostScroll/Post/PostTitle/PostTitle.scss +++ b/client/src/components/main/PostScroll/Post/PostTitle/PostTitle.scss @@ -3,11 +3,11 @@ .post__title { box-sizing: border-box; display: flex; - grid-row: 1; + flex-shrink: 0; align-items: center; justify-content: space-between; width: 100%; - height: 100%; + height: 6rem; padding-inline: 1.2rem; overflow: hidden; diff --git a/client/src/constants/code.ts b/client/src/constants/code.ts index 97966644..dd9ce8cc 100644 --- a/client/src/constants/code.ts +++ b/client/src/constants/code.ts @@ -1 +1,4 @@ export const ONE_SNAPSHOT_LINE_COUNT = 15; + +export const MAXIMUM_CONTENT_LENGTH = 200; +export const MAXIMUM_CONTENT_ENTER = 2; diff --git a/client/src/mocks/datasource/mockConstants.ts b/client/src/mocks/datasource/mockConstants.ts new file mode 100644 index 00000000..02dd3d80 --- /dev/null +++ b/client/src/mocks/datasource/mockConstants.ts @@ -0,0 +1,79 @@ +export const LARGE_CONTENT = `애국가: +동해물과 백두산이 마르고 닳도록 +하느님이 보우하사 우리나라 만세 + +무궁화 삼천리 화려강산 +대한사람 대한으로 길이 보전하세 + +남산위에 저 소나무 철갑을 두른듯 +바람서리 불변함은 우리 기상일세 + +무궁화 삼천리 화려강산 +대한사람 대한으로 길이 보전하세 + +Hey~ 한국! Hey~ 한국! +oh~ oh~ oh~ oh! 한국! + +대~한민국! 짝짝!짝!짝!짝! +대~한민국! 짝짝!짝!짝!짝! + +가을하늘 공활한데 높고 구름없이 +밝은 달은 우리가슴 일편단심일세 + +무궁화 삼천리 화려강산 +대한사람 대한으로 길이 보전하세 + +이 기상과 이 맘으로 충성을 다하여 +괴로우나 즐거우나 나라 사랑하세 + +무궁화 삼천리 화려강산 +대한사람 대한으로 길이 보전하세 + +동해물과 백!두!산!이! +동해물과 백!두!산!이! +동해물과 백!두!산!이! + +동해물과 동해물과 동해물과 백!두!산!이! + +-애국가 - 윤도현 밴드 (MR 반주곡) + +애국가: + 동해물과 백두산이 마르고 닳도록 +하느님이 보우하사 우리나라 만세 + +무궁화 삼천리 화려강산 +대한사람 대한으로 길이 보전하세 + +남산위에 저 소나무 철갑을 두른듯 +바람서리 불변함은 우리 기상일세 + +무궁화 삼천리 화려강산 +대한사람 대한으로 길이 보전하세 + +Hey~ 한국! Hey~ 한국! +oh~ oh~ oh~ oh! 한국! + +대~한민국! 짝짝!짝!짝!짝! +대~한민국! 짝짝!짝!짝!짝! + +가을하늘 공활한데 높고 구름없이 +밝은 달은 우리가슴 일편단심일세 + +무궁화 삼천리 화려강산 +대한사람 대한으로 길이 보전하세 + +이 기상과 이 맘으로 충성을 다하여 +괴로우나 즐거우나 나라 사랑하세 + +무궁화 삼천리 화려강산 +대한사람 대한으로 길이 보전하세 + +동해물과 백!두!산!이! +동해물과 백!두!산!이! +동해물과 백!두!산!이! + +동해물과 동해물과 동해물과 백!두!산!이! + +-애국가 - 윤도현 밴드 (MR 반주곡)`; + +export const SMALL_CONTENT = "코드리뷰 부탁드립니다."; diff --git a/client/src/mocks/datasource/mockDataSource.ts b/client/src/mocks/datasource/mockDataSource.ts index 02dc8113..bc68733c 100644 --- a/client/src/mocks/datasource/mockDataSource.ts +++ b/client/src/mocks/datasource/mockDataSource.ts @@ -2,6 +2,7 @@ import { PostInfo } from "@/types/post"; import { SearchHistory } from "@/types/search"; import { ReviewInfo } from "@/types/review"; import { MyInfo } from "@/types/auth"; +import { LARGE_CONTENT, SMALL_CONTENT } from "@/mocks/datasource/mockConstants"; export const mockUser: MyInfo = { id: "1", @@ -15,7 +16,7 @@ export const mockUser: MyInfo = { export const posts: PostInfo[] = Array.from(Array(1024).keys()).map((id) => ({ id: `${id}`, title: `제목_${id}`, - content: `내용_${id}`, + content: id % 2 ? LARGE_CONTENT : SMALL_CONTENT, images: [ { src: "http://placeimg.com/640/640/animals", From 3fa4aee2d0ec774e9f0a475b59910db3ff3f6406 Mon Sep 17 00:00:00 2001 From: WOOSERK <55542546+WOOSERK@users.noreply.github.com> Date: Wed, 7 Dec 2022 13:36:17 +0900 Subject: [PATCH 02/28] =?UTF-8?q?fix(BE):=20=EA=B2=80=EC=83=89=EC=8B=9C=20?= =?UTF-8?q?=EC=B5=9C=EA=B7=BC=20=EA=B2=80=EC=83=89=EC=96=B4=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=20=EB=8F=99=EA=B8=B0=ED=99=94=20?= =?UTF-8?q?(#347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/domain/post/post.controller.ts | 2 +- server/src/domain/search/search-history.mongo.repository.ts | 2 +- server/src/domain/user/user.service.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/domain/post/post.controller.ts b/server/src/domain/post/post.controller.ts index 27c638bb..9ad45567 100644 --- a/server/src/domain/post/post.controller.ts +++ b/server/src/domain/post/post.controller.ts @@ -76,7 +76,7 @@ export class PostController { await this.addLikesToPostIfLogin(headers['authorization'], returnValue); await this.addBookmarksToPostIfLogin(headers['authorization'], returnValue); - this.addSearchHistory(headers['authorization'], inqueryDto); + await this.addSearchHistory(headers['authorization'], inqueryDto); this.applyTags(tags, lastId); diff --git a/server/src/domain/search/search-history.mongo.repository.ts b/server/src/domain/search/search-history.mongo.repository.ts index 06fef170..2e76eb97 100644 --- a/server/src/domain/search/search-history.mongo.repository.ts +++ b/server/src/domain/search/search-history.mongo.repository.ts @@ -44,6 +44,6 @@ export class SearchHistoryMongoRepository extends MongoRepository ); } - this.save(searchHistory); + await this.save(searchHistory); } } diff --git a/server/src/domain/user/user.service.ts b/server/src/domain/user/user.service.ts index 2ce6afdb..dde21639 100644 --- a/server/src/domain/user/user.service.ts +++ b/server/src/domain/user/user.service.ts @@ -51,7 +51,7 @@ export class UserService { }); } - addSearchHistory( + async addSearchHistory( userId: number, { lastId, tags, reviewCount, likeCount, details }: InqueryDto, ) { @@ -65,7 +65,7 @@ export class UserService { return; } - this.searchHistoryRepository.addSearchHistory( + await this.searchHistoryRepository.addSearchHistory( userId, tags, reviewCount, From 508c72547a75fcdce2b45710fa6287b98ec0da25 Mon Sep 17 00:00:00 2001 From: WOOSERK <55542546+WOOSERK@users.noreply.github.com> Date: Wed, 7 Dec 2022 16:18:23 +0900 Subject: [PATCH 03/28] =?UTF-8?q?fix(BE):=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EB=8F=99=EA=B8=B0=ED=99=94=20=EA=B5=AC=EB=AC=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/domain/post/post.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/domain/post/post.controller.ts b/server/src/domain/post/post.controller.ts index 9ad45567..974949d8 100644 --- a/server/src/domain/post/post.controller.ts +++ b/server/src/domain/post/post.controller.ts @@ -130,7 +130,7 @@ export class PostController { } } - private addSearchHistory(token, inqueryDto: InqueryDto) { + private async addSearchHistory(token, inqueryDto: InqueryDto) { if (!token) { return; } @@ -142,7 +142,7 @@ export class PostController { // TODO 로그인한 유저가 처음 글 검색시 검색 기록을 추가해야 하기 때문에 글 검색이 있는 PostController에 위치 // 후에 글 검색 리팩토링시 같이 리팩토링이 필요할 듯 - this.userService.addSearchHistory(userId, inqueryDto); + await this.userService.addSearchHistory(userId, inqueryDto); } /** From afacc3e1d2058a0704cb66f376d0d4c30ce9daf2 Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Wed, 7 Dec 2022 18:11:23 +0900 Subject: [PATCH 04/28] =?UTF-8?q?fix(FE):=20Search=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=9D=B4=20=EC=99=84=EB=A3=8C=EB=90=9C=20=EC=9D=B4=ED=9B=84=20?= =?UTF-8?q?History=20=EC=9A=94=EC=B2=AD=EC=9D=B4=20=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8D=98=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?(#347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchContentHeader/SearchContentHeader.tsx | 6 +++++- .../SearchContent/SearchHistoryView/SearchHistoryView.tsx | 2 ++ client/src/hooks/usePostInfiniteScroll.ts | 4 ---- client/src/mocks/handlers/searchHandler.ts | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/src/components/main/MainNav/NavContent/SearchContent/SearchContentHeader/SearchContentHeader.tsx b/client/src/components/main/MainNav/NavContent/SearchContent/SearchContentHeader/SearchContentHeader.tsx index ac1895b4..22f572fd 100644 --- a/client/src/components/main/MainNav/NavContent/SearchContent/SearchContentHeader/SearchContentHeader.tsx +++ b/client/src/components/main/MainNav/NavContent/SearchContent/SearchContentHeader/SearchContentHeader.tsx @@ -6,6 +6,7 @@ import ArrowDropDownCircleSharpIcon from "@mui/icons-material/ArrowDropDownCircl import useLabel from "@/hooks/useLabel"; import { Label } from "@/types/search"; import SearchLabel from "@/components/commons/SearchLabel/SearchLabel"; +import useNav from "@/hooks/useNav"; import DetailSearchForm from "./DetailSearchForm/DetailSearchForm"; @@ -20,6 +21,7 @@ const SearchContentHeader = (): JSX.Element => { removeLabel, handleSubmit, } = useLabel(); + const { handleNavClose } = useNav(); const [isDetailOpened, setIsDetailOpened] = useState(false); return ( @@ -36,7 +38,9 @@ const SearchContentHeader = (): JSX.Element => { /> { + handleNavClose(() => handleSubmit()); + }} />
diff --git a/client/src/components/main/MainNav/NavContent/SearchContent/SearchHistoryView/SearchHistoryView.tsx b/client/src/components/main/MainNav/NavContent/SearchContent/SearchHistoryView/SearchHistoryView.tsx index 3a7f5b1c..4d0fb8f6 100644 --- a/client/src/components/main/MainNav/NavContent/SearchContent/SearchHistoryView/SearchHistoryView.tsx +++ b/client/src/components/main/MainNav/NavContent/SearchContent/SearchHistoryView/SearchHistoryView.tsx @@ -60,6 +60,8 @@ const SearchHistoryView = (): JSX.Element => { fetchSearchHistory, { suspense: true, + refetchOnMount: true, // 렌더 시 업데이트 + staleTime: 2 * 1000, // 2초 } ); diff --git a/client/src/hooks/usePostInfiniteScroll.ts b/client/src/hooks/usePostInfiniteScroll.ts index 5e1c2a42..b888102e 100644 --- a/client/src/hooks/usePostInfiniteScroll.ts +++ b/client/src/hooks/usePostInfiniteScroll.ts @@ -71,10 +71,6 @@ const usePostInfiniteScroll = (): PostInfiniteScrollResults => { { queryKey: [QUERY_KEYS.POSTS], exact: true }, { cancelRefetch: true } ); - await queryClient.refetchQueries({ - queryKey: [QUERY_KEYS.HISTORY], - type: "active", - }); })(); }, [filter, searchType]); diff --git a/client/src/mocks/handlers/searchHandler.ts b/client/src/mocks/handlers/searchHandler.ts index b3552e02..52bdde9c 100644 --- a/client/src/mocks/handlers/searchHandler.ts +++ b/client/src/mocks/handlers/searchHandler.ts @@ -8,7 +8,7 @@ const baseUrl = API_SERVER_URL; export const searchHandler = [ rest.get(`${baseUrl}/search/histories`, (req, res, ctx) => { - return res(ctx.status(200), ctx.delay(1000), ctx.json(history)); + return res(ctx.status(200), ctx.delay(500), ctx.json(history)); }), rest.delete(`${baseUrl}/search/histories/:id`, (req, res, ctx) => { setHistory( From bcf0d676d663930427e879b00e7541cf39c8e7e3 Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Wed, 7 Dec 2022 18:32:03 +0900 Subject: [PATCH 05/28] =?UTF-8?q?feat(FE):=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84,=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=82=AC=EC=A7=84=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EC=9C=A0=EC=A0=80=EC=9D=98=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=EB=A7=8C=20=EB=B3=B4=EC=9D=B4=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#352)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorProfile/AuthorProfile.scss | 30 +++++-------------- .../PostTitle/AuthorProfile/AuthorProfile.tsx | 10 ++++++- client/src/mocks/datasource/mockDataSource.ts | 4 +-- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/client/src/components/main/PostScroll/Post/PostTitle/AuthorProfile/AuthorProfile.scss b/client/src/components/main/PostScroll/Post/PostTitle/AuthorProfile/AuthorProfile.scss index dfd5f6fd..844403cc 100644 --- a/client/src/components/main/PostScroll/Post/PostTitle/AuthorProfile/AuthorProfile.scss +++ b/client/src/components/main/PostScroll/Post/PostTitle/AuthorProfile/AuthorProfile.scss @@ -12,33 +12,19 @@ height: 80%; border-radius: 50%; object-fit: contain; + + &:hover { + cursor: pointer; + } } &__username { font-size: $font-medium; - } - - &__follow-button { - @include green-button; - width: fit-content; - height: 1.4rem; - - font-size: $font-form; - - &--on { - @include weview-button; - width: fit-content; - height: 1.5rem; - - font-size: $font-form; - - color: $line-color !important; - border: 1px solid $line-color; - &:hover { - color: $weview-off-white !important; - background-color: $line-color; - } + &:hover { + text-decoration: underline; + cursor: pointer; + background-color: $light-gray; } } } diff --git a/client/src/components/main/PostScroll/Post/PostTitle/AuthorProfile/AuthorProfile.tsx b/client/src/components/main/PostScroll/Post/PostTitle/AuthorProfile/AuthorProfile.tsx index bcb73a4a..8feee9a3 100644 --- a/client/src/components/main/PostScroll/Post/PostTitle/AuthorProfile/AuthorProfile.tsx +++ b/client/src/components/main/PostScroll/Post/PostTitle/AuthorProfile/AuthorProfile.tsx @@ -1,19 +1,27 @@ import React, { useContext } from "react"; import { PostContext } from "@/components/main/PostScroll/Post/Post"; +import useSearchStore from "@/store/useSearchStore"; import "./AuthorProfile.scss"; const AuthorProfile = (): JSX.Element => { const { author } = useContext(PostContext); + const searchAuthorFilter = useSearchStore( + (state) => state.searchAuthorFilter + ); return (
searchAuthorFilter({ userId: author.id })} /> -
+
searchAuthorFilter({ userId: author.id })} + > {author.nickname}
diff --git a/client/src/mocks/datasource/mockDataSource.ts b/client/src/mocks/datasource/mockDataSource.ts index bc68733c..be2c9e08 100644 --- a/client/src/mocks/datasource/mockDataSource.ts +++ b/client/src/mocks/datasource/mockDataSource.ts @@ -16,7 +16,7 @@ export const mockUser: MyInfo = { export const posts: PostInfo[] = Array.from(Array(1024).keys()).map((id) => ({ id: `${id}`, title: `제목_${id}`, - content: id % 2 ? LARGE_CONTENT : SMALL_CONTENT, + content: id % 2 === 0 ? LARGE_CONTENT : SMALL_CONTENT, images: [ { src: "http://placeimg.com/640/640/animals", @@ -28,7 +28,7 @@ export const posts: PostInfo[] = Array.from(Array(1024).keys()).map((id) => ({ }, ], author: { - id: `${id}`, + id: `${id % 100}`, nickname: `sampleUser_${id + 10000}`, profileUrl: "http://placeimg.com/640/640/animals", email: `name_${id + 10000}@gmail.com`, From be8c464e8d142dc3567f6059b436474aa64084a9 Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Wed, 7 Dec 2022 18:54:50 +0900 Subject: [PATCH 06/28] =?UTF-8?q?refactor(FE):=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=AA=A8=EB=8B=AC=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EC=96=B4=20=EB=B6=84=EB=A6=AC=20(#351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WriteModal/CloseButton/CloseButton.tsx | 9 ++++--- .../SubmitModal/CloseButton/CloseButton.tsx | 6 ++--- .../RegisterButton/RegisterButton.tsx | 14 ++++++----- .../WriteModal/SubmitModal/SubmitModal.tsx | 4 ++-- .../WritingForm/SubmitButton/SubmitButton.tsx | 4 ++-- .../WriteModal/WritingForm/WritingForm.tsx | 4 ++-- client/src/pages/Main/Main.tsx | 4 ++-- client/src/pages/Post/Post.tsx | 4 ++-- ...eModalStore.ts => useWritingModalStore.ts} | 24 +++++-------------- 9 files changed, 31 insertions(+), 42 deletions(-) rename client/src/store/{useModalStore.ts => useWritingModalStore.ts} (61%) diff --git a/client/src/components/main/Modal/WriteModal/CloseButton/CloseButton.tsx b/client/src/components/main/Modal/WriteModal/CloseButton/CloseButton.tsx index 028eb2e7..8bc15b76 100644 --- a/client/src/components/main/Modal/WriteModal/CloseButton/CloseButton.tsx +++ b/client/src/components/main/Modal/WriteModal/CloseButton/CloseButton.tsx @@ -1,13 +1,12 @@ import React, { useCallback } from "react"; - -import "./CloseButton.scss"; - import CloseIcon from "@mui/icons-material/Close"; -import useModalStore from "@/store/useModalStore"; +import useWritingModalStore from "@/store/useWritingModalStore"; + +import "./CloseButton.scss"; const CloseButton = (): JSX.Element => { - const { closeModal } = useModalStore((state) => ({ + const { closeModal } = useWritingModalStore((state) => ({ closeModal: state.closeWritingModal, })); diff --git a/client/src/components/main/Modal/WriteModal/SubmitModal/CloseButton/CloseButton.tsx b/client/src/components/main/Modal/WriteModal/SubmitModal/CloseButton/CloseButton.tsx index 9a9c499f..e93bcf3d 100644 --- a/client/src/components/main/Modal/WriteModal/SubmitModal/CloseButton/CloseButton.tsx +++ b/client/src/components/main/Modal/WriteModal/SubmitModal/CloseButton/CloseButton.tsx @@ -1,11 +1,11 @@ import React from "react"; -import "./CloseButton.scss"; +import useWritingModalStore from "@/store/useWritingModalStore"; -import useModalStore from "@/store/useModalStore"; +import "./CloseButton.scss"; const CloseButton = (): JSX.Element => { - const { closeModal } = useModalStore((state) => ({ + const { closeModal } = useWritingModalStore((state) => ({ closeModal: state.closeSubmitModal, })); diff --git a/client/src/components/main/Modal/WriteModal/SubmitModal/RegisterButton/RegisterButton.tsx b/client/src/components/main/Modal/WriteModal/SubmitModal/RegisterButton/RegisterButton.tsx index 79417370..dad5798c 100644 --- a/client/src/components/main/Modal/WriteModal/SubmitModal/RegisterButton/RegisterButton.tsx +++ b/client/src/components/main/Modal/WriteModal/SubmitModal/RegisterButton/RegisterButton.tsx @@ -3,7 +3,7 @@ import React from "react"; import useWritingStore from "@/store/useWritingStore"; import useCodeEditorStore from "@/store/useCodeEditorStore"; import { postWritingsAPI, uploadImage } from "@/apis/post"; -import useModalStore from "@/store/useModalStore"; +import useWritingModalStore from "@/store/useWritingModalStore"; import { isEmpty } from "@/utils/typeCheck"; import { fetchPreSignedData } from "@/apis/auth"; @@ -20,11 +20,13 @@ const RegisterButton = (): JSX.Element => { ]); const images = useCodeEditorStore((state) => state.images); const resetWritingStore = useWritingStore((state) => state.reset); - const { closeWritingModal, closeSubmitModal } = useModalStore((state) => ({ - isOpened: state.isSubmitModalOpened, - closeSubmitModal: state.closeSubmitModal, - closeWritingModal: state.closeWritingModal, - })); + const { closeWritingModal, closeSubmitModal } = useWritingModalStore( + (state) => ({ + isOpened: state.isSubmitModalOpened, + closeSubmitModal: state.closeSubmitModal, + closeWritingModal: state.closeWritingModal, + }) + ); // 제출 불가능 상태 판단 const isInvalidState = (): boolean => diff --git a/client/src/components/main/Modal/WriteModal/SubmitModal/SubmitModal.tsx b/client/src/components/main/Modal/WriteModal/SubmitModal/SubmitModal.tsx index 3a323546..22e95549 100644 --- a/client/src/components/main/Modal/WriteModal/SubmitModal/SubmitModal.tsx +++ b/client/src/components/main/Modal/WriteModal/SubmitModal/SubmitModal.tsx @@ -1,6 +1,6 @@ import React, { useCallback, MouseEvent } from "react"; -import useModalStore from "@/store/useModalStore"; +import useWritingModalStore from "@/store/useWritingModalStore"; import TagInput from "@/components/main/Modal/WriteModal/SubmitModal/TagInput/TagInput"; import TitleInput from "@/components/main/Modal/WriteModal/SubmitModal/TitleInput/TitleInput"; @@ -10,7 +10,7 @@ import RegisterButton from "./RegisterButton/RegisterButton"; import "./SubmitModal.scss"; const SubmitModal = (): JSX.Element => { - const { isOpened, closeModal } = useModalStore((state) => ({ + const { isOpened, closeModal } = useWritingModalStore((state) => ({ isOpened: state.isSubmitModalOpened, closeModal: state.closeSubmitModal, })); diff --git a/client/src/components/main/Modal/WriteModal/WritingForm/SubmitButton/SubmitButton.tsx b/client/src/components/main/Modal/WriteModal/WritingForm/SubmitButton/SubmitButton.tsx index 5387d212..def57cf3 100644 --- a/client/src/components/main/Modal/WriteModal/WritingForm/SubmitButton/SubmitButton.tsx +++ b/client/src/components/main/Modal/WriteModal/WritingForm/SubmitButton/SubmitButton.tsx @@ -1,11 +1,11 @@ import React, { FormEvent, useCallback } from "react"; -import useModalStore from "@/store/useModalStore"; +import useWritingModalStore from "@/store/useWritingModalStore"; import "./SubmitButton.scss"; const SubmitButton = (): JSX.Element => { - const { openSubmitModal } = useModalStore((state) => ({ + const { openSubmitModal } = useWritingModalStore((state) => ({ openSubmitModal: state.openSubmitModal, })); diff --git a/client/src/components/main/Modal/WriteModal/WritingForm/WritingForm.tsx b/client/src/components/main/Modal/WriteModal/WritingForm/WritingForm.tsx index ee465e78..ad0bd379 100644 --- a/client/src/components/main/Modal/WriteModal/WritingForm/WritingForm.tsx +++ b/client/src/components/main/Modal/WriteModal/WritingForm/WritingForm.tsx @@ -1,6 +1,6 @@ import React, { FormEvent, useCallback } from "react"; -import useModalStore from "@/store/useModalStore"; +import useWritingModalStore from "@/store/useWritingModalStore"; import LanguageSelector from "@/components/main/Modal/WriteModal/WritingForm/LanguageSelector/LanguageSelector"; import CodeEditor from "@/components/main/CodeEditor/CodeEditor"; @@ -9,7 +9,7 @@ import SubmitButton from "./SubmitButton/SubmitButton"; import "./WritingForm.scss"; const WritingForm = (): JSX.Element => { - const { openSubmitModal } = useModalStore((state) => ({ + const { openSubmitModal } = useWritingModalStore((state) => ({ openSubmitModal: state.openSubmitModal, })); diff --git a/client/src/pages/Main/Main.tsx b/client/src/pages/Main/Main.tsx index 17f053d6..99feadec 100644 --- a/client/src/pages/Main/Main.tsx +++ b/client/src/pages/Main/Main.tsx @@ -2,13 +2,13 @@ import React from "react"; import MainNav from "@/components/main/MainNav/MainNav"; import PostScroll from "@/components/main/PostScroll/PostScroll"; -import useModalStore from "@/store/useModalStore"; +import useWritingModalStore from "@/store/useWritingModalStore"; import TagRanking from "@/components/main/TagRanking/TagRanking"; import "./Main.scss"; const Main = (): JSX.Element => { - const { isWritingModalOpened } = useModalStore((state) => ({ + const { isWritingModalOpened } = useWritingModalStore((state) => ({ isWritingModalOpened: state.isWritingModalOpened, })); diff --git a/client/src/pages/Post/Post.tsx b/client/src/pages/Post/Post.tsx index e1c21acc..163cb2a5 100644 --- a/client/src/pages/Post/Post.tsx +++ b/client/src/pages/Post/Post.tsx @@ -9,12 +9,12 @@ import PostItem from "@/components/main/PostScroll/Post/Post"; import TagRanking from "@/components/main/TagRanking/TagRanking"; import { PostInfo } from "@/types/post"; import LoadingSpinner from "@/components/commons/LoadingSpinner/LoadingSpinner"; -import useModalStore from "@/store/useModalStore"; +import useWritingModalStore from "@/store/useWritingModalStore"; import "./Post.scss"; const Post = (): JSX.Element => { - const [isWritingModalOpened] = useModalStore((state) => [ + const [isWritingModalOpened] = useWritingModalStore((state) => [ state.isWritingModalOpened, ]); const { postId } = useParams(); diff --git a/client/src/store/useModalStore.ts b/client/src/store/useWritingModalStore.ts similarity index 61% rename from client/src/store/useModalStore.ts rename to client/src/store/useWritingModalStore.ts index 17b9cddb..37942364 100644 --- a/client/src/store/useModalStore.ts +++ b/client/src/store/useWritingModalStore.ts @@ -1,33 +1,29 @@ import create from "zustand"; import { devtools } from "zustand/middleware"; -interface ModalStates { +interface WritingModalStates { isWritingModalOpened: boolean; isSubmitModalOpened: boolean; - isSearchModalOpened: boolean; } -interface ModalStore extends ModalStates, ModalActions {} +interface WritingModalStore extends WritingModalStates, WritingModalActions {} -interface ModalActions { +interface WritingModalActions { openWritingModal: () => void; closeWritingModal: () => void; openSubmitModal: () => void; closeSubmitModal: () => void; - openSearchModal: () => void; - closeSearchModal: () => void; - closeRecentOpenedModal: (() => void) | null; resetRecentOpenedModal: () => void; } -const useModalStore = create()( +const useWritingModalStore = create()( devtools((set) => ({ isWritingModalOpened: false, openWritingModal: () => - set((state: ModalStore) => ({ + set((state: WritingModalStore) => ({ isWritingModalOpened: true, closeRecentOpenedModal: state.closeWritingModal, })), @@ -37,14 +33,6 @@ const useModalStore = create()( openSubmitModal: () => set(() => ({ isSubmitModalOpened: true })), closeSubmitModal: () => set(() => ({ isSubmitModalOpened: false })), - isSearchModalOpened: false, - openSearchModal: () => - set((state: ModalStore) => ({ - isSearchModalOpened: true, - closeRecentOpenedModal: state.closeSearchModal, - })), - closeSearchModal: () => set(() => ({ isSearchModalOpened: false })), - closeRecentOpenedModal: null, resetRecentOpenedModal: () => set(() => ({ @@ -53,4 +41,4 @@ const useModalStore = create()( })) ); -export default useModalStore; +export default useWritingModalStore; From b25532a084455b5a8f18f37143af40a0dd4fb7e1 Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Wed, 7 Dec 2022 22:00:26 +0900 Subject: [PATCH 07/28] =?UTF-8?q?fix(FE):=20=EB=A9=94=EC=9D=B8=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20post=20previousdata=20mutating=20(#359)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/usePostLike.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/src/hooks/usePostLike.ts b/client/src/hooks/usePostLike.ts index 669fb826..8e9a086d 100644 --- a/client/src/hooks/usePostLike.ts +++ b/client/src/hooks/usePostLike.ts @@ -28,7 +28,13 @@ const usePostLike = ({ { onMutate: async () => { await queryClient.cancelQueries([QUERY_KEYS.POSTS]); - const previousPosts = queryClient.getQueryData([QUERY_KEYS.POSTS]); + /* + * https://tanstack.com/query/v4/docs/reference/QueryClient#queryclientgetquerydata + * only one QUERY_KEYS.POSTS data exists, it's key [0], value [1] + */ + const previousPosts = queryClient.getQueriesData([ + QUERY_KEYS.POSTS, + ])[0][1]; setIsLikedState(!isLikedState); return { previousPosts }; }, From f7d0087bb8f287e49a4bf8edcce73ab710fcccae Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Wed, 7 Dec 2022 22:01:35 +0900 Subject: [PATCH 08/28] =?UTF-8?q?fix(FE):=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20previous=20data=20mutation=20undefined=20?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EC=97=90=EB=9F=AC=20(#359)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useBookmark.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/hooks/useBookmark.ts b/client/src/hooks/useBookmark.ts index bd3ddecb..01e1be43 100644 --- a/client/src/hooks/useBookmark.ts +++ b/client/src/hooks/useBookmark.ts @@ -33,7 +33,9 @@ const useBookmark = ({ { onMutate: async () => { await queryClient.cancelQueries([QUERY_KEYS.POSTS]); - const previousPosts = queryClient.getQueryData([QUERY_KEYS.POSTS]); + const previousPosts = queryClient.getQueriesData([ + QUERY_KEYS.POSTS, + ])[0][1]; setIsBookmarkedState(!isBookmarkedState); return { previousPosts }; }, From f2fce643e5447a584a1086e58ce66efb4c058ab6 Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Wed, 7 Dec 2022 22:15:36 +0900 Subject: [PATCH 09/28] =?UTF-8?q?feat(FE):=20=EC=9E=AC=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20=EA=B0=80=EB=8A=A5=ED=95=9C=20=EB=AA=A8=EB=8B=AC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/index.html | 1 + .../components/commons/Modal/Modal/Modal.tsx | 35 +++++++++++++++++++ .../Modal/ModalContainer/ModalContainer.tsx | 15 ++++++++ .../Modal/ModalContents/ModalContents.scss | 6 ++++ .../Modal/ModalContents/ModalContents.tsx | 13 +++++++ .../Modal/ModalOverlay/ModalOverlay.scss | 11 ++++++ .../Modal/ModalOverlay/ModalOverlay.tsx | 13 +++++++ .../commons/Modal/ModalTitle/ModalTitle.scss | 10 ++++++ .../commons/Modal/ModalTitle/ModalTitle.tsx | 13 +++++++ .../Modal/ModalWrapper/ModalWrapper.scss | 18 ++++++++++ .../Modal/ModalWrapper/ModalWrapper.tsx | 19 ++++++++++ client/src/hooks/useBackgroundMouseDown.ts | 27 ++++++++++++++ 12 files changed, 181 insertions(+) create mode 100644 client/src/components/commons/Modal/Modal/Modal.tsx create mode 100644 client/src/components/commons/Modal/ModalContainer/ModalContainer.tsx create mode 100644 client/src/components/commons/Modal/ModalContents/ModalContents.scss create mode 100644 client/src/components/commons/Modal/ModalContents/ModalContents.tsx create mode 100644 client/src/components/commons/Modal/ModalOverlay/ModalOverlay.scss create mode 100644 client/src/components/commons/Modal/ModalOverlay/ModalOverlay.tsx create mode 100644 client/src/components/commons/Modal/ModalTitle/ModalTitle.scss create mode 100644 client/src/components/commons/Modal/ModalTitle/ModalTitle.tsx create mode 100644 client/src/components/commons/Modal/ModalWrapper/ModalWrapper.scss create mode 100644 client/src/components/commons/Modal/ModalWrapper/ModalWrapper.tsx create mode 100644 client/src/hooks/useBackgroundMouseDown.ts diff --git a/client/index.html b/client/index.html index b8ac8169..932b9a23 100644 --- a/client/index.html +++ b/client/index.html @@ -8,6 +8,7 @@
+ diff --git a/client/src/components/commons/Modal/Modal/Modal.tsx b/client/src/components/commons/Modal/Modal/Modal.tsx new file mode 100644 index 00000000..ba66bdde --- /dev/null +++ b/client/src/components/commons/Modal/Modal/Modal.tsx @@ -0,0 +1,35 @@ +import React, { ReactNode, useRef } from "react"; + +import ModalContainer from "@/components/commons/Modal/ModalContainer/ModalContainer"; +import ModalWrapper from "@/components/commons/Modal/ModalWrapper/ModalWrapper"; +import ModalOverlay from "@/components/commons/Modal/ModalOverlay/ModalOverlay"; +import useBackgroundMouseDown from "@/hooks/useBackgroundMouseDown"; +import ModalTitle from "@/components/commons/Modal/ModalTitle/ModalTitle"; +import ModalContents from "@/components/commons/Modal/ModalContents/ModalContents"; + +interface ModalProps { + title?: string; + onClose?: Function; + children?: ReactNode; +} + +const Modal = ({ title, onClose, children }: ModalProps): JSX.Element => { + const modalRef = useRef(null); + const handleModalClose = (): void => { + onClose?.(); + }; + useBackgroundMouseDown(modalRef, handleModalClose); + + return ( + + + + + {children} + + + + ); +}; + +export default Modal; diff --git a/client/src/components/commons/Modal/ModalContainer/ModalContainer.tsx b/client/src/components/commons/Modal/ModalContainer/ModalContainer.tsx new file mode 100644 index 00000000..8d9a9fe1 --- /dev/null +++ b/client/src/components/commons/Modal/ModalContainer/ModalContainer.tsx @@ -0,0 +1,15 @@ +import React, { ReactNode, ReactPortal } from "react"; +import { createPortal } from "react-dom"; + +interface ModalContainerProps { + children: ReactNode; +} + +const ModalContainer = ({ children }: ModalContainerProps): ReactPortal => { + const modalElement = document.getElementById("modal") as HTMLElement; + const modalContents = <>{children}; + + return createPortal(modalContents, modalElement); +}; + +export default ModalContainer; diff --git a/client/src/components/commons/Modal/ModalContents/ModalContents.scss b/client/src/components/commons/Modal/ModalContents/ModalContents.scss new file mode 100644 index 00000000..283a3ce9 --- /dev/null +++ b/client/src/components/commons/Modal/ModalContents/ModalContents.scss @@ -0,0 +1,6 @@ +@import "@/styles/_theme"; + +.modal-contents { + font-size: $font-medium; + color: $text-color; +} diff --git a/client/src/components/commons/Modal/ModalContents/ModalContents.tsx b/client/src/components/commons/Modal/ModalContents/ModalContents.tsx new file mode 100644 index 00000000..287b5284 --- /dev/null +++ b/client/src/components/commons/Modal/ModalContents/ModalContents.tsx @@ -0,0 +1,13 @@ +import React, { ReactNode } from "react"; + +import "./ModalContents.scss"; + +interface ModalContentsProps { + children?: ReactNode; +} + +const ModalContents = ({ children }: ModalContentsProps): JSX.Element => { + return
{children}
; +}; + +export default ModalContents; diff --git a/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.scss b/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.scss new file mode 100644 index 00000000..85b6af35 --- /dev/null +++ b/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.scss @@ -0,0 +1,11 @@ +$highest-z-index: 90000; // 가장 높은 z-index 값으로 설정 + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + z-index: $highest-z-index; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.2); // 모달 배경 색 +} diff --git a/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.tsx b/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.tsx new file mode 100644 index 00000000..c144ca07 --- /dev/null +++ b/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.tsx @@ -0,0 +1,13 @@ +import React, { ReactNode } from "react"; + +import "./ModalOverlay.scss"; + +interface ModalOverlayProps { + children: ReactNode; +} + +const ModalOverlay = ({ children }: ModalOverlayProps): JSX.Element => { + return
{children}
; +}; + +export default ModalOverlay; diff --git a/client/src/components/commons/Modal/ModalTitle/ModalTitle.scss b/client/src/components/commons/Modal/ModalTitle/ModalTitle.scss new file mode 100644 index 00000000..bc0319f6 --- /dev/null +++ b/client/src/components/commons/Modal/ModalTitle/ModalTitle.scss @@ -0,0 +1,10 @@ +@import "@/styles/_theme"; + +.modal-title { + width: 100%; + height: 3rem; + font-size: $font-large; + // div 텍스트 중앙 정렬 + line-height: 3rem; + text-align: center; +} diff --git a/client/src/components/commons/Modal/ModalTitle/ModalTitle.tsx b/client/src/components/commons/Modal/ModalTitle/ModalTitle.tsx new file mode 100644 index 00000000..f282ed15 --- /dev/null +++ b/client/src/components/commons/Modal/ModalTitle/ModalTitle.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +import "./ModalTitle.scss"; + +interface ModalTitleProps { + title?: string; +} + +const ModalTitle = ({ title }: ModalTitleProps): JSX.Element => { + return
{title ?? ""}
; +}; + +export default ModalTitle; diff --git a/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.scss b/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.scss new file mode 100644 index 00000000..108a65f6 --- /dev/null +++ b/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.scss @@ -0,0 +1,18 @@ +@import "@/styles/_theme"; + +.modal-wrapper { + position: absolute; + // 좌측 상단 위치를 modal 중앙에 위치 + top: 50%; + left: 50%; + box-sizing: border-box; + width: fit-content; + min-width: 20rem; + height: fit-content; + min-height: 5rem; + background-color: $weview-white; + border-radius: $radius-modal; + box-shadow: $shadow-box; + // wrapper 의 중앙을 modal 중앙에 위치 + transform: translate(-50%, -50%); +} diff --git a/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.tsx b/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.tsx new file mode 100644 index 00000000..f527cbfe --- /dev/null +++ b/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.tsx @@ -0,0 +1,19 @@ +import React, { forwardRef, ReactNode } from "react"; + +import "./ModalWrapper.scss"; + +interface ModalWrapperProps { + children: ReactNode; +} + +const ModalWrapper = forwardRef( + ({ children }, ref): JSX.Element => { + return ( +
+ {children} +
+ ); + } +); + +export default ModalWrapper; diff --git a/client/src/hooks/useBackgroundMouseDown.ts b/client/src/hooks/useBackgroundMouseDown.ts new file mode 100644 index 00000000..5acf9ab8 --- /dev/null +++ b/client/src/hooks/useBackgroundMouseDown.ts @@ -0,0 +1,27 @@ +import { RefObject, useEffect } from "react"; + +/** + * ref 외부의 요소를 클릭했을 경우 실행할 콜백 함수를 등록합니다. + */ +const useBackgroundMouseDown = ( + ref: RefObject, + callback?: (event?: Event) => void +): void => { + useEffect(() => { + const handleMouseDown = (e: Event): void => { + if (ref.current === null || ref.current.contains(e.target as Node)) { + return; + } + callback?.(e); // 모달 외부 요소 클릭 시 실행 + }; + // "click" 이벤트로 사용하려면 캡쳐링을 사용해야 함. (혹은 stopPropagation 사용 필요) + window.addEventListener("mousedown", handleMouseDown); + window.addEventListener("touchstart", handleMouseDown); + return () => { + window.removeEventListener("mousedown", handleMouseDown); + window.removeEventListener("touchstart", handleMouseDown); + }; + }, [ref, callback]); +}; + +export default useBackgroundMouseDown; From 48d88b85dd8059c221243d6a6b730715f9ae435b Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Wed, 7 Dec 2022 22:32:35 +0900 Subject: [PATCH 10/28] =?UTF-8?q?fix(FE):=20=EA=B0=9C=EB=B3=84=20=ED=8F=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=97=90=EC=84=9C=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=8B=9C=20=EB=A9=94=EC=9D=B8=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=A1=9C=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=ED=9B=84=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20(#349)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useLabel.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/client/src/hooks/useLabel.ts b/client/src/hooks/useLabel.ts index fd9d01ec..392fc763 100644 --- a/client/src/hooks/useLabel.ts +++ b/client/src/hooks/useLabel.ts @@ -1,10 +1,5 @@ -import { - ChangeEvent, - KeyboardEvent, - useCallback, - useEffect, - useState, -} from "react"; +import { ChangeEvent, KeyboardEvent, useCallback, useState } from "react"; +import { useNavigate } from "react-router-dom"; import { Label } from "@/types/search"; import useSearchStore from "@/store/useSearchStore"; @@ -28,6 +23,7 @@ const useLabel = (): UseLabelResult => { const [searchDefaultFilter] = useSearchStore((state) => [ state.searchDefaultFilter, ]); + const navigate = useNavigate(); const [word, setWord] = useState(""); // 입력중인 검색어 const [labels, setLabels] = useLabelStore((state) => [ state.labels, @@ -80,6 +76,7 @@ const useLabel = (): UseLabelResult => { // PostScroll 에 현재 검색 필터를 적용 const handleSubmit = (): void => { + navigate("/"); searchDefaultFilter(createSearchFilter(labels)); }; From c6dbdf1329f90204ee6fb6f163b08037ad797ca7 Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Wed, 7 Dec 2022 22:34:01 +0900 Subject: [PATCH 11/28] =?UTF-8?q?fix(FE):=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=EB=82=98=20=EC=8A=A4=ED=81=AC=EB=9E=A9=20=ED=81=B4=EB=A6=AD?= =?UTF-8?q?=EC=8B=9C=20=EA=B9=9C=EB=B9=A1=EC=9D=B4=EB=8A=94=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EC=88=98=EC=A0=95=20(#349)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/Post/Post.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/pages/Post/Post.tsx b/client/src/pages/Post/Post.tsx index e1c21acc..693d8395 100644 --- a/client/src/pages/Post/Post.tsx +++ b/client/src/pages/Post/Post.tsx @@ -18,8 +18,8 @@ const Post = (): JSX.Element => { state.isWritingModalOpened, ]); const { postId } = useParams(); - const { isFetching, data } = useQuery( - [QUERY_KEYS.POSTS, postId], + const { isLoading, data } = useQuery( + [QUERY_KEYS.POSTS], async () => await getPostItem(postId as string) ); @@ -28,7 +28,7 @@ const Post = (): JSX.Element => {
- {isFetching ? ( + {isLoading ? ( ) : ( From ea2ff70adb292f869dd95435b0c209ae05f5e3ac Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Wed, 7 Dec 2022 22:34:32 +0900 Subject: [PATCH 12/28] =?UTF-8?q?design(FE):=20=EB=8D=94=EB=B3=B4=EA=B8=B0?= =?UTF-8?q?=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20=ED=8F=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EA=B0=80=EB=8A=A5=ED=95=98?= =?UTF-8?q?=EA=B2=8C=EB=81=94=20=EC=88=98=EC=A0=95=20(#349)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/Post/Post.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/pages/Post/Post.scss b/client/src/pages/Post/Post.scss index 80480bf7..66032d63 100644 --- a/client/src/pages/Post/Post.scss +++ b/client/src/pages/Post/Post.scss @@ -10,6 +10,10 @@ align-items: center; width: $main-post-bar-width; height: 100%; - margin-top: 4rem; + overflow-y: scroll; background-color: #fcfcfc; + + &::-webkit-scrollbar { + display: none; + } } From ea3fc8a1cf84c1b587b45bc88791c1a0bfd18bf3 Mon Sep 17 00:00:00 2001 From: WOOSERK <55542546+WOOSERK@users.noreply.github.com> Date: Wed, 7 Dec 2022 22:53:28 +0900 Subject: [PATCH 13/28] =?UTF-8?q?feat(BE):=20=EA=B8=80=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20API=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A2=8B=EC=95=84=EC=9A=94,=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=91=9C=EC=8B=9C=20(#361)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/domain/likes/likes.service.ts | 9 +++++++++ server/src/domain/post/post.controller.ts | 19 +++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/server/src/domain/likes/likes.service.ts b/server/src/domain/likes/likes.service.ts index fc81eb1a..4f03af1b 100644 --- a/server/src/domain/likes/likes.service.ts +++ b/server/src/domain/likes/likes.service.ts @@ -45,6 +45,15 @@ export class LikesService { return postsYouLiked.map((likesInfo) => likesInfo.postId); } + async getIsLiked(postId: number, userId: number) { + const likes = await this.likesRepository.findOneBy({ + postId, + userId, + }); + + return !!likes; + } + async countLikesCntByPostId(postId: number) { return this.likesRepository.count({ where: { diff --git a/server/src/domain/post/post.controller.ts b/server/src/domain/post/post.controller.ts index 974949d8..801f701f 100644 --- a/server/src/domain/post/post.controller.ts +++ b/server/src/domain/post/post.controller.ts @@ -214,9 +214,24 @@ export class PostController { @ApiNotFoundResponse({ description: '유저 혹은 게시물이 존재하지 않습니다', }) - async inqueryPost(@Param('postId') postId: number) { + async inqueryPost(@Param('postId') postId: number, @Headers() header) { try { - return { post: await this.postService.inqueryPost(postId) }; + const post = await this.postService.inqueryPost(postId); + post.likesCount = await this.likesService.countLikesCntByPostId(post.id); + + const token = header['authorization']; + if (token) { + const userId = this.authService.authenticate(token); + if (userId) { + post.isLiked = await this.likesService.getIsLiked(postId, userId); + post.isBookmarked = await this.bookmarkService.getIsBookmarked( + postId, + userId, + ); + } + } + + return { post: post }; } catch (err) { if (err instanceof PostNotFoundException) { throw new NotFoundException(err.message); From 418b6ed79160cf5c92a2a45e1f382b40ebf0339d Mon Sep 17 00:00:00 2001 From: WOOSERK <55542546+WOOSERK@users.noreply.github.com> Date: Wed, 7 Dec 2022 22:54:17 +0900 Subject: [PATCH 14/28] =?UTF-8?q?feat(BE):=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=EB=90=9C=20=EA=B8=80=20=EB=AA=A9=EB=A1=9D=20API=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20(#361)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/domain/bookmark/bookmark.module.ts | 2 ++ .../src/domain/bookmark/bookmark.service.ts | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/server/src/domain/bookmark/bookmark.module.ts b/server/src/domain/bookmark/bookmark.module.ts index 40db779e..7df20136 100644 --- a/server/src/domain/bookmark/bookmark.module.ts +++ b/server/src/domain/bookmark/bookmark.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { LikesRepository } from '../likes/likes.repository'; import { PostRepository } from '../post/post.repository'; import { UserRepository } from '../user/user.repository'; import { BookmarkController } from './bookmark.controller'; @@ -12,6 +13,7 @@ import { BookmarkService } from './bookmark.service'; BookmarkRepository, UserRepository, PostRepository, + LikesRepository, ], exports: [BookmarkService, BookmarkRepository], }) diff --git a/server/src/domain/bookmark/bookmark.service.ts b/server/src/domain/bookmark/bookmark.service.ts index d1f3f1bd..9561f036 100644 --- a/server/src/domain/bookmark/bookmark.service.ts +++ b/server/src/domain/bookmark/bookmark.service.ts @@ -14,6 +14,7 @@ import { import { UserNotSameException } from '../../exception/user-not-same.exception'; import { PostNotFoundException } from '../../exception/post-not-found.exception'; import { LoadPostListResponseDto } from '../post/dto/service-response.dto'; +import { LikesRepository } from '../likes/likes.repository'; @Injectable() export class BookmarkService { @@ -21,6 +22,7 @@ export class BookmarkService { private bookmarkRepository: BookmarkRepository, private userRepository: UserRepository, private postRepository: PostRepository, + private likesRepository: LikesRepository, ) {} async getAll(userId: number, { lastId }: BookmarkGetAllRequestDto) { @@ -44,7 +46,14 @@ export class BookmarkService { isLast, ); - postList.posts.forEach((post) => (post.isBookmarked = true)); + const likes = await this.likesRepository.findBy({ + userId, + }); + postList.posts.forEach((post) => { + post.isBookmarked = true; + post.isLiked = likes.some((like) => like.postId === post.id); + }); + return postList; } @@ -123,4 +132,17 @@ export class BookmarkService { return postsYouBookmarked.map((bookmarkInfo) => bookmarkInfo.post.id); } + + async getIsBookmarked(postId: number, userId: number) { + const bookmark = await this.bookmarkRepository.findOneBy({ + post: { + id: postId, + }, + user: { + id: userId, + }, + }); + + return !!bookmark; + } } From a43ab85779ee4b3ff45dff49d46665abfb6a429b Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 01:54:27 +0900 Subject: [PATCH 15/28] =?UTF-8?q?design(FE):=20=EC=97=90=EB=94=94=ED=84=B0?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EC=AA=BD=20=EB=94=94=EC=9E=90=EC=9D=B8?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20(#223)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.scss | 5 +++++ .../Modal/ModalContainer/ModalContainer.scss | 14 ++++---------- .../Modal/ModalWrapper/CommonModalWrapper.tsx | 14 +++++++++++--- .../main/Modal/ModalWrapper/ModalWrapper.scss | 17 +++++++++++++++++ .../WriteModal/SnapShotNav/SnapShotNav.scss | 12 ++++++------ .../main/Modal/WriteModal/WriteModal.scss | 3 +++ client/src/constants/options.ts | 7 +++---- client/src/styles/_global-style.scss | 7 +++---- client/src/utils/dom.ts | 4 ++++ 9 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 client/src/utils/dom.ts diff --git a/client/src/App.scss b/client/src/App.scss index bf7902a7..f4c72428 100644 --- a/client/src/App.scss +++ b/client/src/App.scss @@ -15,6 +15,11 @@ @import "@/styles/global-style"; @import "@/styles/theme"; +* { + padding: 0; + margin: 0; +} + html, body { height: 100%; diff --git a/client/src/components/main/Modal/ModalContainer/ModalContainer.scss b/client/src/components/main/Modal/ModalContainer/ModalContainer.scss index a097a067..6ff2f5e0 100644 --- a/client/src/components/main/Modal/ModalContainer/ModalContainer.scss +++ b/client/src/components/main/Modal/ModalContainer/ModalContainer.scss @@ -1,21 +1,15 @@ @import "@/styles/theme"; +@import "@/styles/responsive"; @import "@/styles/global-style"; @import "@/styles/animation"; .modal-container { - // root 의 가운데 정렬 - position: absolute; - top: 0; - right: 0; - left: 0; - z-index: $modal-content-z-index; + z-index: $modal-content-z-index !important; box-sizing: border-box; - display: flex; - width: 100%; + width: fit-content; max-width: $desktop-main-width; - height: 100%; - + height: 80%; // 모달 내용들은 공통으로 2rem 패딩 내에 존재 padding: 2rem; margin: 0 auto; diff --git a/client/src/components/main/Modal/ModalWrapper/CommonModalWrapper.tsx b/client/src/components/main/Modal/ModalWrapper/CommonModalWrapper.tsx index c7659b4c..01b0bdeb 100644 --- a/client/src/components/main/Modal/ModalWrapper/CommonModalWrapper.tsx +++ b/client/src/components/main/Modal/ModalWrapper/CommonModalWrapper.tsx @@ -1,7 +1,9 @@ import React, { useCallback, MouseEvent } from "react"; +import CloseIcon from "@mui/icons-material/Close"; import ModalContainer from "@/components/main/Modal/ModalContainer/ModalContainer"; import useCommonModalStore from "@/store/useCommonModalStore"; +import { isCloseModalElement } from "@/utils/dom"; import "./ModalWrapper.scss"; @@ -12,7 +14,8 @@ const CommonModalWrapper = (): JSX.Element => { ]); const clickWrapperBackGround = useCallback( - (e: MouseEvent) => { + (e: MouseEvent) => { + if (!isCloseModalElement(e.target as HTMLElement)) return; closeModal(); }, [] @@ -20,8 +23,13 @@ const CommonModalWrapper = (): JSX.Element => { return modalContent !== null ? ( <> -
- +
+ + +
) : ( <> diff --git a/client/src/components/main/Modal/ModalWrapper/ModalWrapper.scss b/client/src/components/main/Modal/ModalWrapper/ModalWrapper.scss index a0025f83..22f2386f 100644 --- a/client/src/components/main/Modal/ModalWrapper/ModalWrapper.scss +++ b/client/src/components/main/Modal/ModalWrapper/ModalWrapper.scss @@ -1,9 +1,26 @@ @import "@/styles/_global-style.scss"; +@import "@/styles/theme.scss"; +@import "@/styles/responsive"; .modal-background { position: absolute; top: 0; z-index: $modal-background-z-index !important; + display: flex; + align-items: center; width: 100vw; height: 100vh; + min-height: 100vh; + background-color: rgba(0, 0, 0, 0.75); +} + +.modal-close-button { + position: absolute; + top: 2rem; + right: 2rem; + z-index: $modal-content-z-index; + font-size: 2rem; + color: $line-color; + cursor: pointer; + transform: scale(2); } diff --git a/client/src/components/main/Modal/WriteModal/SnapShotNav/SnapShotNav.scss b/client/src/components/main/Modal/WriteModal/SnapShotNav/SnapShotNav.scss index 3c046953..0ffa3151 100644 --- a/client/src/components/main/Modal/WriteModal/SnapShotNav/SnapShotNav.scss +++ b/client/src/components/main/Modal/WriteModal/SnapShotNav/SnapShotNav.scss @@ -5,7 +5,7 @@ flex-direction: column; flex-grow: 1; align-items: center; - width: auto !important; + width: 35rem !important; height: 100%; overflow-x: hidden; overflow-y: auto; @@ -14,16 +14,16 @@ box-shadow: $shadow-box; &__item { - width: 40rem; - height: 40rem; + width: 30rem; + height: 30rem; margin: 1rem 0; &--img { - width: 40rem; - height: 40rem; + width: 100%; + height: 100%; cursor: pointer; border-radius: $radius-medium; - object-fit: cover; + object-fit: contain; } } @media screen and (max-width: 720px) { diff --git a/client/src/components/main/Modal/WriteModal/WriteModal.scss b/client/src/components/main/Modal/WriteModal/WriteModal.scss index 361fb8b7..0aad7f83 100644 --- a/client/src/components/main/Modal/WriteModal/WriteModal.scss +++ b/client/src/components/main/Modal/WriteModal/WriteModal.scss @@ -6,4 +6,7 @@ gap: 1rem; width: 100%; height: 100%; + @media screen and (max-width: 720px) { + flex-direction: column; + } } diff --git a/client/src/constants/options.ts b/client/src/constants/options.ts index 43f6a1b1..d882e432 100644 --- a/client/src/constants/options.ts +++ b/client/src/constants/options.ts @@ -19,13 +19,12 @@ export const LANGUAGES = [ export const DEFAULT_LANGUAGE = "JavaScript"; export const IMAGE_OPTIONS = { - width: 400, - height: 400, + width: 600, + height: 600, bgcolor: "#292c33", style: { display: "flex", - justifyContent: "center", alignItems: "center", - paddingInline: "15px", + paddingInline: "16px", }, }; diff --git a/client/src/styles/_global-style.scss b/client/src/styles/_global-style.scss index 4d050cf2..4e979b4b 100644 --- a/client/src/styles/_global-style.scss +++ b/client/src/styles/_global-style.scss @@ -33,14 +33,13 @@ $desktop-main-width: $main-nav-width + $main-post-bar-width + $main-gap + */ $default-z-index: 0; -$modal-background-z-index: 1; -$modal-content-z-index: 2; -$modal-upper-z-index: 3; +$modal-background-z-index: 20000; +$modal-content-z-index: 20001; +$modal-upper-z-index: 20002; /** * editor */ - $editing-form-width: 72rem; $device-editor-width: 64rem; // device 최대 너비가 69.5rem - 6:4정도 유지를 위해 40rem 선정 $line-width: 4rem; diff --git a/client/src/utils/dom.ts b/client/src/utils/dom.ts new file mode 100644 index 00000000..1fb85075 --- /dev/null +++ b/client/src/utils/dom.ts @@ -0,0 +1,4 @@ +export const isCloseModalElement = (element: HTMLElement): boolean => + element.matches(".modal-background") || + element.matches(".modal-close-button") || + element.matches("path"); From b837767826574d6eda2d4d8fff8fb9102bb98574 Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Thu, 8 Dec 2022 02:31:34 +0900 Subject: [PATCH 16/28] =?UTF-8?q?feat(FE):=20=EB=AA=A8=EB=8B=AC=EC=9D=84?= =?UTF-8?q?=20=EC=A0=84=EC=97=AD=EC=9C=BC=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20(#351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 2 + .../Modal/ModalProvider/ModalProvider.tsx | 25 ++++++++++++ .../commons/Modal/{ => core}/Modal/Modal.tsx | 14 +++---- .../ModalContainer/ModalContainer.tsx | 0 .../ModalContents/ModalContents.scss | 2 +- .../ModalContents/ModalContents.tsx | 0 .../{ => core}/ModalOverlay/ModalOverlay.scss | 1 + .../{ => core}/ModalOverlay/ModalOverlay.tsx | 0 .../{ => core}/ModalTitle/ModalTitle.scss | 2 +- .../{ => core}/ModalTitle/ModalTitle.tsx | 0 .../{ => core}/ModalWrapper/ModalWrapper.scss | 2 +- .../{ => core}/ModalWrapper/ModalWrapper.tsx | 0 .../PostFooter/RightBlockItems/MoreButton.tsx | 22 +++++++---- .../PostMoreModal/PostMoreModal.tsx | 24 ++++++++++++ client/src/hooks/useBackgroundMouseDown.ts | 27 ------------- client/src/hooks/useModal.ts | 26 +++++++++++++ client/src/hooks/useOutsideClickHandler.ts | 24 ++++++++++++ client/src/store/useModalStore.ts | 39 +++++++++++++++++++ client/src/types/modal.ts | 12 ++++++ 19 files changed, 177 insertions(+), 45 deletions(-) create mode 100644 client/src/components/commons/Modal/ModalProvider/ModalProvider.tsx rename client/src/components/commons/Modal/{ => core}/Modal/Modal.tsx (52%) rename client/src/components/commons/Modal/{ => core}/ModalContainer/ModalContainer.tsx (100%) rename client/src/components/commons/Modal/{ => core}/ModalContents/ModalContents.scss (68%) rename client/src/components/commons/Modal/{ => core}/ModalContents/ModalContents.tsx (100%) rename client/src/components/commons/Modal/{ => core}/ModalOverlay/ModalOverlay.scss (89%) rename client/src/components/commons/Modal/{ => core}/ModalOverlay/ModalOverlay.tsx (100%) rename client/src/components/commons/Modal/{ => core}/ModalTitle/ModalTitle.scss (82%) rename client/src/components/commons/Modal/{ => core}/ModalTitle/ModalTitle.tsx (100%) rename client/src/components/commons/Modal/{ => core}/ModalWrapper/ModalWrapper.scss (92%) rename client/src/components/commons/Modal/{ => core}/ModalWrapper/ModalWrapper.tsx (100%) create mode 100644 client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx delete mode 100644 client/src/hooks/useBackgroundMouseDown.ts create mode 100644 client/src/hooks/useModal.ts create mode 100644 client/src/hooks/useOutsideClickHandler.ts create mode 100644 client/src/store/useModalStore.ts create mode 100644 client/src/types/modal.ts diff --git a/client/src/App.tsx b/client/src/App.tsx index 7e0ff77e..89a4844b 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -7,6 +7,7 @@ import { isEmpty } from "@/utils/typeCheck"; import ReactRouter from "@/ReactRouter"; import CommonModalWrapper from "@/components/main/Modal/ModalWrapper/CommonModalWrapper"; import { useRefreshInterceptor } from "@/hooks/useRefreshInterceptor"; +import ModalProvider from "@/components/commons/Modal/ModalProvider/ModalProvider"; import "./App.scss"; @@ -21,6 +22,7 @@ function App(): JSX.Element { + ); diff --git a/client/src/components/commons/Modal/ModalProvider/ModalProvider.tsx b/client/src/components/commons/Modal/ModalProvider/ModalProvider.tsx new file mode 100644 index 00000000..bcd9e669 --- /dev/null +++ b/client/src/components/commons/Modal/ModalProvider/ModalProvider.tsx @@ -0,0 +1,25 @@ +import React, { ReactPortal } from "react"; +import { createPortal } from "react-dom"; + +import useModalStore from "@/store/useModalStore"; +import PostMoreModal from "@/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal"; +import { MODAL_KEY, ModalProps } from "@/types/modal"; + +const MODALS = new Map>([ + [MODAL_KEY.POST_MORE, PostMoreModal], +]); + +const ModalProvider = (): ReactPortal => { + const modals = useModalStore((state) => state.modals); + const Modals = modals.map(({ key, props }) => { + const Modal = MODALS.get(key) as React.FC; + return ; + }); + + return createPortal( + <>{Modals}, + document.getElementById("modal") as Element + ); +}; + +export default ModalProvider; diff --git a/client/src/components/commons/Modal/Modal/Modal.tsx b/client/src/components/commons/Modal/core/Modal/Modal.tsx similarity index 52% rename from client/src/components/commons/Modal/Modal/Modal.tsx rename to client/src/components/commons/Modal/core/Modal/Modal.tsx index ba66bdde..5db96bbf 100644 --- a/client/src/components/commons/Modal/Modal/Modal.tsx +++ b/client/src/components/commons/Modal/core/Modal/Modal.tsx @@ -1,11 +1,11 @@ import React, { ReactNode, useRef } from "react"; -import ModalContainer from "@/components/commons/Modal/ModalContainer/ModalContainer"; -import ModalWrapper from "@/components/commons/Modal/ModalWrapper/ModalWrapper"; -import ModalOverlay from "@/components/commons/Modal/ModalOverlay/ModalOverlay"; -import useBackgroundMouseDown from "@/hooks/useBackgroundMouseDown"; -import ModalTitle from "@/components/commons/Modal/ModalTitle/ModalTitle"; -import ModalContents from "@/components/commons/Modal/ModalContents/ModalContents"; +import ModalContainer from "@/components/commons/Modal/core/ModalContainer/ModalContainer"; +import ModalWrapper from "@/components/commons/Modal/core/ModalWrapper/ModalWrapper"; +import ModalOverlay from "@/components/commons/Modal/core/ModalOverlay/ModalOverlay"; +import useOutsideClickHandler from "@/hooks/useOutsideClickHandler"; +import ModalTitle from "@/components/commons/Modal/core/ModalTitle/ModalTitle"; +import ModalContents from "@/components/commons/Modal/core/ModalContents/ModalContents"; interface ModalProps { title?: string; @@ -18,7 +18,7 @@ const Modal = ({ title, onClose, children }: ModalProps): JSX.Element => { const handleModalClose = (): void => { onClose?.(); }; - useBackgroundMouseDown(modalRef, handleModalClose); + useOutsideClickHandler(modalRef, handleModalClose); return ( diff --git a/client/src/components/commons/Modal/ModalContainer/ModalContainer.tsx b/client/src/components/commons/Modal/core/ModalContainer/ModalContainer.tsx similarity index 100% rename from client/src/components/commons/Modal/ModalContainer/ModalContainer.tsx rename to client/src/components/commons/Modal/core/ModalContainer/ModalContainer.tsx diff --git a/client/src/components/commons/Modal/ModalContents/ModalContents.scss b/client/src/components/commons/Modal/core/ModalContents/ModalContents.scss similarity index 68% rename from client/src/components/commons/Modal/ModalContents/ModalContents.scss rename to client/src/components/commons/Modal/core/ModalContents/ModalContents.scss index 283a3ce9..f93467dc 100644 --- a/client/src/components/commons/Modal/ModalContents/ModalContents.scss +++ b/client/src/components/commons/Modal/core/ModalContents/ModalContents.scss @@ -1,4 +1,4 @@ -@import "@/styles/_theme"; +@import "@/styles/_theme.scss"; .modal-contents { font-size: $font-medium; diff --git a/client/src/components/commons/Modal/ModalContents/ModalContents.tsx b/client/src/components/commons/Modal/core/ModalContents/ModalContents.tsx similarity index 100% rename from client/src/components/commons/Modal/ModalContents/ModalContents.tsx rename to client/src/components/commons/Modal/core/ModalContents/ModalContents.tsx diff --git a/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.scss b/client/src/components/commons/Modal/core/ModalOverlay/ModalOverlay.scss similarity index 89% rename from client/src/components/commons/Modal/ModalOverlay/ModalOverlay.scss rename to client/src/components/commons/Modal/core/ModalOverlay/ModalOverlay.scss index 85b6af35..6d93e63a 100644 --- a/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.scss +++ b/client/src/components/commons/Modal/core/ModalOverlay/ModalOverlay.scss @@ -8,4 +8,5 @@ $highest-z-index: 90000; // 가장 높은 z-index 값으로 설정 width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.2); // 모달 배경 색 + backdrop-filter: blur(2px); } diff --git a/client/src/components/commons/Modal/ModalOverlay/ModalOverlay.tsx b/client/src/components/commons/Modal/core/ModalOverlay/ModalOverlay.tsx similarity index 100% rename from client/src/components/commons/Modal/ModalOverlay/ModalOverlay.tsx rename to client/src/components/commons/Modal/core/ModalOverlay/ModalOverlay.tsx diff --git a/client/src/components/commons/Modal/ModalTitle/ModalTitle.scss b/client/src/components/commons/Modal/core/ModalTitle/ModalTitle.scss similarity index 82% rename from client/src/components/commons/Modal/ModalTitle/ModalTitle.scss rename to client/src/components/commons/Modal/core/ModalTitle/ModalTitle.scss index bc0319f6..a118f5b3 100644 --- a/client/src/components/commons/Modal/ModalTitle/ModalTitle.scss +++ b/client/src/components/commons/Modal/core/ModalTitle/ModalTitle.scss @@ -1,4 +1,4 @@ -@import "@/styles/_theme"; +@import "@/styles/_theme.scss"; .modal-title { width: 100%; diff --git a/client/src/components/commons/Modal/ModalTitle/ModalTitle.tsx b/client/src/components/commons/Modal/core/ModalTitle/ModalTitle.tsx similarity index 100% rename from client/src/components/commons/Modal/ModalTitle/ModalTitle.tsx rename to client/src/components/commons/Modal/core/ModalTitle/ModalTitle.tsx diff --git a/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.scss b/client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.scss similarity index 92% rename from client/src/components/commons/Modal/ModalWrapper/ModalWrapper.scss rename to client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.scss index 108a65f6..91968939 100644 --- a/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.scss +++ b/client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.scss @@ -1,4 +1,4 @@ -@import "@/styles/_theme"; +@import "@/styles/_theme.scss"; .modal-wrapper { position: absolute; diff --git a/client/src/components/commons/Modal/ModalWrapper/ModalWrapper.tsx b/client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.tsx similarity index 100% rename from client/src/components/commons/Modal/ModalWrapper/ModalWrapper.tsx rename to client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.tsx diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/MoreButton.tsx b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/MoreButton.tsx index 88a323c2..f91877c2 100644 --- a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/MoreButton.tsx +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/MoreButton.tsx @@ -1,20 +1,26 @@ -import React, { MouseEventHandler } from "react"; +import React, { useContext } from "react"; import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; import SvgIconButton from "@/components/commons/SvgIconButton/SvgIconButton"; +import useModal from "@/hooks/useModal"; +import { PostContext } from "@/components/main/PostScroll/Post/Post"; +import { MODAL_KEY } from "@/types/modal"; import "./RightBlockItems.scss"; const MoreButton = (): JSX.Element => { - const handleOpenMore: MouseEventHandler = () => {}; + const { id } = useContext(PostContext); + const { handleModalOpen } = useModal(); return ( - + <> + handleModalOpen(MODAL_KEY.POST_MORE, { postId: id })} + className="post__footer__right-block--btn" + /> + ); }; diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx new file mode 100644 index 00000000..8b6ffb83 --- /dev/null +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx @@ -0,0 +1,24 @@ +import React from "react"; + +import Modal from "@/components/commons/Modal/core/Modal/Modal"; +import useModal from "@/hooks/useModal"; +import { ModalProps } from "@/types/modal"; + +interface PostMoreModalProps extends ModalProps {} + +const PostMoreModal = ({ postId }: PostMoreModalProps): JSX.Element => { + const { handleModalClose } = useModal(); + + return ( + { + handleModalClose(); + }} + title={"메뉴를 선택해주세요."} + > +
{postId}
+
+ ); +}; + +export default PostMoreModal; diff --git a/client/src/hooks/useBackgroundMouseDown.ts b/client/src/hooks/useBackgroundMouseDown.ts deleted file mode 100644 index 5acf9ab8..00000000 --- a/client/src/hooks/useBackgroundMouseDown.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { RefObject, useEffect } from "react"; - -/** - * ref 외부의 요소를 클릭했을 경우 실행할 콜백 함수를 등록합니다. - */ -const useBackgroundMouseDown = ( - ref: RefObject, - callback?: (event?: Event) => void -): void => { - useEffect(() => { - const handleMouseDown = (e: Event): void => { - if (ref.current === null || ref.current.contains(e.target as Node)) { - return; - } - callback?.(e); // 모달 외부 요소 클릭 시 실행 - }; - // "click" 이벤트로 사용하려면 캡쳐링을 사용해야 함. (혹은 stopPropagation 사용 필요) - window.addEventListener("mousedown", handleMouseDown); - window.addEventListener("touchstart", handleMouseDown); - return () => { - window.removeEventListener("mousedown", handleMouseDown); - window.removeEventListener("touchstart", handleMouseDown); - }; - }, [ref, callback]); -}; - -export default useBackgroundMouseDown; diff --git a/client/src/hooks/useModal.ts b/client/src/hooks/useModal.ts new file mode 100644 index 00000000..0096a313 --- /dev/null +++ b/client/src/hooks/useModal.ts @@ -0,0 +1,26 @@ +import useModalStore from "@/store/useModalStore"; +import { MODAL_KEY, ModalProps } from "@/types/modal"; + +interface UseModalResult { + handleModalOpen: (key: MODAL_KEY, props: ModalProps) => void; + handleModalClose: () => void; +} + +const useModal = (): UseModalResult => { + const [openModal, closeModal] = useModalStore((state) => [ + state.openModal, + state.closeModal, + ]); + + const handleModalOpen = (key: MODAL_KEY, props: ModalProps): void => { + openModal(key, props); + }; + + const handleModalClose = (): void => { + closeModal(); + }; + + return { handleModalOpen, handleModalClose }; +}; + +export default useModal; diff --git a/client/src/hooks/useOutsideClickHandler.ts b/client/src/hooks/useOutsideClickHandler.ts new file mode 100644 index 00000000..5c871145 --- /dev/null +++ b/client/src/hooks/useOutsideClickHandler.ts @@ -0,0 +1,24 @@ +import { RefObject, useEffect } from "react"; + +/** + * ref 외부의 요소를 클릭했을 경우 실행할 콜백 함수를 등록합니다. + */ +const useOutsideClickHandler = ( + ref: RefObject, + callback?: (event?: Event) => void +): void => { + useEffect(() => { + const handleClickOutside = (e: Event): void => { + if (ref.current === null || ref.current.contains(e.target as Node)) { + return; + } + callback?.(e); // 모달 외부 요소 클릭 시 실행 + }; + window.addEventListener("mousedown", handleClickOutside); + return () => { + window.removeEventListener("mousedown", handleClickOutside); + }; + }, [ref, callback]); +}; + +export default useOutsideClickHandler; diff --git a/client/src/store/useModalStore.ts b/client/src/store/useModalStore.ts new file mode 100644 index 00000000..b6d704a4 --- /dev/null +++ b/client/src/store/useModalStore.ts @@ -0,0 +1,39 @@ +import create from "zustand"; +import { devtools } from "zustand/middleware"; + +import { MODAL_KEY, ModalContext, ModalProps } from "@/types/modal"; + +interface ModalState { + modals: ModalContext[]; +} + +interface ModalAction { + openModal: (key: MODAL_KEY, props: ModalProps) => void; + closeModal: () => void; +} + +const initialState: ModalState = { + modals: [], +}; + +const useModalStore = create()( + devtools( + (set) => ({ + ...initialState, + openModal: (key, props) => { + set((state) => ({ + modals: [...state.modals, { key, props }], + })); + }, + closeModal: () => + set((state) => ({ + modals: state.modals.slice(0, -1), + })), + }), + { + name: "modal-store", + } + ) +); + +export default useModalStore; diff --git a/client/src/types/modal.ts b/client/src/types/modal.ts new file mode 100644 index 00000000..2259c87f --- /dev/null +++ b/client/src/types/modal.ts @@ -0,0 +1,12 @@ +export enum MODAL_KEY { + POST_MORE, +} + +export interface ModalProps { + postId?: string; +} + +export interface ModalContext { + key: MODAL_KEY; + props: ModalProps; +} From ec37e484ceb11a53617a88796835dcfd5185189d Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 02:46:46 +0900 Subject: [PATCH 17/28] =?UTF-8?q?feat(FE):=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20=EC=BD=94=EB=93=9C=20+=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=AA=A8=EB=8B=AC=EC=9D=B4=20=EC=98=AC?= =?UTF-8?q?=EB=9D=BC=EC=98=A4=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#354)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commons/ProgressiveImage/ProgressiveImage.tsx | 12 +++++++++++- .../Post/PostImageSlider/PostImageSlider.scss | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/client/src/components/commons/ProgressiveImage/ProgressiveImage.tsx b/client/src/components/commons/ProgressiveImage/ProgressiveImage.tsx index 177faff2..12b8a9c0 100644 --- a/client/src/components/commons/ProgressiveImage/ProgressiveImage.tsx +++ b/client/src/components/commons/ProgressiveImage/ProgressiveImage.tsx @@ -1,6 +1,9 @@ -import React from "react"; +import React, { useCallback, useContext } from "react"; import useImageIntersect from "@/hooks/useImageIntersect"; +import useCommonModalStore from "@/store/useCommonModalStore"; +import ReviewModal from "@/components/main/Modal/ReviewModal/ReviewModal"; +import { PostContext } from "@/components/main/PostScroll/Post/Post"; interface ProgressiveImageProps { className: string; @@ -20,12 +23,19 @@ const ProgressiveImage = ({ alt, }: ProgressiveImageProps): JSX.Element => { const { observeImage } = useImageIntersect(); + const { id: postId, code, language } = useContext(PostContext); + const [openModal] = useCommonModalStore((state) => [state.openModal]); + + const handleOpenReviewModal = useCallback(() => { + openModal(); + }, [openModal]); return ( Date: Thu, 8 Dec 2022 02:52:37 +0900 Subject: [PATCH 18/28] =?UTF-8?q?design(FE):=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#223)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/Modal/ReviewModal/ReviewModal.scss | 25 +++++++++++++------ .../ReviewScroll/Review/Review.scss | 2 +- .../ReviewModal/ReviewScroll/ReviewScroll.tsx | 4 +-- client/src/hooks/useViewerScroll.ts | 1 + client/src/mocks/datasource/mockDataSource.ts | 2 +- client/src/styles/_mixin.scss | 3 +++ 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/client/src/components/main/Modal/ReviewModal/ReviewModal.scss b/client/src/components/main/Modal/ReviewModal/ReviewModal.scss index eeba416a..32278bd1 100644 --- a/client/src/components/main/Modal/ReviewModal/ReviewModal.scss +++ b/client/src/components/main/Modal/ReviewModal/ReviewModal.scss @@ -3,12 +3,11 @@ @import "@/styles/mixin"; $code-line-width: 4rem; -$review-code-width: calc($device-editor-width - $code-line-width); +$review-code-width: calc($device-editor-width - $code-line-width - 8rem); $github-dark-font-color: #c9d1d9; .review-modal { display: flex; - width: 100%; height: 100%; @@ -18,6 +17,7 @@ $github-dark-font-color: #c9d1d9; width: $review-code-width; min-width: $review-code-width; height: 100%; + background-color: $codeblock-color; border-radius: $radius-small; @@ -30,13 +30,24 @@ $github-dark-font-color: #c9d1d9; margin: 0; overflow-x: hidden; overflow-y: auto; - font-size: $font-medium; - white-space: pre-wrap; + + font-size: $font-large; + + &::-webkit-scrollbar { + display: none; + } } &--view { left: $code-line-width; + width: calc(100% - $code-line-width); + overflow-x: scroll; color: $github-dark-font-color; + letter-spacing: 1px; + + & > code { + font-family: D2Coding, "D2 coding", monospace; + } } &--lines { @@ -48,9 +59,9 @@ $github-dark-font-color: #c9d1d9; box-sizing: border-box; display: flex; flex-direction: column; - width: 100%; - max-width: $device-review-max-width; + width: 40rem; + max-width: 50rem; height: 100%; - margin-left: calc($device-review-modal-gap / 2); + margin-left: calc($device-review-modal-gap); } } diff --git a/client/src/components/main/Modal/ReviewModal/ReviewScroll/Review/Review.scss b/client/src/components/main/Modal/ReviewModal/ReviewScroll/Review/Review.scss index 3dd72b10..1a22d1a6 100644 --- a/client/src/components/main/Modal/ReviewModal/ReviewScroll/Review/Review.scss +++ b/client/src/components/main/Modal/ReviewModal/ReviewScroll/Review/Review.scss @@ -12,7 +12,7 @@ $review-form-header-height: 4rem; box-sizing: border-box; display: flex; - gap: 1.6rem; + gap: 1rem; width: 100%; height: auto; diff --git a/client/src/components/main/Modal/ReviewModal/ReviewScroll/ReviewScroll.tsx b/client/src/components/main/Modal/ReviewModal/ReviewScroll/ReviewScroll.tsx index 9e1e4d10..8123304a 100644 --- a/client/src/components/main/Modal/ReviewModal/ReviewScroll/ReviewScroll.tsx +++ b/client/src/components/main/Modal/ReviewModal/ReviewScroll/ReviewScroll.tsx @@ -24,7 +24,7 @@ const ReviewScroll = ({ postId }: ReviewScrollProps): JSX.Element => { ); return ( -
+ <>
    {reviewInfos.map((reviewInfo: ReviewInfo) => ( @@ -36,7 +36,7 @@ const ReviewScroll = ({ postId }: ReviewScrollProps): JSX.Element => { />
-
+ ); }; diff --git a/client/src/hooks/useViewerScroll.ts b/client/src/hooks/useViewerScroll.ts index 3e47a3f4..f4c2caf1 100644 --- a/client/src/hooks/useViewerScroll.ts +++ b/client/src/hooks/useViewerScroll.ts @@ -12,6 +12,7 @@ const useViewerScroll = (): UseEditorScroll => { const handleScrollChange = useCallback((): void => { if (lineRef.current !== null && preRef.current !== null) { lineRef.current.scrollTop = preRef.current.scrollTop; + preRef.current.scrollTop = lineRef.current.scrollTop; } }, [preRef, lineRef]); return { preRef, lineRef, handleScrollChange }; diff --git a/client/src/mocks/datasource/mockDataSource.ts b/client/src/mocks/datasource/mockDataSource.ts index be2c9e08..fb9f6c10 100644 --- a/client/src/mocks/datasource/mockDataSource.ts +++ b/client/src/mocks/datasource/mockDataSource.ts @@ -38,7 +38,7 @@ export const posts: PostInfo[] = Array.from(Array(1024).keys()).map((id) => ({ likeCount: id % 10, lineCount: 1, updatedAt: "2022-11-16 12:26:56.124939", - code: `sourcecode: ~~~~~~~~~~~~~~~~~~`, + code: `sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~sourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\nsourcecode: ~~~~~~~~~~~~~~~~~~\n`, language: `javascript`, isLiked: false, })); diff --git a/client/src/styles/_mixin.scss b/client/src/styles/_mixin.scss index e3fdb97d..3bddb140 100644 --- a/client/src/styles/_mixin.scss +++ b/client/src/styles/_mixin.scss @@ -29,6 +29,9 @@ white-space: pre-wrap; background-color: $codeblock-color; border-right: 1px solid $line-color; + &::-webkit-scrollbar{ + display: none; + } } @mixin flex-middle-start { From eab5d32891680de8c86cbb010c1cb21994ab5517 Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Thu, 8 Dec 2022 03:02:32 +0900 Subject: [PATCH 19/28] =?UTF-8?q?feat(FE):=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=EC=97=90=20=EC=82=AD=EC=A0=9C=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/post.ts | 5 ++++ .../PostMoreModal/PostMoreModal.scss | 13 ++++++++ .../PostMoreModal/PostMoreModal.tsx | 30 ++++++++++++++----- .../PostMoreModalMenu/PostMoreModalMenu.scss | 16 ++++++++++ .../PostMoreModalMenu/PostMoreModalMenu.tsx | 21 +++++++++++++ client/src/mocks/datasource/mockDataSource.ts | 7 ++++- client/src/mocks/handlers/postHandler.ts | 12 +++++++- 7 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.scss create mode 100644 client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu.scss create mode 100644 client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu.tsx diff --git a/client/src/apis/post.ts b/client/src/apis/post.ts index 8f060889..d39d5401 100644 --- a/client/src/apis/post.ts +++ b/client/src/apis/post.ts @@ -38,6 +38,11 @@ export const fetchBookmarkPost = async ( return data; }; +export const deletePost = async (postId: string): Promise => { + const { data } = await axiosInstance.delete(`/posts/${postId}`); + return data; +}; + export const uploadImage = async ({ preSignedData, imageUri, diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.scss b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.scss new file mode 100644 index 00000000..7ee05ea6 --- /dev/null +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.scss @@ -0,0 +1,13 @@ +@import "@/styles/_theme"; + +.post-more { + width: 20rem; + height: fit-content; + + &__menu { + &:last-of-type { + border-bottom-right-radius: $radius-modal; + border-bottom-left-radius: $radius-modal; + } + } +} diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx index 8b6ffb83..1ce18a9e 100644 --- a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx @@ -3,20 +3,36 @@ import React from "react"; import Modal from "@/components/commons/Modal/core/Modal/Modal"; import useModal from "@/hooks/useModal"; import { ModalProps } from "@/types/modal"; +import PostMoreModalMenu from "@/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu"; + +import "./PostMoreModal.scss"; + +import { deletePost } from "@/apis/post"; +import { queryClient } from "@/react-query/queryClient"; +import { QUERY_KEYS } from "@/react-query/queryKeys"; interface PostMoreModalProps extends ModalProps {} const PostMoreModal = ({ postId }: PostMoreModalProps): JSX.Element => { const { handleModalClose } = useModal(); - return ( - { + const handleRemovePost = (): void => { + void (async () => { + try { + await deletePost(postId as string); + await queryClient.invalidateQueries([QUERY_KEYS.POSTS]); handleModalClose(); - }} - title={"메뉴를 선택해주세요."} - > -
{postId}
+ } catch (e: any) { + alert(e.message); + } + })(); + }; + + return ( + +
+ +
); }; diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu.scss b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu.scss new file mode 100644 index 00000000..9de8e3c0 --- /dev/null +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu.scss @@ -0,0 +1,16 @@ +@import "@/styles/_theme"; + +.post-more__menu { + box-sizing: border-box; + width: 100%; + height: 5rem; + padding: 0 1rem; + font-size: $font-large; + line-height: 5rem; + text-align: center; + + &:hover { + cursor: pointer; + background-color: $light-gray; + } +} diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu.tsx b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu.tsx new file mode 100644 index 00000000..702de53b --- /dev/null +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModalMenu/PostMoreModalMenu.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +import "./PostMoreModalMenu.scss"; + +interface PostMoreModalMenuProps { + text: string; + onClick?: (e?: Event) => void; +} + +const PostMoreModalMenu = ({ + text, + onClick, +}: PostMoreModalMenuProps): JSX.Element => { + return ( +
onClick?.()}> + {text} +
+ ); +}; + +export default PostMoreModalMenu; diff --git a/client/src/mocks/datasource/mockDataSource.ts b/client/src/mocks/datasource/mockDataSource.ts index be2c9e08..5e310086 100644 --- a/client/src/mocks/datasource/mockDataSource.ts +++ b/client/src/mocks/datasource/mockDataSource.ts @@ -13,7 +13,8 @@ export const mockUser: MyInfo = { profileUrl: "http://placeimg.com/640/640/animals", }; -export const posts: PostInfo[] = Array.from(Array(1024).keys()).map((id) => ({ +export let posts: PostInfo[]; +posts = Array.from(Array(1024).keys()).map((id) => ({ id: `${id}`, title: `제목_${id}`, content: id % 2 === 0 ? LARGE_CONTENT : SMALL_CONTENT, @@ -43,6 +44,10 @@ export const posts: PostInfo[] = Array.from(Array(1024).keys()).map((id) => ({ isLiked: false, })); +export const setPosts = (newPosts: PostInfo[]): void => { + posts = newPosts; +}; + export const reviews: ReviewInfo[] = Array.from(Array(1024).keys()).map( (id) => ({ id: String(id), diff --git a/client/src/mocks/handlers/postHandler.ts b/client/src/mocks/handlers/postHandler.ts index f557193a..1d3bebca 100644 --- a/client/src/mocks/handlers/postHandler.ts +++ b/client/src/mocks/handlers/postHandler.ts @@ -2,7 +2,12 @@ import { rest } from "msw"; import { API_SERVER_URL } from "@/constants/env"; import { parsePostQueryString } from "@/mocks/utils/mockUtils"; -import { posts, history, bookmarks } from "@/mocks/datasource/mockDataSource"; +import { + posts, + history, + bookmarks, + setPosts, +} from "@/mocks/datasource/mockDataSource"; // Backend API Server URL const baseUrl = API_SERVER_URL; @@ -69,6 +74,11 @@ export const postHandler = [ ctx.json({ message: "글 작성에 성공했습니다." }) ); }), + rest.delete(`${baseUrl}/posts/:postId`, (req, res, ctx) => { + const postId = String(req.params.postId); + setPosts(posts.filter((postInfo) => postInfo.id !== postId)); + return res(ctx.status(200)); + }), rest.post(`${baseUrl}/posts/:postId/likes`, (req, res, ctx) => { const postId = req.params.postId; posts From 043879a2bcb96b1051891eab2fc7a762cab86951 Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 11:04:58 +0900 Subject: [PATCH 20/28] =?UTF-8?q?refactor(FE):=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0(#364)=20=EB=B0=98=EC=98=81=ED=95=98=EC=97=AC?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=ED=81=B4=EB=A6=AD=20props?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=EB=8B=AC=20(#354)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../commons/ProgressiveImage/ProgressiveImage.tsx | 15 ++++----------- .../Post/PostImageSlider/PostImageSlider.tsx | 10 +++++++++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/client/src/components/commons/ProgressiveImage/ProgressiveImage.tsx b/client/src/components/commons/ProgressiveImage/ProgressiveImage.tsx index 12b8a9c0..f83b8708 100644 --- a/client/src/components/commons/ProgressiveImage/ProgressiveImage.tsx +++ b/client/src/components/commons/ProgressiveImage/ProgressiveImage.tsx @@ -1,9 +1,6 @@ -import React, { useCallback, useContext } from "react"; +import React from "react"; import useImageIntersect from "@/hooks/useImageIntersect"; -import useCommonModalStore from "@/store/useCommonModalStore"; -import ReviewModal from "@/components/main/Modal/ReviewModal/ReviewModal"; -import { PostContext } from "@/components/main/PostScroll/Post/Post"; interface ProgressiveImageProps { className: string; @@ -12,6 +9,7 @@ interface ProgressiveImageProps { width: number | "100%"; height: number | "100%"; alt: string; + handleClickImage?: () => void; } const ProgressiveImage = ({ @@ -21,21 +19,16 @@ const ProgressiveImage = ({ width, height, alt, + handleClickImage, }: ProgressiveImageProps): JSX.Element => { const { observeImage } = useImageIntersect(); - const { id: postId, code, language } = useContext(PostContext); - const [openModal] = useCommonModalStore((state) => [state.openModal]); - - const handleOpenReviewModal = useCallback(() => { - openModal(); - }, [openModal]); return ( { - const { images } = useContext(PostContext); + const { images, id: postId, code, language } = useContext(PostContext); + const [openModal] = useCommonModalStore((state) => [state.openModal]); + + const handleClickImage = (): void => { + openModal(); + }; return (
@@ -18,6 +25,7 @@ const PostImageSlider = (): JSX.Element => { width="100%" height="100%" alt="이미지 설명" + handleClickImage={handleClickImage} />
); From f36e9b954b4780d9a69cd6c5a2c7194fa0f6d0a6 Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Thu, 8 Dec 2022 12:07:30 +0900 Subject: [PATCH 21/28] =?UTF-8?q?feat(FE):=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=ED=91=9C=EC=8B=9C=EB=A5=BC=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=9C=A0=EB=AC=B4,=20=EB=82=B4=20?= =?UTF-8?q?=ED=8F=AC=EC=8A=A4=ED=8A=B8=20=EC=9C=A0=EB=AC=B4=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20=ED=91=9C=EC=8B=9C,=20=EC=B7=A8=EC=86=8C?= =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80=20(#351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PostFooter/RightBlockItems/MoreButton.tsx | 9 +++++++-- .../PostMoreModal/PostMoreModal.tsx | 16 +++++++++++++--- client/src/types/modal.ts | 1 + 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/MoreButton.tsx b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/MoreButton.tsx index f91877c2..048bf14f 100644 --- a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/MoreButton.tsx +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/MoreButton.tsx @@ -9,7 +9,7 @@ import { MODAL_KEY } from "@/types/modal"; import "./RightBlockItems.scss"; const MoreButton = (): JSX.Element => { - const { id } = useContext(PostContext); + const { id, author } = useContext(PostContext); const { handleModalOpen } = useModal(); return ( @@ -17,7 +17,12 @@ const MoreButton = (): JSX.Element => { handleModalOpen(MODAL_KEY.POST_MORE, { postId: id })} + onClick={() => + handleModalOpen(MODAL_KEY.POST_MORE, { + postId: id, + authorId: author.id, + }) + } className="post__footer__right-block--btn" /> diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx index 1ce18a9e..d2dde6c2 100644 --- a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.tsx @@ -10,13 +10,20 @@ import "./PostMoreModal.scss"; import { deletePost } from "@/apis/post"; import { queryClient } from "@/react-query/queryClient"; import { QUERY_KEYS } from "@/react-query/queryKeys"; +import useAuth from "@/hooks/useAuth"; interface PostMoreModalProps extends ModalProps {} -const PostMoreModal = ({ postId }: PostMoreModalProps): JSX.Element => { +const PostMoreModal = ({ + postId, + authorId, +}: PostMoreModalProps): JSX.Element => { + const { isLoggedIn, myInfo } = useAuth(); const { handleModalClose } = useModal(); - const handleRemovePost = (): void => { + const isDeletable = isLoggedIn && myInfo?.id === authorId; + + const handleDeletePost = (): void => { void (async () => { try { await deletePost(postId as string); @@ -31,7 +38,10 @@ const PostMoreModal = ({ postId }: PostMoreModalProps): JSX.Element => { return (
- + {isDeletable && ( + + )} +
); diff --git a/client/src/types/modal.ts b/client/src/types/modal.ts index 2259c87f..4a3f7f8c 100644 --- a/client/src/types/modal.ts +++ b/client/src/types/modal.ts @@ -4,6 +4,7 @@ export enum MODAL_KEY { export interface ModalProps { postId?: string; + authorId?: string; } export interface ModalContext { From 8f1993e4847728f17473d1a90eb2b8c58924890e Mon Sep 17 00:00:00 2001 From: Seung-hyun Kim Date: Thu, 8 Dec 2022 12:08:08 +0900 Subject: [PATCH 22/28] =?UTF-8?q?design(FE):=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83,=20PostMoreModal=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95-=20?= =?UTF-8?q?=ED=81=AC=EA=B8=B0=20=ED=86=B5=EC=9D=BC,=20=ED=85=8C=EB=91=90?= =?UTF-8?q?=EB=A6=AC=20(#351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/commons/Modal/core/ModalTitle/ModalTitle.scss | 5 +++-- .../commons/Modal/core/ModalWrapper/ModalWrapper.scss | 1 + .../RightBlockItems/PostMoreModal/PostMoreModal.scss | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/components/commons/Modal/core/ModalTitle/ModalTitle.scss b/client/src/components/commons/Modal/core/ModalTitle/ModalTitle.scss index a118f5b3..27682647 100644 --- a/client/src/components/commons/Modal/core/ModalTitle/ModalTitle.scss +++ b/client/src/components/commons/Modal/core/ModalTitle/ModalTitle.scss @@ -2,9 +2,10 @@ .modal-title { width: 100%; - height: 3rem; + height: 5rem; font-size: $font-large; // div 텍스트 중앙 정렬 - line-height: 3rem; + line-height: 5rem; text-align: center; + border-bottom: $border-small $line-color; } diff --git a/client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.scss b/client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.scss index 91968939..82cb05bb 100644 --- a/client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.scss +++ b/client/src/components/commons/Modal/core/ModalWrapper/ModalWrapper.scss @@ -11,6 +11,7 @@ height: fit-content; min-height: 5rem; background-color: $weview-white; + border: $border-small $line-color; border-radius: $radius-modal; box-shadow: $shadow-box; // wrapper 의 중앙을 modal 중앙에 위치 diff --git a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.scss b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.scss index 7ee05ea6..8921a374 100644 --- a/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.scss +++ b/client/src/components/main/PostScroll/Post/PostFooter/RightBlockItems/PostMoreModal/PostMoreModal.scss @@ -1,7 +1,7 @@ @import "@/styles/_theme"; .post-more { - width: 20rem; + width: 25rem; height: fit-content; &__menu { From e4a564123dc5a83f21cd91f6b8c4f1812c9c4689 Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 17:00:29 +0900 Subject: [PATCH 23/28] =?UTF-8?q?design(FE):=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=ED=8C=A8=EB=94=A9=20=EB=A7=88=EC=A7=84=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=A1=A4=EB=B0=B1=20(#371)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/src/App.scss b/client/src/App.scss index f4c72428..bf7902a7 100644 --- a/client/src/App.scss +++ b/client/src/App.scss @@ -15,11 +15,6 @@ @import "@/styles/global-style"; @import "@/styles/theme"; -* { - padding: 0; - margin: 0; -} - html, body { height: 100%; From 096dc67c9ed87fbb572ca7bfff88fdccfadc3e4a Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 17:08:06 +0900 Subject: [PATCH 24/28] =?UTF-8?q?fix(FE):=20=EB=A6=AC=EB=B7=B0=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=EC=84=9C=20=EB=B3=B4=EC=9D=B4=EB=8A=94=20?= =?UTF-8?q?=EC=A4=84=20=EC=88=98=20=EC=88=98=EC=A0=95=20(#354)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/main/Modal/ReviewModal/ReviewModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/main/Modal/ReviewModal/ReviewModal.tsx b/client/src/components/main/Modal/ReviewModal/ReviewModal.tsx index d36b2de5..26e09224 100644 --- a/client/src/components/main/Modal/ReviewModal/ReviewModal.tsx +++ b/client/src/components/main/Modal/ReviewModal/ReviewModal.tsx @@ -24,7 +24,7 @@ const ReviewModal = ({
- {Array.from(Array(getLineCount(code) + 1).keys()) + {Array.from(Array(getLineCount(code)).keys()) .slice(1) .join("\n")}
From 797c25f7e43dea3dc8ab5fdd2523a186557a64d5 Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 21:30:15 +0900 Subject: [PATCH 25/28] =?UTF-8?q?feat(FE):=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=97=90=EB=94=94=ED=84=B0=20=ED=83=AD=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/main/CodeEditor/CodeEditor.tsx | 5 +-- client/src/hooks/useCodeEditor.ts | 31 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/client/src/components/main/CodeEditor/CodeEditor.tsx b/client/src/components/main/CodeEditor/CodeEditor.tsx index eacd5690..3ff5783c 100644 --- a/client/src/components/main/CodeEditor/CodeEditor.tsx +++ b/client/src/components/main/CodeEditor/CodeEditor.tsx @@ -7,9 +7,9 @@ import CodeViewer from "@/components/main/CodeViewer/CodeViewer"; import "./CodeEditor.scss"; const CodeEditor = (): JSX.Element => { - const { code, handleCodeChange, language, lineCount } = useCodeEditor(); const { lineRef, textRef, preRef, handleScrollChange } = useEditorScroll(); - + const { code, language, lineCount, handleCodeChange, handleKeyDown } = + useCodeEditor(textRef); useEffect(() => { textRef.current?.focus(); }, []); @@ -25,6 +25,7 @@ const CodeEditor = (): JSX.Element => { ref={textRef} onScroll={handleScrollChange} onChange={handleCodeChange} + onKeyDown={handleKeyDown} value={code} className="code__textarea" autoComplete="false" diff --git a/client/src/hooks/useCodeEditor.ts b/client/src/hooks/useCodeEditor.ts index cf4dec92..51401e58 100644 --- a/client/src/hooks/useCodeEditor.ts +++ b/client/src/hooks/useCodeEditor.ts @@ -1,4 +1,11 @@ -import { ChangeEvent, useCallback, useEffect, useState } from "react"; +import { + ChangeEvent, + KeyboardEvent, + useCallback, + useEffect, + useState, + RefObject, +} from "react"; import domtoimage from "dom-to-image"; import { ONE_SNAPSHOT_LINE_COUNT } from "@/constants/code"; @@ -10,10 +17,13 @@ interface UseCodeEditor { code: string; language: string; handleCodeChange: (e: ChangeEvent) => void; + handleKeyDown: (e: KeyboardEvent) => void; lineCount: number; } -const useCodeEditor = (): UseCodeEditor => { +const useCodeEditor = ( + textAreaRef: RefObject +): UseCodeEditor => { const { code, setCode, language, images, setImages, removeImage } = useCodeEditorStore((state) => ({ code: state.code, @@ -25,6 +35,22 @@ const useCodeEditor = (): UseCodeEditor => { })); const [lineCount, setLineCount] = useState(0); + const handleKeyDown = (e: KeyboardEvent): void => { + if (e.key === "Tab") { + if (textAreaRef.current !== null) { + e.preventDefault(); + const start = textAreaRef.current.selectionStart; + const end = textAreaRef.current.selectionEnd; + const value = code.substring(0, start) + " " + code.substring(end); + textAreaRef.current.value = value; + textAreaRef.current.selectionStart = textAreaRef.current.selectionEnd = + end + 2 - (end - start); + /* 커서가 뒤로 먼저 가지 않기 위해 value 먼저 변경하고 setCode 실행합니다. */ + setCode(value); + } + } + }; + const handleCodeChange = useCallback( (e: ChangeEvent) => { setCode(e.target.value); @@ -86,6 +112,7 @@ const useCodeEditor = (): UseCodeEditor => { handleCodeChange, language, lineCount, + handleKeyDown, }; }; From 7e85be64babf63fc3c06e073543ae38180fb9c07 Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 22:31:04 +0900 Subject: [PATCH 26/28] =?UTF-8?q?fix(FE):=20=ED=83=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=8B=9C=20navigate=20=EC=9D=B4=ED=9B=84=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EB=90=98=EA=B2=8C=EB=81=94=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20(#349)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/TagRanking/TagRankingItem/TagRankingItem.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/components/main/TagRanking/TagRankingItem/TagRankingItem.tsx b/client/src/components/main/TagRanking/TagRankingItem/TagRankingItem.tsx index 2ad474bc..6f278835 100644 --- a/client/src/components/main/TagRanking/TagRankingItem/TagRankingItem.tsx +++ b/client/src/components/main/TagRanking/TagRankingItem/TagRankingItem.tsx @@ -1,4 +1,5 @@ import React, { useCallback } from "react"; +import { useNavigate } from "react-router-dom"; import HorizontalRuleIcon from "@mui/icons-material/HorizontalRule"; import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward"; import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward"; @@ -19,8 +20,10 @@ const TagRankingItem = ({ tagInfo }: PopularTagBoxProps): JSX.Element => { const [searchDefaultFilter] = useSearchStore((state) => [ state.searchDefaultFilter, ]); + const navigate = useNavigate(); const handleItemClick = useCallback((): void => { searchDefaultFilter({ tags: [tagInfo.name], lastId: "-1" }); + navigate("/"); }, []); return ( From 02b132d220841d4aeb43ce9a66f799f04d50e623 Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 23:31:19 +0900 Subject: [PATCH 27/28] =?UTF-8?q?fix(FE):=20=EC=A4=84=20=EC=88=98=20?= =?UTF-8?q?=EA=B0=99=EC=9D=B4=20=EC=9B=80=EC=A7=81=EC=9D=B4=EB=8A=94=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20(#350)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/styles/_mixin.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/styles/_mixin.scss b/client/src/styles/_mixin.scss index 3bddb140..c282fa56 100644 --- a/client/src/styles/_mixin.scss +++ b/client/src/styles/_mixin.scss @@ -22,7 +22,7 @@ width: $line-width; height: 100%; overflow-x: hidden; - overflow-y: auto; + overflow-y: hidden; font-family: D2Coding, "D2 coding", monospace; color: $weview-white; text-align: end; From 242c0163f0462dc0e3b67098c7e83b79c3f67ddf Mon Sep 17 00:00:00 2001 From: Jiwon Kim Date: Thu, 8 Dec 2022 23:32:00 +0900 Subject: [PATCH 28/28] =?UTF-8?q?fix(FE):=2015=EB=B0=B0=EC=88=98=20?= =?UTF-8?q?=EC=A4=84=EC=97=90=20=EB=B9=88=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=EC=9D=BC=20=EB=95=8C=20=EC=A4=84=20=EB=B0=80=EB=A6=AC=EB=8A=94?= =?UTF-8?q?=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95=20(#350)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/utils/code.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/src/utils/code.ts b/client/src/utils/code.ts index 7fb6e70b..bef76e1c 100644 --- a/client/src/utils/code.ts +++ b/client/src/utils/code.ts @@ -21,8 +21,10 @@ export const chunkHTML = (htmlData: string[]): string[][] => { export const wrapHTML = (chunkedHTML: string[][]): string => chunkedHTML - .map( - (arr, idx) => - `
${arr.join("\n")}
` - ) + .map((arr, idx) => { + return `
${arr + // 배열의 마지막 원소가 빈 문자열일 때 join하면 개행이 사라지므로 이를 확인하여 추가 + .map((item, idx2) => (item === "" && idx2 === 14 ? "
" : item)) + .join("\n")}
`; + }) .join("");