From 091234d1caf56017315e5c2ca4810a86866afb58 Mon Sep 17 00:00:00 2001 From: parkhyeonki Date: Thu, 8 Dec 2022 22:31:49 +0900 Subject: [PATCH 01/27] =?UTF-8?q?chore:=20=EC=B6=A9=EB=8F=8C=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/apis/books/books.service.ts | 2 + backend/src/apis/index.ts | 1 + backend/src/apis/scraps/scraps.controller.ts | 10 +++++ frontend/apis/scrapApi.ts | 8 ++++ .../viewer/ArticleContent/index.tsx | 39 +++++++++++-------- frontend/pages/viewer/[...data].tsx | 31 +++++++++++---- 6 files changed, 68 insertions(+), 23 deletions(-) diff --git a/backend/src/apis/books/books.service.ts b/backend/src/apis/books/books.service.ts index 3c27c33a..1549b9f5 100644 --- a/backend/src/apis/books/books.service.ts +++ b/backend/src/apis/books/books.service.ts @@ -17,7 +17,9 @@ const findBook = async (bookId: number, userId: number) => { scraps: { orderBy: { order: 'asc' }, select: { + id: true, order: true, + is_original: true, article: { select: { id: true, diff --git a/backend/src/apis/index.ts b/backend/src/apis/index.ts index 1f31e1e7..6ff246cf 100644 --- a/backend/src/apis/index.ts +++ b/backend/src/apis/index.ts @@ -52,6 +52,7 @@ router.delete('/bookmarks/:bookmarkId', catchAsync(bookmarksController.deleteBoo router.post('/scraps', catchAsync(scrapsController.createScrap)); router.delete('/scraps/:scrapId', catchAsync(guard), catchAsync(scrapsController.deleteScrap)); router.get('/scraps', catchAsync(scrapsController.getScraps)); +router.patch('/scraps', catchAsync(guard), catchAsync(scrapsController.updateScrapsOrder)); router.get('/users', catchAsync(usersController.getUserProfile)); router.patch('/users/:userId', catchAsync(usersController.editUserProfile)); diff --git a/backend/src/apis/scraps/scraps.controller.ts b/backend/src/apis/scraps/scraps.controller.ts index 12091023..714a1a57 100644 --- a/backend/src/apis/scraps/scraps.controller.ts +++ b/backend/src/apis/scraps/scraps.controller.ts @@ -35,8 +35,18 @@ const getScraps = async (req: Request, res: Response) => { res.status(200).send(scraps); }; +const updateScrapsOrder = async (req: Request, res: Response) => { + const scraps = req.body; + scraps.forEach(async (scrap: IScrap) => { + await scrapsService.updateScrapOrder(scrap); + }); + + res.status(200).send(); +}; + export default { createScrap, deleteScrap, getScraps, + updateScrapsOrder, }; diff --git a/frontend/apis/scrapApi.ts b/frontend/apis/scrapApi.ts index 2ac50ba2..7396b622 100644 --- a/frontend/apis/scrapApi.ts +++ b/frontend/apis/scrapApi.ts @@ -1,3 +1,4 @@ +import { IScrap } from '@interfaces'; import api from '@utils/api'; export const getScrapsApi = async () => { @@ -30,3 +31,10 @@ export const deleteScrapApi = async (scrapId: string) => { return response.data; }; +export const updateScrapsOrderApi = async (data: IScrap[]) => { + const url = `/api/scraps`; + console.log(data); + const response = await api({ url, method: 'PATCH', data }); + + return response.data; +}; diff --git a/frontend/components/viewer/ArticleContent/index.tsx b/frontend/components/viewer/ArticleContent/index.tsx index 9883e081..db0c0f03 100644 --- a/frontend/components/viewer/ArticleContent/index.tsx +++ b/frontend/components/viewer/ArticleContent/index.tsx @@ -6,12 +6,15 @@ import { useEffect } from 'react'; import axios from 'axios'; import { useRecoilValue } from 'recoil'; +import { deleteArticleApi } from '@apis/articleApi'; +import { deleteScrapApi, updateScrapsOrderApi } from '@apis/scrapApi'; import LeftBtnIcon from '@assets/ico_leftBtn.svg'; import Original from '@assets/ico_original.svg'; import RightBtnIcon from '@assets/ico_rightBtn.svg'; import Scrap from '@assets/ico_scrap.svg'; import signInStatusState from '@atoms/signInStatus'; import Content from '@components/common/Content'; +import useFetch from '@hooks/useFetch'; import { IArticleBook, IScrap } from '@interfaces'; import { TextLarge } from '@styles/common'; @@ -42,6 +45,10 @@ export default function Article({ }: ArticleProps) { const user = useRecoilValue(signInStatusState); + const { data: deleteArticleData, execute: deleteArticle } = useFetch(deleteArticleApi); + const { data: deleteScrapData, execute: deleteScrap } = useFetch(deleteScrapApi); + const { execute: updateScrapsOrder } = useFetch(updateScrapsOrderApi); + const router = useRouter(); const handleOriginalBtnOnClick = () => { @@ -62,20 +69,23 @@ export default function Article({ const handleDeleteBtnOnClick = () => { if (window.confirm('해당 글을 삭제하시겠습니까?')) { - axios - .delete(`${process.env.NEXT_PUBLIC_SERVER_URL}/api/articles/${article.id}`) - .catch((err) => { - // 추후 에러 핸들링 추가 예정 - console.log(err); - }); - - router.push('/'); + deleteArticle(article.id); } }; const handleScrapDeleteBtnOnClick = () => { if (window.confirm('해당 글을 책에서 삭제하시겠습니까?')) { - // + const curScrap = scraps.find((scrap) => scrap.article.id === article.id); + if (!curScrap) return; + const nextOrder = curScrap.order + 1; + const nextArticleId = scraps.filter((scrap) => scrap.order === nextOrder)[0].article.id; + + const newScraps = scraps + .filter((scrap) => scrap.id !== curScrap.id) + .map((v, i) => ({ ...v, order: i + 1 })); + updateScrapsOrder(newScraps); + deleteScrap(curScrap?.id); + router.push(`/viewer/${bookId}/${nextArticleId}`); } }; @@ -87,16 +97,13 @@ export default function Article({ if (scraps.find((scrap) => scrap.article.id === id)) { return true; } - // alert 두번뜨는 현상... - // 404 페이지로 처리? 고민 중 - // alert('잘못된 접근입니다.'); router.push('/'); return false; }; useEffect(() => { - checkArticleAuthority(article.id); - }, []); + if (deleteArticleData !== undefined) router.push('/'); + }, [deleteArticleData]); return ( @@ -122,9 +129,9 @@ export default function Article({ 글 수정 )} - {article.book_id !== bookId && bookAuthor === user.nickname && ( + {/* {article.book_id !== bookId && bookAuthor === user.nickname && ( 스크랩 삭제 - )} + )} */} {user.id !== 0 && ( Scrap Icon diff --git a/frontend/pages/viewer/[...data].tsx b/frontend/pages/viewer/[...data].tsx index 1f11e67d..4035db23 100644 --- a/frontend/pages/viewer/[...data].tsx +++ b/frontend/pages/viewer/[...data].tsx @@ -1,4 +1,5 @@ import { GetServerSideProps } from 'next'; +import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; @@ -34,6 +35,7 @@ export default function Viewer({ book, article }: ViewerProps) { const handleModalOpen = () => setModalShown(true); const handleModalClose = () => setModalShown(false); + const router = useRouter(); const handleSideBarToggle = () => { setIsOpened((prev) => !prev); @@ -43,6 +45,17 @@ export default function Viewer({ book, article }: ViewerProps) { getUserKnottedBooks(user.nickname); }, [user.nickname]); + const checkArticleAuthority = (id: number) => { + if (book.scraps.find((scrap) => scrap.article.id === id)) { + return true; + } + return false; + }; + + useEffect(() => { + if (!checkArticleAuthority(article.id)) router.push('/404'); + }); + return ( <> @@ -54,13 +67,17 @@ export default function Viewer({ book, article }: ViewerProps) { ) : ( )} - + {book.scraps.find((scrap) => scrap.article.id === article.id) ? ( + + ) : ( +
올바르지 않은 접근입니다.
+ )} ) : (
loading
From acaf4f2298856e7063a4ed6c28070d547d8613a9 Mon Sep 17 00:00:00 2001 From: parkhyeonki Date: Thu, 8 Dec 2022 23:17:09 +0900 Subject: [PATCH 02/27] =?UTF-8?q?feat:=20=EB=B7=B0=EC=96=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=8A=A4=ED=81=AC=EB=9E=A9=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B5=AC=ED=98=84=20-=20#246?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/apis/books/books.service.ts | 2 ++ backend/src/apis/scraps/scraps.controller.ts | 3 +- .../viewer/ArticleContent/index.tsx | 34 +++++++++---------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/backend/src/apis/books/books.service.ts b/backend/src/apis/books/books.service.ts index 59be48f7..15965734 100644 --- a/backend/src/apis/books/books.service.ts +++ b/backend/src/apis/books/books.service.ts @@ -77,7 +77,9 @@ const getBook = async (bookId: number, userId: number) => { scraps: { orderBy: { order: 'asc' }, select: { + id: true, order: true, + is_original: true, article: { select: { id: true, diff --git a/backend/src/apis/scraps/scraps.controller.ts b/backend/src/apis/scraps/scraps.controller.ts index 8b9aae22..0a03e39a 100644 --- a/backend/src/apis/scraps/scraps.controller.ts +++ b/backend/src/apis/scraps/scraps.controller.ts @@ -38,11 +38,12 @@ const getScraps = async (req: Request, res: Response) => { const updateScrapsOrder = async (req: Request, res: Response) => { const scraps = req.body; + scraps.forEach(async (scrap: IScrap) => { await scrapsService.updateScrapOrder(scrap); }); - res.status(200).send(); + res.status(200).send(scraps); }; export default { diff --git a/frontend/components/viewer/ArticleContent/index.tsx b/frontend/components/viewer/ArticleContent/index.tsx index d0de5f4f..fa5d48af 100644 --- a/frontend/components/viewer/ArticleContent/index.tsx +++ b/frontend/components/viewer/ArticleContent/index.tsx @@ -1,7 +1,7 @@ import Image from 'next/image'; import { useRouter } from 'next/router'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useRecoilValue } from 'recoil'; @@ -47,7 +47,7 @@ export default function Article({ const { data: deleteArticleData, execute: deleteArticle } = useFetch(deleteArticleApi); const { data: deleteScrapData, execute: deleteScrap } = useFetch(deleteScrapApi); - const { execute: updateScrapsOrder } = useFetch(updateScrapsOrderApi); + const { data: updateScrapsData, execute: updateScrapsOrder } = useFetch(updateScrapsOrderApi); const router = useRouter(); @@ -69,6 +69,8 @@ export default function Article({ const handleDeleteBtnOnClick = () => { if (window.confirm('해당 글을 삭제하시겠습니까?')) { + const curScrap = scraps.find((scrap) => scrap.article.id === article.id); + deleteScrap(curScrap?.id); deleteArticle(article.id); } }; @@ -77,15 +79,11 @@ export default function Article({ if (window.confirm('해당 글을 책에서 삭제하시겠습니까?')) { const curScrap = scraps.find((scrap) => scrap.article.id === article.id); if (!curScrap) return; - const nextOrder = curScrap.order + 1; - const nextArticleId = scraps.filter((scrap) => scrap.order === nextOrder)[0].article.id; - const newScraps = scraps .filter((scrap) => scrap.id !== curScrap.id) .map((v, i) => ({ ...v, order: i + 1 })); updateScrapsOrder(newScraps); - deleteScrap(curScrap?.id); - router.push(`/viewer/${bookId}/${nextArticleId}`); + deleteScrap(curScrap.id); } }; @@ -93,18 +91,20 @@ export default function Article({ router.push(`/editor?id=${article.id}`); }; - const checkArticleAuthority = (id: number) => { - if (scraps.find((scrap) => scrap.article.id === id)) { - return true; - } - router.push('/'); - return false; - }; - useEffect(() => { if (deleteArticleData !== undefined) router.push('/'); }, [deleteArticleData]); + useEffect(() => { + if (updateScrapsData === undefined) return; + + if (updateScrapsData.length !== 0) { + router.push(`/viewer/${bookId}/${updateScrapsData[0].article.id}`); + return; + } + router.push('/'); + }, [updateScrapsData]); + return ( {article.id === scraps.at(0)?.article.id ? null : ( @@ -134,9 +134,9 @@ export default function Article({ 글 수정 )} - {/* {article.book_id !== bookId && bookAuthor === user.nickname && ( + {article.book_id !== bookId && bookAuthor === user.nickname && ( 스크랩 삭제 - )} */} + )} {user.id !== 0 && ( Scrap Icon From e8fa8de2c12452a9259e34a99d34f74a9200bfbf Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Thu, 8 Dec 2022 23:33:12 +0900 Subject: [PATCH 03/27] =?UTF-8?q?fix:=20=EB=B7=B0=EC=96=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=B6=81=EB=A7=88=ED=81=AC=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B0=98=EC=98=81=20=EC=95=88=20=EB=90=98=EB=8A=94?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/pages/viewer/[...data].tsx | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/frontend/pages/viewer/[...data].tsx b/frontend/pages/viewer/[...data].tsx index 1d62a19e..14bb3663 100644 --- a/frontend/pages/viewer/[...data].tsx +++ b/frontend/pages/viewer/[...data].tsx @@ -20,12 +20,12 @@ import { IArticleBook, IBookScraps } from '@interfaces'; import { Flex } from '@styles/layout'; interface ViewerProps { - book: IBookScraps; article: IArticleBook; } -export default function Viewer({ book, article }: ViewerProps) { +export default function Viewer({ article }: ViewerProps) { const { data: userBooks, execute: getUserKnottedBooks } = useFetch(getUserKnottedBooksApi); + const { data: book, execute: getBook } = useFetch(getBookApi); const user = useRecoilValue(signInStatusState); const router = useRouter(); @@ -45,23 +45,32 @@ export default function Viewer({ book, article }: ViewerProps) { getUserKnottedBooks(user.nickname); }, [user.nickname]); - const checkArticleAuthority = (id: number) => { - if (book.scraps.find((scrap) => scrap.article.id === id)) { + const checkArticleAuthority = (targetBook: IBookScraps, id: number) => { + if (targetBook.scraps.find((scrap) => scrap.article.id === id)) { return true; } return false; }; useEffect(() => { - if (!checkArticleAuthority(article.id)) router.push('/404'); - }); + if (Array.isArray(router.query.data) && router.query.data?.length === 2) { + const bookId = router.query.data[0]; + getBook(bookId); + } + }, [router.query.data]); + + useEffect(() => { + if (!book) return; + if (!checkArticleAuthority(book, article.id)) router.push('/404'); + }, [book]); + useEffect(() => { if (window.innerWidth > 576) setIsOpened(true); }, []); return ( <> - + {article && } {book && article ? ( @@ -96,8 +105,7 @@ export default function Viewer({ book, article }: ViewerProps) { export const getServerSideProps: GetServerSideProps = async (context) => { const [bookId, articleId] = context.query.data as string[]; - const book = await getBookApi(bookId); const article = await getArticleApi(articleId); - return { props: { book, article } }; + return { props: { article } }; }; From fb07f351c61fa6b3b0af3d2eedc9cd99e4fea255 Mon Sep 17 00:00:00 2001 From: parkhyeonki Date: Thu, 8 Dec 2022 23:52:33 +0900 Subject: [PATCH 04/27] =?UTF-8?q?chore:=20lint=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/apis/scrapApi.ts | 1 - frontend/components/common/Book/index.tsx | 5 +--- .../components/study/BookListTab/index.tsx | 2 +- .../viewer/ArticleContent/index.tsx | 4 +-- frontend/pages/booktest.tsx | 26 ------------------- 5 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 frontend/pages/booktest.tsx diff --git a/frontend/apis/scrapApi.ts b/frontend/apis/scrapApi.ts index 7396b622..47ece7ff 100644 --- a/frontend/apis/scrapApi.ts +++ b/frontend/apis/scrapApi.ts @@ -33,7 +33,6 @@ export const deleteScrapApi = async (scrapId: string) => { }; export const updateScrapsOrderApi = async (data: IScrap[]) => { const url = `/api/scraps`; - console.log(data); const response = await api({ url, method: 'PATCH', data }); return response.data; diff --git a/frontend/components/common/Book/index.tsx b/frontend/components/common/Book/index.tsx index 9e4fc0fb..7cd28b13 100644 --- a/frontend/components/common/Book/index.tsx +++ b/frontend/components/common/Book/index.tsx @@ -1,13 +1,10 @@ -import Image from 'next/image'; - import InactiveBookmarkIcon from '@assets/ico_bookmark_black.svg'; import ActiveBookmarkIcon from '@assets/ico_bookmark_grey_filled.svg'; -import MoreContentsIcon from '@assets/ico_more_contents.svg'; import sampleImage from '@assets/img_sample_thumbnail.jpg'; import useBookmark from '@hooks/useBookmark'; import { IBookScraps } from '@interfaces'; import { TextLarge, TextXSmall, TextSmall } from '@styles/common'; -import { FlexCenter, FlexSpaceBetween } from '@styles/layout'; +import { FlexSpaceBetween } from '@styles/layout'; import { BookWrapper, diff --git a/frontend/components/study/BookListTab/index.tsx b/frontend/components/study/BookListTab/index.tsx index f659d3a6..98187a7d 100644 --- a/frontend/components/study/BookListTab/index.tsx +++ b/frontend/components/study/BookListTab/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useState } from 'react'; import { useRecoilState } from 'recoil'; diff --git a/frontend/components/viewer/ArticleContent/index.tsx b/frontend/components/viewer/ArticleContent/index.tsx index fa5d48af..67c2bf61 100644 --- a/frontend/components/viewer/ArticleContent/index.tsx +++ b/frontend/components/viewer/ArticleContent/index.tsx @@ -1,7 +1,7 @@ import Image from 'next/image'; import { useRouter } from 'next/router'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useRecoilValue } from 'recoil'; @@ -46,7 +46,7 @@ export default function Article({ const user = useRecoilValue(signInStatusState); const { data: deleteArticleData, execute: deleteArticle } = useFetch(deleteArticleApi); - const { data: deleteScrapData, execute: deleteScrap } = useFetch(deleteScrapApi); + const { execute: deleteScrap } = useFetch(deleteScrapApi); const { data: updateScrapsData, execute: updateScrapsOrder } = useFetch(updateScrapsOrderApi); const router = useRouter(); diff --git a/frontend/pages/booktest.tsx b/frontend/pages/booktest.tsx deleted file mode 100644 index 1f7a26b6..00000000 --- a/frontend/pages/booktest.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useEffect } from 'react'; - -import { getOrderedBookListApi } from '@apis/bookApi'; -import Book from '@components/common/Book'; -import useFetch from '@hooks/useFetch'; - -export default function Booktest() { - const { - data: popularBookList, - isLoading: isPopularBookListLoading, - execute: getPopularBookList, - } = useFetch(getOrderedBookListApi); - - useEffect(() => { - getPopularBookList('bookmark'); - }, []); - return ( -
- {popularBookList && ( - <> - - - )} -
- ); -} From 1479c629b912641c2f31c957394e3d1813262067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Fri, 9 Dec 2022 00:22:19 +0900 Subject: [PATCH 05/27] =?UTF-8?q?fix:=20Content=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=EC=9D=B4=20=EC=A4=84=EB=B0=94=EA=BF=88=20?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components/common/Content/styled.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/components/common/Content/styled.ts b/frontend/components/common/Content/styled.ts index af2efe59..1fd5f4cf 100644 --- a/frontend/components/common/Content/styled.ts +++ b/frontend/components/common/Content/styled.ts @@ -68,8 +68,6 @@ export const ContentBody = styled.div` blockquote { margin: 24px 0; padding: 24px 16px; - /* background-color: var(--light-orange-color); */ - /* border-radius: 4px; */ border-left: 8px solid var(--light-orange-color); } @@ -90,6 +88,7 @@ export const ContentBody = styled.div` code { padding: 0; + white-space: pre-wrap; } } `; From 577683f9675c1840b52d7a4017e9cdb4894c464b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=84?= Date: Fri, 9 Dec 2022 01:43:38 +0900 Subject: [PATCH 06/27] =?UTF-8?q?feat:=20Editor=20=EB=B0=8F=20Content=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20Syntax=20Highlighting=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20-=20#243?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components/common/Content/index.tsx | 2 + frontend/package-lock.json | 100 +++++++++++++++++++ frontend/package.json | 2 + frontend/utils/parser.ts | 10 +- 4 files changed, 113 insertions(+), 1 deletion(-) diff --git a/frontend/components/common/Content/index.tsx b/frontend/components/common/Content/index.tsx index 3fb1f599..c62932a2 100644 --- a/frontend/components/common/Content/index.tsx +++ b/frontend/components/common/Content/index.tsx @@ -1,5 +1,7 @@ import { ContentBody, ContentTitle, ContentWrapper } from './styled'; +import 'highlight.js/styles/github.css'; + interface ContentProps { title?: string; content: string; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 58dad363..92adead7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ "dotenv-webpack": "^8.0.1", "eslint": "8.27.0", "eslint-config-next": "13.0.3", + "highlight.js": "^11.7.0", "immutability-helper": "^3.1.1", "next": "13.0.3", "next-sitemap": "^3.1.32", @@ -33,6 +34,7 @@ "react-dom": "18.2.0", "react-toastify": "^9.1.1", "recoil": "^0.7.6", + "rehype-highlight": "^6.0.0", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", "rehype-stringify": "^9.0.3", @@ -3036,6 +3038,18 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3122,6 +3136,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3533,6 +3555,14 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/highlight.js": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz", + "integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -4051,6 +4081,20 @@ "loose-envify": "cli.js" } }, + "node_modules/lowlight": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-2.8.0.tgz", + "integrity": "sha512-WeExw1IKEkel9ZcYwzpvcFzORIB0IlleTcxJYoEuUgHASuYe/OBYbV6ym/AetG7unNVCBU/SXpgTgs2nT93mhg==", + "dependencies": { + "@types/hast": "^2.0.0", + "fault": "^2.0.0", + "highlight.js": "~11.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -5273,6 +5317,22 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/rehype-highlight": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-6.0.0.tgz", + "integrity": "sha512-q7UtlFicLhetp7K48ZgZiJgchYscMma7XjzX7t23bqEJF8m6/s+viXQEe4oHjrATTIZpX7RG8CKD7BlNZoh9gw==", + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-to-text": "^3.0.0", + "lowlight": "^2.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/rehype-minify-whitespace": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-5.0.1.tgz", @@ -8697,6 +8757,14 @@ "reusify": "^1.0.4" } }, + "fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "requires": { + "format": "^0.2.0" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -8751,6 +8819,11 @@ "mime-types": "^2.1.12" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -9048,6 +9121,11 @@ "space-separated-tokens": "^2.0.0" } }, + "highlight.js": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz", + "integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==" + }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -9396,6 +9474,16 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lowlight": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-2.8.0.tgz", + "integrity": "sha512-WeExw1IKEkel9ZcYwzpvcFzORIB0IlleTcxJYoEuUgHASuYe/OBYbV6ym/AetG7unNVCBU/SXpgTgs2nT93mhg==", + "requires": { + "@types/hast": "^2.0.0", + "fault": "^2.0.0", + "highlight.js": "~11.7.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -10138,6 +10226,18 @@ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" }, + "rehype-highlight": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-6.0.0.tgz", + "integrity": "sha512-q7UtlFicLhetp7K48ZgZiJgchYscMma7XjzX7t23bqEJF8m6/s+viXQEe4oHjrATTIZpX7RG8CKD7BlNZoh9gw==", + "requires": { + "@types/hast": "^2.0.0", + "hast-util-to-text": "^3.0.0", + "lowlight": "^2.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + } + }, "rehype-minify-whitespace": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-5.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index e3450499..0b7c13c2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "dotenv-webpack": "^8.0.1", "eslint": "8.27.0", "eslint-config-next": "13.0.3", + "highlight.js": "^11.7.0", "immutability-helper": "^3.1.1", "next": "13.0.3", "next-sitemap": "^3.1.32", @@ -34,6 +35,7 @@ "react-dom": "18.2.0", "react-toastify": "^9.1.1", "recoil": "^0.7.6", + "rehype-highlight": "^6.0.0", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", "rehype-stringify": "^9.0.3", diff --git a/frontend/utils/parser.ts b/frontend/utils/parser.ts index 10f4790e..93de0109 100644 --- a/frontend/utils/parser.ts +++ b/frontend/utils/parser.ts @@ -1,3 +1,4 @@ +import rehypeHighlight from 'rehype-highlight'; import rehypeParse from 'rehype-parse'; import rehypeRemark from 'rehype-remark'; import rehypeStringify from 'rehype-stringify'; @@ -7,12 +8,19 @@ import remarkStringify from 'remark-stringify'; import { unified } from 'unified'; export const markdown2html = (markdown: string) => { - return unified() + const html = unified() .use(remarkParse) .use(remarkRehype) .use(rehypeStringify) .processSync(markdown) .toString(); + + return unified() + .use(rehypeParse) + .use(rehypeHighlight, { ignoreMissing: true }) + .use(rehypeStringify) + .processSync(html) + .toString(); }; export const html2markdown = (html: string) => { From ddb2c94eb863f0d512e22404cb546bbcfcb2544c Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Fri, 9 Dec 2022 17:20:47 +0900 Subject: [PATCH 07/27] =?UTF-8?q?fix:=20=EC=B1=85=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EA=B0=80=20=EC=B4=88=EA=B8=B0=ED=99=94?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=20-=20#267?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/pages/search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/search.tsx b/frontend/pages/search.tsx index 6ed1eb17..2d58e49b 100644 --- a/frontend/pages/search.tsx +++ b/frontend/pages/search.tsx @@ -173,7 +173,7 @@ export default function Search() { }); if (bookPage.pageNumber === 2) setBooks(newBooksHighlighted); - setBooks(books.concat(newBooksHighlighted)); + else setBooks(books.concat(newBooksHighlighted)); setBookPage({ ...bookPage, From cf24c403d99be506e82a541f30419577ea6235d8 Mon Sep 17 00:00:00 2001 From: dahyeon405 Date: Fri, 9 Dec 2022 17:41:07 +0900 Subject: [PATCH 08/27] =?UTF-8?q?chore:=20Booklink=20props=20=EB=AA=85?= =?UTF-8?q?=EC=B9=AD=20=EC=86=8C=EB=AC=B8=EC=9E=90=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components/common/Book/index.tsx | 4 ++-- frontend/components/common/Book/styled.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/components/common/Book/index.tsx b/frontend/components/common/Book/index.tsx index 9e4fc0fb..9d3b946d 100644 --- a/frontend/components/common/Book/index.tsx +++ b/frontend/components/common/Book/index.tsx @@ -38,7 +38,7 @@ export default function Book({ book }: BookProps) { // 수정모드일때만 아래 onclick이 실행되도록 수정해야함 -> 민형님 작업 후 {title} diff --git a/frontend/components/common/Book/styled.ts b/frontend/components/common/Book/styled.ts index 9e564be3..e82fa156 100644 --- a/frontend/components/common/Book/styled.ts +++ b/frontend/components/common/Book/styled.ts @@ -112,7 +112,7 @@ export const AuthorLink = styled(Link)` margin-top: 2px; `; -export const BookLink = styled(Link)<{ isArticleExists: boolean }>` +export const BookLink = styled(Link)<{ isarticleexists: string }>` text-decoration: none; - ${(props) => (props.isArticleExists ? '' : 'pointer-events: none;')} + ${(props) => (props.isarticleexists === 'true' ? '' : 'pointer-events: none;')} `; From b85966f13911c0f5ec719f1c947c569e3747179d Mon Sep 17 00:00:00 2001 From: MinHK4 Date: Fri, 9 Dec 2022 18:06:40 +0900 Subject: [PATCH 09/27] =?UTF-8?q?fix:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=EC=88=9C,=20=EC=B5=9C=EC=8B=A0=EC=88=9C=20=EC=88=9C=EC=84=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20-=20#265?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/pages/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index efc4d888..2665adab 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -62,18 +62,18 @@ export default function Home() { {numberPerPage !== 0 && ( <> - +