From 342c7db2188c560d39d9a4c27c82d694d5232f36 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Mon, 27 Feb 2023 12:12:07 +0900 Subject: [PATCH 01/16] =?UTF-8?q?install:=20=ED=95=84=EC=9A=94=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=9D=B8=EC=8A=A4=ED=86=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 패키지 인스톨 - next.config.js 적용 editor 변경 #224 @hyoloui --- next.config.js | 6 +++++- package.json | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/next.config.js b/next.config.js index 8c781fc1..26726934 100644 --- a/next.config.js +++ b/next.config.js @@ -1,4 +1,8 @@ /** @type {import('next').NextConfig} */ + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const removeImports = require("next-remove-imports")(); + const nextConfig = { images: { domains: [ @@ -13,4 +17,4 @@ const nextConfig = { }, }; -module.exports = nextConfig; +module.exports = removeImports({ ...nextConfig }); diff --git a/package.json b/package.json index fb9d129f..476cdbb9 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@toast-ui/editor-plugin-code-syntax-highlight": "^3.1.0", "@toast-ui/editor-plugin-color-syntax": "^3.1.0", "@toast-ui/react-editor": "^3.2.2", + "@uiw/react-md-editor": "^3.20.5", "axios": "^1.3.2", "browser-image-compression": "^2.0.0", "eslint": "8.33.0", @@ -25,6 +26,7 @@ "highlight": "^0.2.4", "lodash": "^4.17.21", "next": "13.1.6", + "next-remove-imports": "^1.0.10", "prismjs": "^1.29.0", "react": "18.2.0", "react-datepicker": "^4.10.0", From 158e3bd38495b4a28b463581d78607cf5061b173 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Mon, 27 Feb 2023 12:31:14 +0900 Subject: [PATCH 02/16] =?UTF-8?q?delete(toastUI):=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=97=90=EB=94=94=ED=84=B0=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit editor 변경 #224 @hyoloui --- Components/CreatePost/PostEditor.tsx | 79 +++++----------------------- pages/create-post.tsx | 20 ++----- pages/edit-post/[id].tsx | 6 +-- 3 files changed, 21 insertions(+), 84 deletions(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index 923fe3de..fa7109d3 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -1,16 +1,7 @@ -import "@toast-ui/editor/dist/toastui-editor.css"; -import "@toast-ui/editor/dist/theme/toastui-editor-dark.css"; -import "tui-color-picker/dist/tui-color-picker.css"; -import "@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css"; -import "@toast-ui/editor/dist/i18n/ko-kr"; -import "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight.css"; -import "prismjs/themes/prism.css"; +import "@uiw/react-md-editor/markdown-editor.css"; +import "@uiw/react-markdown-preview/markdown.css"; -import { Editor } from "@toast-ui/react-editor"; -import colorSyntax from "@toast-ui/editor-plugin-color-syntax"; -import codeSyntaxHighlightPlugin from "@toast-ui/editor-plugin-code-syntax-highlight"; -import Prism from "prismjs"; -import { RefObject, useCallback, useEffect } from "react"; +import { useCallback } from "react"; import supabase from "@/lib/supabase"; import imageCompression from "browser-image-compression"; import { useRecoilState } from "recoil"; @@ -21,21 +12,8 @@ import { postContent as recoilPostContent } from "@/lib/recoil"; * @TODO uuid flag 꽃아야 함 >> 게시와 임시저장의 용도로 분류 */ -interface PostEditorProps { - editorRef: RefObject; -} - -const PostEditor = ({ editorRef }: PostEditorProps) => { +const PostEditor = () => { const [postContent, setPostContent] = useRecoilState(recoilPostContent); - const toolbarItems = [ - ["heading", "bold", "italic", "strike"], - ["hr"], - ["ul", "ol", "task"], - ["table", "link"], - ["image"], // <-- 이미지 추가 툴바 - ["code"], - ["scrollSync"], - ]; // 이미지 추가 type HookCallback = (url: string, text?: string) => void; @@ -48,16 +26,7 @@ const PostEditor = ({ editorRef }: PostEditorProps) => { dropImage(url, `${blob.name}`); // 에디터에 이미지 추가 }, []); - useEffect(() => { - if (editorRef.current) { - const editorIns = editorRef.current.getInstance(); - editorIns.removeHook("addImageBlobHook"); - editorIns.addHook("addImageBlobHook", addImage); - } - }, [editorRef, addImage]); - // 이미지 업로드 - const uploadImage = async (blob: File) => { try { const imgPath = crypto.randomUUID(); @@ -86,37 +55,17 @@ const PostEditor = ({ editorRef }: PostEditorProps) => { return result; }; - const handleOnEditorChange = () => { - // 유효성 검사 - const editorText = editorRef.current?.getInstance().getMarkdown(); - if (editorText === " " || editorText === "" || editorText === undefined) { - return; - } - // HTML 대신에 Markdown으로 저장합니다. - setPostContent(editorText); - }; + // const handleOnEditorChange = () => { + // // 유효성 검사 + // const editorText = editorRef.current?.getInstance().getMarkdown(); + // if (editorText === " " || editorText === "" || editorText === undefined) { + // return; + // } + // // HTML 대신에 Markdown으로 저장합니다. + // setPostContent(editorText); + // }; - return ( - handleOnEditorChange()} - /> - ); + return
에디터
; }; export default PostEditor; diff --git a/pages/create-post.tsx b/pages/create-post.tsx index 467dc484..e33f3047 100644 --- a/pages/create-post.tsx +++ b/pages/create-post.tsx @@ -1,14 +1,13 @@ import Post from "@/Components/CreatePost/Post/Post"; +import PostEditor from "@/Components/CreatePost/PostEditor"; import { userLoginCheck } from "@/lib/recoil"; -import { Editor } from "@toast-ui/react-editor"; -import { NextPage, GetServerSideProps } from "next"; -import dynamic from "next/dynamic"; +import { GetServerSideProps } from "next"; import { useRouter } from "next/router"; -import { useEffect, useRef } from "react"; +import { useEffect } from "react"; import { useRecoilValue } from "recoil"; import styled from "styled-components"; -const CreatePostPage: NextPage = () => { +const CreatePostPage = () => { const isLogin = useRecoilValue(userLoginCheck); const router = useRouter(); @@ -19,19 +18,10 @@ const CreatePostPage: NextPage = () => { } }, []); - const editorRef = useRef(null); - - const PostEditor = dynamic( - () => import("@/Components/CreatePost/PostEditor"), - { - ssr: false, - } - ); - return ( - + ); }; diff --git a/pages/edit-post/[id].tsx b/pages/edit-post/[id].tsx index fb98dde2..b440ec93 100644 --- a/pages/edit-post/[id].tsx +++ b/pages/edit-post/[id].tsx @@ -14,16 +14,14 @@ import { userLoginCheck, } from "@/lib/recoil"; import supabase from "@/lib/supabase"; -import { Editor } from "@toast-ui/react-editor"; import { NextPage } from "next"; import dynamic from "next/dynamic"; import { useRouter } from "next/router"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import { useRecoilValue, useSetRecoilState } from "recoil"; import styled from "styled-components"; const EditPostPage: NextPage = () => { - const editorRef = useRef(null); const router = useRouter(); const [post, setPost] = useState(null); const isLogin = useRecoilValue(userLoginCheck); @@ -98,7 +96,7 @@ const EditPostPage: NextPage = () => { return ( - + ); }; From 7537290162323f004fa85aa8d2e27e2d85ae8778 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Mon, 27 Feb 2023 14:47:24 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat(PostEditor):=20MDEditor=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 에디터를 추가 합니다. editor 변경 #224 @hyoloui --- Components/CreatePost/PostEditor.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index fa7109d3..7ba7f040 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -6,12 +6,17 @@ import supabase from "@/lib/supabase"; import imageCompression from "browser-image-compression"; import { useRecoilState } from "recoil"; import { postContent as recoilPostContent } from "@/lib/recoil"; +import dynamic from "next/dynamic"; /** * @TODO storage 삭제 구현 필요 * @TODO uuid flag 꽃아야 함 >> 게시와 임시저장의 용도로 분류 */ +const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { + ssr: false, +}); + const PostEditor = () => { const [postContent, setPostContent] = useRecoilState(recoilPostContent); @@ -32,7 +37,7 @@ const PostEditor = () => { const imgPath = crypto.randomUUID(); await supabase.storage.from("post-image").upload(imgPath, blob); - // 이미지 올리기 + // 이미지 url 가져오기 const urlResult = await supabase.storage .from("post-image") .getPublicUrl(imgPath); @@ -65,7 +70,9 @@ const PostEditor = () => { // setPostContent(editorText); // }; - return
에디터
; + return ( + + ); }; export default PostEditor; From 4eed639419c3aab2306dc5b7904e233b3dce120e Mon Sep 17 00:00:00 2001 From: hyoloui Date: Mon, 27 Feb 2023 15:01:16 +0900 Subject: [PATCH 04/16] =?UTF-8?q?install:=20=ED=95=84=EC=9A=94=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=9D=B8=EC=8A=A4=ED=86=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - editor 뷰어 패키지 인스톨 editor 변경 #224 @hyoloui --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 476cdbb9..3dbd73e2 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@toast-ui/editor-plugin-code-syntax-highlight": "^3.1.0", "@toast-ui/editor-plugin-color-syntax": "^3.1.0", "@toast-ui/react-editor": "^3.2.2", + "@uiw/react-markdown-preview": "^4.1.9", "@uiw/react-md-editor": "^3.20.5", "axios": "^1.3.2", "browser-image-compression": "^2.0.0", From 904a088fe0aada8876aea23d0981b297ce67f0f1 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Mon, 27 Feb 2023 15:13:42 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat(DetailContent):=20=EB=B7=B0=EC=96=B4?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 에디터 뷰어를 추가 합니다. editor 변경 #224 @hyoloui --- Components/Detail/DetailArticle.tsx | 8 ++------ Components/Detail/DetailContent.tsx | 26 ++++++++++++++------------ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Components/Detail/DetailArticle.tsx b/Components/Detail/DetailArticle.tsx index 8a9fc3ef..13e67eaf 100644 --- a/Components/Detail/DetailArticle.tsx +++ b/Components/Detail/DetailArticle.tsx @@ -8,15 +8,11 @@ import { import supabase from "@/lib/supabase"; import getTextColorByBackgroundColor from "@/utils/detail/getTextColorByBackgroundColor"; import { useQueryClient } from "@tanstack/react-query"; -import dynamic from "next/dynamic"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { ClimbingBoxLoader } from "react-spinners"; import styled from "styled-components"; - -const Viewer = dynamic(() => import("@/Components/Detail/DetailContent"), { - ssr: false, -}); +import DetailContent from "./DetailContent"; const DetailArticle = () => { const { @@ -208,7 +204,7 @@ const DetailArticle = () => { /> - {content && } + {content && } diff --git a/Components/Detail/DetailContent.tsx b/Components/Detail/DetailContent.tsx index b689a51c..ca6a814b 100644 --- a/Components/Detail/DetailContent.tsx +++ b/Components/Detail/DetailContent.tsx @@ -1,22 +1,24 @@ -import "@toast-ui/editor/dist/toastui-editor-viewer.css"; -import "prismjs/themes/prism.css"; -import "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight.css"; +import "@uiw/react-md-editor/markdown-editor.css"; +import "@uiw/react-markdown-preview/markdown.css"; -import { Viewer } from "@toast-ui/react-editor"; -import codeSyntaxHighlightPlugin from "@toast-ui/editor-plugin-code-syntax-highlight"; -import Prism from "prismjs"; +import dynamic from "next/dynamic"; +import styled from "styled-components"; + +const MarkdownPreview = dynamic(() => import("@uiw/react-markdown-preview"), { + ssr: false, +}); interface DetailContentProps { content: string; } const DetailContent = ({ content }: DetailContentProps) => { - return ( - - ); + return ; }; +const PreviewContent = styled(MarkdownPreview)` + width: 100%; + padding: 1rem; +`; + export default DetailContent; From 9f884da57bbff93ccf68d1f6cee67fbab8fb3641 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Mon, 27 Feb 2023 15:30:41 +0900 Subject: [PATCH 06/16] =?UTF-8?q?fix(DetailContent):=20=EB=B7=B0=EC=96=B4?= =?UTF-8?q?=20=EB=84=9A=EC=9D=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 에디터 뷰어 스타일을 수정 합니다. editor 변경 #224 @hyoloui --- Components/Detail/DetailContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/Detail/DetailContent.tsx b/Components/Detail/DetailContent.tsx index ca6a814b..65f333e5 100644 --- a/Components/Detail/DetailContent.tsx +++ b/Components/Detail/DetailContent.tsx @@ -17,7 +17,7 @@ const DetailContent = ({ content }: DetailContentProps) => { }; const PreviewContent = styled(MarkdownPreview)` - width: 100%; + width: 60rem; padding: 1rem; `; From 8aef73aec1522ada55696b0e67a1870e21363b8c Mon Sep 17 00:00:00 2001 From: hyoloui Date: Mon, 27 Feb 2023 21:02:20 +0900 Subject: [PATCH 07/16] =?UTF-8?q?fix(Post):=20content=20=EC=9D=B4=EB=8B=88?= =?UTF-8?q?=EC=85=9C=20string=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - place holder를 사용하기 위해 이니셜 코드 삭제 editor 변경 #224 @hyoloui --- Components/CreatePost/Post/Post.tsx | 2 +- lib/recoil/postAtoms.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Components/CreatePost/Post/Post.tsx b/Components/CreatePost/Post/Post.tsx index 9e18eff2..ddf19581 100644 --- a/Components/CreatePost/Post/Post.tsx +++ b/Components/CreatePost/Post/Post.tsx @@ -162,7 +162,7 @@ const Post: NextPage = () => { setTag([]); setIsPublic(true); setMembers([]); - setContent("프로젝트 내용을 입력해주세요."); + setContent(""); setPostLargeCategory(""); setPostSubCategory(""); }; diff --git a/lib/recoil/postAtoms.ts b/lib/recoil/postAtoms.ts index c5199b7d..63e1ed05 100644 --- a/lib/recoil/postAtoms.ts +++ b/lib/recoil/postAtoms.ts @@ -59,7 +59,7 @@ const postPublic = atom({ const postContent = atom({ key: "postContent", - default: "프로젝트 내용을 입력해주세요.", + default: "", }); export { From c22a4f28bfd3eec925709b36606789fe1239ae3e Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 03:16:35 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat(PostEditor):=20MDEditor=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이미지 업로드를 드래그 앤 드랍으로 구현 합니다. editor 변경 #224 @hyoloui --- Components/CreatePost/PostEditor.tsx | 125 ++++++++++++++++++--------- 1 file changed, 82 insertions(+), 43 deletions(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index 7ba7f040..17dcdab3 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -1,16 +1,17 @@ import "@uiw/react-md-editor/markdown-editor.css"; import "@uiw/react-markdown-preview/markdown.css"; -import { useCallback } from "react"; +import React, { useCallback } from "react"; import supabase from "@/lib/supabase"; -import imageCompression from "browser-image-compression"; import { useRecoilState } from "recoil"; -import { postContent as recoilPostContent } from "@/lib/recoil"; +import { postContent as recoilPostContent, postId } from "@/lib/recoil"; import dynamic from "next/dynamic"; +import imageCompression from "browser-image-compression"; /** + * @TODO postId 에러 해결 필요 + * @TODO 이미지 아이콘 클릭시 이미지 업로드 구현 필요 * @TODO storage 삭제 구현 필요 - * @TODO uuid flag 꽃아야 함 >> 게시와 임시저장의 용도로 분류 */ const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { @@ -18,40 +19,47 @@ const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { }); const PostEditor = () => { + const [isPostId] = useRecoilState(postId); const [postContent, setPostContent] = useRecoilState(recoilPostContent); - // 이미지 추가 - type HookCallback = (url: string, text?: string) => void; - - const addImage = useCallback(async (blob: File, dropImage: HookCallback) => { - const img = await compressImg(blob); // 이미지 압축 - if (!img) return; - const url = await uploadImage(img); // 업로드된 이미지 서버 url - if (!url) return; - dropImage(url, `${blob.name}`); // 에디터에 이미지 추가 - }, []); - - // 이미지 업로드 - const uploadImage = async (blob: File) => { - try { - const imgPath = crypto.randomUUID(); - await supabase.storage.from("post-image").upload(imgPath, blob); - - // 이미지 url 가져오기 - const urlResult = await supabase.storage - .from("post-image") - .getPublicUrl(imgPath); - return urlResult.data.publicUrl; - } catch (error) { - console.log(error); - return false; - } - }; + const onImagePasted = useCallback( + async ( + dataTransfer: DataTransfer // Drag and Drop API + ) => { + const files: File[] = []; // 드래그 앤 드랍으로 가져온 파일들 + for (let index = 0; index < dataTransfer.items.length; index += 1) { + const file = dataTransfer.files.item(index); + if (file) { + const compressedFile = await compressImg(file); + if (!compressedFile) return; + files.push(compressedFile); // 배열에 파일 추가 + } + } + const fileId = crypto.randomUUID(); // 파일 id 생성 + files.map(async (file) => { + const { data: uploadImg } = await supabase.storage + .from("post-image") + .upload(`${isPostId}/${fileId}`, file); + if (!uploadImg) return; + + const { data: insertedMarkdown } = supabase.storage + .from("post-image") + .getPublicUrl(`${isPostId}/${fileId}`); + if (!insertedMarkdown) return; + + const insertString = `![${file.name}](${insertedMarkdown.publicUrl})`; + const resultString = insertToTextArea(insertString); + + setPostContent(resultString || ""); + }); + }, + [isPostId, setPostContent] + ); - // //이미지 압축 + // 이미지 압축 const compressImg = async (blob: File): Promise => { const options = { - maxSize: 1, + maxSizeMB: 1, initialQuality: 0.55, // initial 0.7 }; const result = await imageCompression(blob, options) @@ -60,18 +68,49 @@ const PostEditor = () => { return result; }; - // const handleOnEditorChange = () => { - // // 유효성 검사 - // const editorText = editorRef.current?.getInstance().getMarkdown(); - // if (editorText === " " || editorText === "" || editorText === undefined) { - // return; - // } - // // HTML 대신에 Markdown으로 저장합니다. - // setPostContent(editorText); - // }; + // 에디터에 이미지 추가 + const insertToTextArea = (intsertString: string) => { + const textarea = document.querySelector("textarea"); + if (!textarea) { + return null; + } + let sentence = textarea.value; + const len = sentence.length; + const pos = textarea.selectionStart; + const end = textarea.selectionEnd; + + const front = sentence.slice(0, pos); + const back = sentence.slice(pos, len); + + sentence = front + intsertString + back; + + textarea.value = sentence; + textarea.selectionEnd = end + intsertString.length; + + return sentence; + }; return ( - + // div에 클래스를 적용하여 다크모드를 수동으로 적용할 수 있습니다. +
+ { + setPostContent(value || ""); + }} + height={600} + onPaste={(event) => { + onImagePasted(event.clipboardData); + }} + onDrop={(event) => { + onImagePasted(event.dataTransfer); + }} + textareaProps={{ + placeholder: "Fill in your markdown for the coolest of the cool.", + }} + /> +
); }; From d5a14f6069a7d11d0f7b85a4c6f4782d892b86d5 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 03:17:20 +0900 Subject: [PATCH 09/16] =?UTF-8?q?fix(postId):=20post=5Fid=EB=A5=BC=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95=ED=95=A9=EB=8B=88=EB=8B=A4.=20editor=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20#224=20@hyoloui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Components/CreatePost/Post/Post.tsx | 3 +++ lib/recoil/index.ts | 2 ++ lib/recoil/postAtoms.ts | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/Components/CreatePost/Post/Post.tsx b/Components/CreatePost/Post/Post.tsx index ddf19581..6d909dab 100644 --- a/Components/CreatePost/Post/Post.tsx +++ b/Components/CreatePost/Post/Post.tsx @@ -16,6 +16,7 @@ import { postTags, postTitle, postTitleBackgroundColor, + postId, } from "@/lib/recoil"; import supabase from "@/lib/supabase"; import { useState, useEffect } from "react"; @@ -29,6 +30,7 @@ import ProjectInfo from "./ProjectInfo"; * @TODO user_id 리코일로 관리 */ const Post: NextPage = () => { + const [isPostId] = useRecoilState(postId); const [title, setTitle] = useRecoilState(postTitle); const [subTitle, setSubTitle] = useRecoilState(postSubTitle); const [titleBackgroundColor, setTitleBackgroundColor] = useRecoilState( @@ -54,6 +56,7 @@ const Post: NextPage = () => { const router = useRouter(); const newPostRow = { + id: isPostId, title, sub_title: subTitle, title_background_color: titleBackgroundColor, diff --git a/lib/recoil/index.ts b/lib/recoil/index.ts index 94553b63..0cf5a933 100644 --- a/lib/recoil/index.ts +++ b/lib/recoil/index.ts @@ -1,6 +1,7 @@ import { largeCategoryState, subCategoryState } from "@/lib/recoil/atoms"; import { + postId, postTitle, postSubTitle, postTitleBackgroundColor, @@ -40,6 +41,7 @@ export { largeCategoryState, subCategoryState, // post + postId, postTitle, postSubTitle, postTitleBackgroundColor, diff --git a/lib/recoil/postAtoms.ts b/lib/recoil/postAtoms.ts index 63e1ed05..108f8c3a 100644 --- a/lib/recoil/postAtoms.ts +++ b/lib/recoil/postAtoms.ts @@ -1,6 +1,11 @@ import getYYYYMM from "@/utils/commons/getYYYYMM"; import { atom } from "recoil"; +const postId = atom({ + key: "postId", + default: `${crypto.randomUUID()}`, +}); + const postTitle = atom({ key: "postTitle", default: "", @@ -63,6 +68,7 @@ const postContent = atom({ }); export { + postId, postTitle, postSubTitle, postTitleBackgroundColor, From b0cf9d67b19e4c3c6aa882248128598c0582fdcd Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 10:27:31 +0900 Subject: [PATCH 10/16] =?UTF-8?q?update:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=20=EC=84=A4=EC=B9=98=20=EB=B0=8F=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - toast-ui 라이브러리를 삭제합니다. - 기존 crypto.randomUUID() 를 uuid 라이브러리로 대체합니다. editor 변경 #224 @hyoloui --- Components/MyPage/UserInfoContainer/UserInfoContainer.tsx | 3 ++- lib/recoil/postAtoms.ts | 3 ++- package.json | 7 +++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Components/MyPage/UserInfoContainer/UserInfoContainer.tsx b/Components/MyPage/UserInfoContainer/UserInfoContainer.tsx index 20c0e7b7..9c630958 100644 --- a/Components/MyPage/UserInfoContainer/UserInfoContainer.tsx +++ b/Components/MyPage/UserInfoContainer/UserInfoContainer.tsx @@ -2,6 +2,7 @@ import supabase from "@/lib/supabase"; import Image, { StaticImageData } from "next/image"; import { ChangeEvent, useState } from "react"; import styled from "styled-components"; +import { v4 as uuidv4 } from "uuid"; import convertEase64ToFile from "@/utils/commons/convertBase64ToFile"; import { useUserProfile } from "@/hooks/query"; import { useInput } from "@/hooks/common"; @@ -42,7 +43,7 @@ const UserInfoContainer = () => { }; const uploadImage = async (file: File) => { - const imgPath = crypto.randomUUID(); + const imgPath = uuidv4(); try { await supabase.storage.from("post-image").upload(imgPath, file); const { data } = await supabase.storage diff --git a/lib/recoil/postAtoms.ts b/lib/recoil/postAtoms.ts index 108f8c3a..58e805da 100644 --- a/lib/recoil/postAtoms.ts +++ b/lib/recoil/postAtoms.ts @@ -1,9 +1,10 @@ import getYYYYMM from "@/utils/commons/getYYYYMM"; +import { v4 as uuidv4 } from "uuid"; import { atom } from "recoil"; const postId = atom({ key: "postId", - default: `${crypto.randomUUID()}`, + default: `${uuidv4()}`, }); const postTitle = atom({ diff --git a/package.json b/package.json index 3dbd73e2..ce6a70f0 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,6 @@ "@supabase/supabase-js": "^2.7.1", "@tanstack/react-query": "^4.24.6", "@tanstack/react-query-devtools": "^4.24.6", - "@toast-ui/editor-plugin-code-syntax-highlight": "^3.1.0", - "@toast-ui/editor-plugin-color-syntax": "^3.1.0", - "@toast-ui/react-editor": "^3.2.2", "@uiw/react-markdown-preview": "^4.1.9", "@uiw/react-md-editor": "^3.20.5", "axios": "^1.3.2", @@ -37,7 +34,8 @@ "recoil": "^0.7.6", "styled-components": "^5.3.6", "swiper": "^9.0.5", - "typescript": "4.9.5" + "typescript": "4.9.5", + "uuid": "^9.0.0" }, "devDependencies": { "@types/lodash": "^4.14.191", @@ -47,6 +45,7 @@ "@types/react-datepicker": "^4.8.0", "@types/react-dom": "18.0.10", "@types/styled-components": "^5.1.26", + "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.51.0", "eslint-config-airbnb": "^19.0.4", From 2ba907fb27d168c6b42c964130a33784e2771416 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 10:42:50 +0900 Subject: [PATCH 11/16] =?UTF-8?q?fix(PostEditor):=20build=20error=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - for 문 안에서 이루어지는 압축을 밖으로 뺐습니다. editor 변경 #224 @hyoloui --- Components/CreatePost/PostEditor.tsx | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index 17dcdab3..e6c57aae 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -1,15 +1,17 @@ import "@uiw/react-md-editor/markdown-editor.css"; import "@uiw/react-markdown-preview/markdown.css"; -import React, { useCallback } from "react"; +import { useCallback } from "react"; +import dynamic from "next/dynamic"; import supabase from "@/lib/supabase"; +import { v4 as uuidv4 } from "uuid"; import { useRecoilState } from "recoil"; import { postContent as recoilPostContent, postId } from "@/lib/recoil"; -import dynamic from "next/dynamic"; import imageCompression from "browser-image-compression"; /** * @TODO postId 에러 해결 필요 + * @TODO 이미지 업로드시 링크 열리는 문제 해결 필요 * @TODO 이미지 아이콘 클릭시 이미지 업로드 구현 필요 * @TODO storage 삭제 구현 필요 */ @@ -29,17 +31,19 @@ const PostEditor = () => { const files: File[] = []; // 드래그 앤 드랍으로 가져온 파일들 for (let index = 0; index < dataTransfer.items.length; index += 1) { const file = dataTransfer.files.item(index); - if (file) { - const compressedFile = await compressImg(file); - if (!compressedFile) return; - files.push(compressedFile); // 배열에 파일 추가 - } + if (!file) return; + console.log(file, "파일"); + files.push(file); } - const fileId = crypto.randomUUID(); // 파일 id 생성 + const fileId = uuidv4(); files.map(async (file) => { + const compressedFile = await compressImg(file); + if (!compressedFile) return; + console.log(compressedFile, "압축된 파일"); + const { data: uploadImg } = await supabase.storage .from("post-image") - .upload(`${isPostId}/${fileId}`, file); + .upload(`${isPostId}/${fileId}`, compressedFile); if (!uploadImg) return; const { data: insertedMarkdown } = supabase.storage @@ -86,7 +90,6 @@ const PostEditor = () => { textarea.value = sentence; textarea.selectionEnd = end + intsertString.length; - return sentence; }; @@ -95,7 +98,6 @@ const PostEditor = () => {
{ setPostContent(value || ""); }} From adf14dc39b99fe801191604875c504c7424faa96 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 11:14:30 +0900 Subject: [PATCH 12/16] =?UTF-8?q?fix(console):=20=EC=BD=98=EC=86=94=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20editor=20=EB=B3=80=EA=B2=BD=20#224?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Components/CreatePost/PostEditor.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index e6c57aae..e956fd59 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -32,14 +32,12 @@ const PostEditor = () => { for (let index = 0; index < dataTransfer.items.length; index += 1) { const file = dataTransfer.files.item(index); if (!file) return; - console.log(file, "파일"); files.push(file); } const fileId = uuidv4(); files.map(async (file) => { const compressedFile = await compressImg(file); if (!compressedFile) return; - console.log(compressedFile, "압축된 파일"); const { data: uploadImg } = await supabase.storage .from("post-image") From 015848a4e0fe0e4d24409b1b62c6c628da5fb17d Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 17:52:47 +0900 Subject: [PATCH 13/16] =?UTF-8?q?feat(PostEditor):=20commands=20toolbar=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 - 툴바를 커스텀하여 이미지를 업로드 합니다 editor 변경 #224 @hyoloui @nno3onn --- Components/CreatePost/PostEditor.tsx | 107 +++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 101 insertions(+), 8 deletions(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index e956fd59..084a2026 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -8,35 +8,58 @@ import { v4 as uuidv4 } from "uuid"; import { useRecoilState } from "recoil"; import { postContent as recoilPostContent, postId } from "@/lib/recoil"; import imageCompression from "browser-image-compression"; +import { MDEditorProps } from "@uiw/react-md-editor"; +import { NextPage } from "next"; +import * as commands from "@uiw/react-md-editor/lib/commands"; /** - * @TODO postId 에러 해결 필요 * @TODO 이미지 업로드시 링크 열리는 문제 해결 필요 - * @TODO 이미지 아이콘 클릭시 이미지 업로드 구현 필요 * @TODO storage 삭제 구현 필요 */ -const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { +const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { ssr: false, }); -const PostEditor = () => { +const PostEditor: NextPage = () => { const [isPostId] = useRecoilState(postId); const [postContent, setPostContent] = useRecoilState(recoilPostContent); const onImagePasted = useCallback( async ( - dataTransfer: DataTransfer // Drag and Drop API + dataTransfer: DataTransfer | any // Drag and Drop API ) => { const files: File[] = []; // 드래그 앤 드랍으로 가져온 파일들 - for (let index = 0; index < dataTransfer.items.length; index += 1) { - const file = dataTransfer.files.item(index); + if (dataTransfer.items) { + for (let index = 0; index < dataTransfer.items.length; index += 1) { + const file = dataTransfer.items[index].getAsFile(); + if (!file) return; + files.push(file); + } + } else { + const file = dataTransfer[0]; + console.log("👉👉 file:", file); + if (!file) return; files.push(file); } + + // for ( + // let index = 0; + // index < + // (dataTransfer.items ? dataTransfer.items.length : dataTransfer.length); + // index += 1 + // ) { + // const file = dataTransfer.items + // ? dataTransfer.files.item(index) + // : dataTransfer[0]; + // if (!file) return; + // files.push(file); + // } const fileId = uuidv4(); files.map(async (file) => { const compressedFile = await compressImg(file); + if (!compressedFile) return; const { data: uploadImg } = await supabase.storage @@ -109,6 +132,76 @@ const PostEditor = () => { textareaProps={{ placeholder: "Fill in your markdown for the coolest of the cool.", }} + commands={[ + commands.bold, + commands.italic, + commands.strikethrough, + commands.hr, + commands.title, + commands.divider, + + commands.link, + commands.group([], { + name: "image", + groupName: "image", + icon: ( + + + + + + + + + ), + // eslint-disable-next-line react/no-unstable-nested-components + children: (handle: any) => { + return ( +
+ onImagePasted(e.target.files)} + /> + +
+ ); + }, + execute: ( + state: commands.TextState, + api: commands.TextAreaTextApi + ) => { + console.log(">>>>>>update>>>>>", state); + }, + buttonProps: { "aria-label": "Insert title" }, + }), + commands.quote, + commands.code, + commands.divider, + + commands.unorderedListCommand, + commands.orderedListCommand, + commands.checkedListCommand, + commands.divider, + ]} />
); diff --git a/package.json b/package.json index ce6a70f0..4555fbc0 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@tanstack/react-query": "^4.24.6", "@tanstack/react-query-devtools": "^4.24.6", "@uiw/react-markdown-preview": "^4.1.9", - "@uiw/react-md-editor": "^3.20.5", + "@uiw/react-md-editor": "3.6.0", "axios": "^1.3.2", "browser-image-compression": "^2.0.0", "eslint": "8.33.0", From 1c177bc5c9a4baf3b9038c079f61b571f6c3572e Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 19:44:46 +0900 Subject: [PATCH 14/16] =?UTF-8?q?fix(PostEditor):=20PR=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #224 @nno3onn @hyoloui --- Components/CreatePost/PostEditor.tsx | 170 ++++++++++++--------------- package.json | 2 - 2 files changed, 75 insertions(+), 97 deletions(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index 084a2026..d75ad1fb 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -8,7 +8,7 @@ import { v4 as uuidv4 } from "uuid"; import { useRecoilState } from "recoil"; import { postContent as recoilPostContent, postId } from "@/lib/recoil"; import imageCompression from "browser-image-compression"; -import { MDEditorProps } from "@uiw/react-md-editor"; +import type { MDEditorProps } from "@uiw/react-md-editor"; import { NextPage } from "next"; import * as commands from "@uiw/react-md-editor/lib/commands"; @@ -38,24 +38,11 @@ const PostEditor: NextPage = () => { } } else { const file = dataTransfer[0]; - console.log("👉👉 file:", file); if (!file) return; files.push(file); } - // for ( - // let index = 0; - // index < - // (dataTransfer.items ? dataTransfer.items.length : dataTransfer.length); - // index += 1 - // ) { - // const file = dataTransfer.items - // ? dataTransfer.files.item(index) - // : dataTransfer[0]; - // if (!file) return; - // files.push(file); - // } const fileId = uuidv4(); files.map(async (file) => { const compressedFile = await compressImg(file); @@ -116,94 +103,87 @@ const PostEditor: NextPage = () => { return ( // div에 클래스를 적용하여 다크모드를 수동으로 적용할 수 있습니다. -
- { - setPostContent(value || ""); - }} - height={600} - onPaste={(event) => { - onImagePasted(event.clipboardData); - }} - onDrop={(event) => { - onImagePasted(event.dataTransfer); - }} - textareaProps={{ - placeholder: "Fill in your markdown for the coolest of the cool.", - }} - commands={[ - commands.bold, - commands.italic, - commands.strikethrough, - commands.hr, - commands.title, - commands.divider, - - commands.link, - commands.group([], { - name: "image", - groupName: "image", - icon: ( - + { + setPostContent(value || ""); + }} + height={600} + onPaste={(event) => { + onImagePasted(event.clipboardData); + }} + onDrop={(event) => { + onImagePasted(event.dataTransfer); + }} + textareaProps={{ + placeholder: "Fill in your markdown for the coolest of the cool.", + }} + commands={[ + commands.bold, + commands.italic, + commands.strikethrough, + commands.hr, + commands.title, + commands.divider, + + commands.link, + commands.group([], { + name: "image", + groupName: "image", + icon: ( + + - - - - - - - ), - // eslint-disable-next-line react/no-unstable-nested-components - children: (handle: any) => { - return ( -
- onImagePasted(e.target.files)} /> - -
- ); - }, - execute: ( - state: commands.TextState, - api: commands.TextAreaTextApi - ) => { - console.log(">>>>>>update>>>>>", state); - }, - buttonProps: { "aria-label": "Insert title" }, - }), - commands.quote, - commands.code, - commands.divider, - - commands.unorderedListCommand, - commands.orderedListCommand, - commands.checkedListCommand, - commands.divider, - ]} - /> -
+ + + + ), + // eslint-disable-next-line react/no-unstable-nested-components + children: (handle: any) => { + return ( +
+ onImagePasted(e.target.files)} + /> + +
+ ); + }, + buttonProps: { "aria-label": "Insert image" }, + }), + commands.quote, + commands.code, + commands.divider, + + commands.unorderedListCommand, + commands.orderedListCommand, + commands.checkedListCommand, + commands.divider, + ]} + /> ); }; diff --git a/package.json b/package.json index 4555fbc0..a59226e6 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "lodash": "^4.17.21", "next": "13.1.6", "next-remove-imports": "^1.0.10", - "prismjs": "^1.29.0", "react": "18.2.0", "react-datepicker": "^4.10.0", "react-dom": "18.2.0", @@ -40,7 +39,6 @@ "devDependencies": { "@types/lodash": "^4.14.191", "@types/node": "18.13.0", - "@types/prismjs": "^1.26.0", "@types/react": "18.0.27", "@types/react-datepicker": "^4.8.0", "@types/react-dom": "18.0.10", From 6441411490f19b701ccb02d45dc94245bb6b31d6 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 20:20:11 +0900 Subject: [PATCH 15/16] =?UTF-8?q?fix(PostEditor):=20PR=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #224 @nno3onn @hyoloui --- Components/CreatePost/PostEditor.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index d75ad1fb..c19e5c1b 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -5,7 +5,7 @@ import { useCallback } from "react"; import dynamic from "next/dynamic"; import supabase from "@/lib/supabase"; import { v4 as uuidv4 } from "uuid"; -import { useRecoilState } from "recoil"; +import { useRecoilState, useRecoilValue } from "recoil"; import { postContent as recoilPostContent, postId } from "@/lib/recoil"; import imageCompression from "browser-image-compression"; import type { MDEditorProps } from "@uiw/react-md-editor"; @@ -13,6 +13,7 @@ import { NextPage } from "next"; import * as commands from "@uiw/react-md-editor/lib/commands"; /** + * @TODO supabase api utill함수로 사용하도록 변경 필요 * @TODO 이미지 업로드시 링크 열리는 문제 해결 필요 * @TODO storage 삭제 구현 필요 */ @@ -22,7 +23,7 @@ const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { }); const PostEditor: NextPage = () => { - const [isPostId] = useRecoilState(postId); + const [isPostId] = useRecoilValue(postId); const [postContent, setPostContent] = useRecoilState(recoilPostContent); const onImagePasted = useCallback( From 79a9c2709555a39a6bfb154a51ef1f465b3a1f30 Mon Sep 17 00:00:00 2001 From: hyoloui Date: Tue, 28 Feb 2023 20:26:34 +0900 Subject: [PATCH 16/16] =?UTF-8?q?fix(PostEditor):=20PR=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #224 @nno3onn @hyoloui --- Components/CreatePost/PostEditor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/CreatePost/PostEditor.tsx b/Components/CreatePost/PostEditor.tsx index c19e5c1b..510a115f 100644 --- a/Components/CreatePost/PostEditor.tsx +++ b/Components/CreatePost/PostEditor.tsx @@ -23,7 +23,7 @@ const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { }); const PostEditor: NextPage = () => { - const [isPostId] = useRecoilValue(postId); + const isPostId = useRecoilValue(postId); const [postContent, setPostContent] = useRecoilState(recoilPostContent); const onImagePasted = useCallback(