From 32d1bbb95055042550b63d489d43cc19f595fde7 Mon Sep 17 00:00:00 2001 From: hae-on Date: Mon, 16 Oct 2023 13:28:42 +0900 Subject: [PATCH 1/7] =?UTF-8?q?refactor:=20alert=EB=A5=BC=20toast=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Common/ImageUploader/ImageUploader.tsx | 4 +++- frontend/src/hooks/common/useImageUploader.ts | 6 +++++- frontend/src/pages/MemberModifyPage.tsx | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Common/ImageUploader/ImageUploader.tsx b/frontend/src/components/Common/ImageUploader/ImageUploader.tsx index bd85c20e3..9c915081e 100644 --- a/frontend/src/components/Common/ImageUploader/ImageUploader.tsx +++ b/frontend/src/components/Common/ImageUploader/ImageUploader.tsx @@ -4,6 +4,7 @@ import styled from 'styled-components'; import { IMAGE_MAX_SIZE } from '@/constants'; import { useEnterKeyDown } from '@/hooks/common'; +import { useToastActionContext } from '@/hooks/context'; interface ReviewImageUploaderProps { previewImage: string; @@ -13,6 +14,7 @@ interface ReviewImageUploaderProps { const ImageUploader = ({ previewImage, uploadImage, deleteImage }: ReviewImageUploaderProps) => { const { inputRef, handleKeydown } = useEnterKeyDown(); + const { toast } = useToastActionContext(); const handleImageUpload: ChangeEventHandler = (event) => { if (!event.target.files) { @@ -22,7 +24,7 @@ const ImageUploader = ({ previewImage, uploadImage, deleteImage }: ReviewImageUp const imageFile = event.target.files[0]; if (imageFile.size > IMAGE_MAX_SIZE) { - alert('이미지 크기가 너무 커요. 5MB 이하의 이미지를 골라주세요.'); + toast.error('이미지 크기가 너무 커요. 5MB 이하의 이미지를 골라주세요.'); event.target.value = ''; return; } diff --git a/frontend/src/hooks/common/useImageUploader.ts b/frontend/src/hooks/common/useImageUploader.ts index c5783ad59..18dfea459 100644 --- a/frontend/src/hooks/common/useImageUploader.ts +++ b/frontend/src/hooks/common/useImageUploader.ts @@ -1,14 +1,18 @@ import { useState } from 'react'; +import { useToastActionContext } from '../context'; + const isImageFile = (file: File) => file.type !== 'image/png' && file.type !== 'image/jpeg'; const useImageUploader = () => { + const { toast } = useToastActionContext(); + const [imageFile, setImageFile] = useState(null); const [previewImage, setPreviewImage] = useState(''); const uploadImage = (imageFile: File) => { if (isImageFile(imageFile)) { - alert('이미지 파일만 업로드 가능합니다.'); + toast.error('이미지 파일만 업로드 가능합니다.'); return; } diff --git a/frontend/src/pages/MemberModifyPage.tsx b/frontend/src/pages/MemberModifyPage.tsx index c6c43a8c3..a5c21490b 100644 --- a/frontend/src/pages/MemberModifyPage.tsx +++ b/frontend/src/pages/MemberModifyPage.tsx @@ -8,6 +8,7 @@ import { SectionTitle, SvgIcon } from '@/components/Common'; import { MemberModifyInput } from '@/components/Members'; import { IMAGE_MAX_SIZE } from '@/constants'; import { useFormData, useImageUploader } from '@/hooks/common'; +import { useToastActionContext } from '@/hooks/context'; import { useMemberModifyMutation, useMemberQuery } from '@/hooks/queries/members'; import type { MemberRequest } from '@/types/member'; @@ -16,6 +17,7 @@ export const MemberModifyPage = () => { const { mutate } = useMemberModifyMutation(); const { previewImage, imageFile, uploadImage } = useImageUploader(); + const { toast } = useToastActionContext(); const [nickname, setNickname] = useState(member?.nickname ?? ''); const navigate = useNavigate(); @@ -43,7 +45,7 @@ export const MemberModifyPage = () => { const imageFile = event.target.files[0]; if (imageFile.size > IMAGE_MAX_SIZE) { - alert('이미지 크기가 너무 커요. 5MB 이하의 이미지를 골라주세요.'); + toast.error('이미지 크기가 너무 커요. 5MB 이하의 이미지를 골라주세요.'); event.target.value = ''; return; } From 7f089275c9fa4e29bb5c3cbf6b7047c6ea6a13c3 Mon Sep 17 00:00:00 2001 From: hae-on Date: Wed, 18 Oct 2023 11:18:35 +0900 Subject: [PATCH 2/7] =?UTF-8?q?refactor:=20dialog=20=EC=BB=A8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=84=88=20div=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/public/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/public/index.html b/frontend/public/index.html index 0359ca16d..36782ea87 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -27,6 +27,7 @@
+
From 883fd79fa04d5ead424c8e1d5787c056a1dcc5bc Mon Sep 17 00:00:00 2001 From: hae-on Date: Wed, 18 Oct 2023 12:45:34 +0900 Subject: [PATCH 3/7] =?UTF-8?q?refactor:=20dialog=20=EC=BB=A8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=84=88=20div=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.storybook/preview-body.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html index 9e1c31c57..aaa544054 100644 --- a/frontend/.storybook/preview-body.html +++ b/frontend/.storybook/preview-body.html @@ -116,4 +116,6 @@ +
+ From 47fa0e01d05585fedf79fcd0e5e954d2a63d42a5 Mon Sep 17 00:00:00 2001 From: hae-on Date: Wed, 18 Oct 2023 13:24:37 +0900 Subject: [PATCH 4/7] =?UTF-8?q?chore:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=B2=84=EC=A0=84=20=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package.json | 2 +- frontend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index ef994ca5e..76abadcd4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,7 @@ "test:coverage": "jest --watchAll --coverage" }, "dependencies": { - "@fun-eat/design-system": "^0.3.15", + "@fun-eat/design-system": "^0.3.18", "@tanstack/react-query": "^4.32.6", "@tanstack/react-query-devtools": "^4.32.6", "dayjs": "^1.11.9", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 3cdae7d06..0a59dfb08 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1375,10 +1375,10 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== -"@fun-eat/design-system@^0.3.15": - version "0.3.15" - resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.3.15.tgz#61a9a01a82f84fa5627c49bd646cb72ca9e648c8" - integrity sha512-uhn5UZWfvQhNz/2sOoMwDr7Hj7SSx94bN35jifuYpm7ju0A8LHfivmu0mAbrMojuQ6XKYf0ZUME8FMMHwpw9Fg== +"@fun-eat/design-system@^0.3.18": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.3.18.tgz#0c930437cd47923a9daffbaec748ef5db3b4d0c1" + integrity sha512-d1yfTLJLKPakFzf/wiDcLkRi5cit16hDJClH4+Mj6nMtChxMeUu3VU+i4oCJNqaNjZHDw9wOa+7L4kmIcKQnRg== "@humanwhocodes/config-array@^0.11.11": version "0.11.11" From eaff299ffb994d80f798bea1216e05f5fa2a0a47 Mon Sep 17 00:00:00 2001 From: hae-on Date: Wed, 18 Oct 2023 13:24:54 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20dialog=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/SortOptionList/SortOptionList.stories.tsx | 4 ++-- frontend/src/pages/ProductDetailPage.tsx | 4 ++-- frontend/src/pages/ProductListPage.tsx | 4 ++-- frontend/src/pages/RecipePage.tsx | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx b/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx index 779e68942..5e7c2f935 100644 --- a/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx +++ b/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx @@ -17,7 +17,7 @@ type Story = StoryObj; export const Default: Story = { render: () => { - const { ref, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); + const { isOpen, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); const { selectedOption, selectSortOption } = useSortOption(PRODUCT_SORT_OPTIONS[0]); useEffect(() => { @@ -25,7 +25,7 @@ export const Default: Story = { }, []); return ( - + { const tabRef = useRef(null); const { selectedOption, selectSortOption } = useSortOption(REVIEW_SORT_OPTIONS[0]); - const { ref, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); + const { isOpen, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); const [activeSheet, setActiveSheet] = useState<'registerReview' | 'sortOption'>('sortOption'); const { gaEvent } = useGA(); @@ -136,7 +136,7 @@ export const ProductDetailPage = () => { /> - + {activeSheet === 'registerReview' ? ( { const { category } = useParams(); const productListRef = useRef(null); - const { ref, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); + const { isOpen, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); const { selectedOption, selectSortOption } = useSortOption(PRODUCT_SORT_OPTIONS[0]); const { reset } = useQueryErrorResetBoundary(); const { gaEvent } = useGA(); @@ -68,7 +68,7 @@ export const ProductListPage = () => { - + ('sortOption'); const { selectedOption, selectSortOption } = useSortOption(RECIPE_SORT_OPTIONS[0]); - const { ref, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); + const { isOpen, isClosing, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); const { reset } = useQueryErrorResetBoundary(); const { gaEvent } = useGA(); @@ -72,7 +72,7 @@ export const RecipePage = () => { /> - + {activeSheet === 'sortOption' ? ( Date: Wed, 18 Oct 2023 14:03:35 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=95=95=EC=B6=95=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 76abadcd4..358f1fe4b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,6 +16,7 @@ "@fun-eat/design-system": "^0.3.18", "@tanstack/react-query": "^4.32.6", "@tanstack/react-query-devtools": "^4.32.6", + "browser-image-compression": "^2.0.2", "dayjs": "^1.11.9", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -46,7 +47,6 @@ "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", "babel-plugin-styled-components": "^2.1.4", - "browser-image-compression": "^2.0.2", "copy-webpack-plugin": "^11.0.0", "dotenv-webpack": "^8.0.1", "eslint": "^8.44.0", From 9949e1eb3d0bc06629fe50a8e00c8c41a940244b Mon Sep 17 00:00:00 2001 From: hae-on Date: Wed, 18 Oct 2023 14:09:49 +0900 Subject: [PATCH 7/7] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=95=95=EC=B6=95=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReviewRegisterForm/ReviewRegisterForm.tsx | 13 +++++--- frontend/src/hooks/common/useImageUploader.ts | 32 +++++++++++++++++-- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx b/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx index 32207d184..6c422c68d 100644 --- a/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx +++ b/frontend/src/components/Review/ReviewRegisterForm/ReviewRegisterForm.tsx @@ -11,7 +11,7 @@ import { ImageUploader, SvgIcon } from '@/components/Common'; import { ProductOverviewItem } from '@/components/Product'; import { MIN_DISPLAYED_TAGS_LENGTH } from '@/constants'; import { useFormData, useImageUploader, useScroll } from '@/hooks/common'; -import { useReviewFormActionContext, useReviewFormValueContext } from '@/hooks/context'; +import { useReviewFormActionContext, useReviewFormValueContext, useToastActionContext } from '@/hooks/context'; import { useProductDetailQuery } from '@/hooks/queries/product'; import { useReviewRegisterFormMutation } from '@/hooks/queries/review'; import type { ReviewRequest } from '@/types/review'; @@ -29,10 +29,11 @@ interface ReviewRegisterFormProps { const ReviewRegisterForm = ({ productId, targetRef, closeReviewDialog, initTabMenu }: ReviewRegisterFormProps) => { const { scrollToPosition } = useScroll(); - const { previewImage, imageFile, uploadImage, deleteImage } = useImageUploader(); + const { isImageUploading, previewImage, imageFile, uploadImage, deleteImage } = useImageUploader(); const reviewFormValue = useReviewFormValueContext(); const { resetReviewFormValue } = useReviewFormActionContext(); + const { toast } = useToastActionContext(); const { data: productDetail } = useProductDetailQuery(productId); const { mutate, isLoading } = useReviewRegisterFormMutation(productId); @@ -41,7 +42,8 @@ const ReviewRegisterForm = ({ productId, targetRef, closeReviewDialog, initTabMe reviewFormValue.rating > MIN_RATING_SCORE && reviewFormValue.tagIds.length >= MIN_SELECTED_TAGS_COUNT && reviewFormValue.tagIds.length <= MIN_DISPLAYED_TAGS_LENGTH && - reviewFormValue.content.length > MIN_CONTENT_LENGTH; + reviewFormValue.content.length > MIN_CONTENT_LENGTH && + !isImageUploading; const formData = useFormData({ imageKey: 'image', @@ -64,15 +66,16 @@ const ReviewRegisterForm = ({ productId, targetRef, closeReviewDialog, initTabMe resetAndCloseForm(); initTabMenu(); scrollToPosition(targetRef); + toast.success('📝 리뷰가 등록 됐어요'); }, onError: (error) => { resetAndCloseForm(); if (error instanceof Error) { - alert(error.message); + toast.error(error.message); return; } - alert('리뷰 등록을 다시 시도해주세요'); + toast.error('리뷰 등록을 다시 시도해주세요'); }, }); }; diff --git a/frontend/src/hooks/common/useImageUploader.ts b/frontend/src/hooks/common/useImageUploader.ts index 18dfea459..cc093517d 100644 --- a/frontend/src/hooks/common/useImageUploader.ts +++ b/frontend/src/hooks/common/useImageUploader.ts @@ -1,23 +1,50 @@ +import imageCompression from 'browser-image-compression'; import { useState } from 'react'; import { useToastActionContext } from '../context'; const isImageFile = (file: File) => file.type !== 'image/png' && file.type !== 'image/jpeg'; +const options = { + maxSizeMB: 1, + maxWidthOrHeight: 1920, + useWebWorker: true, +}; + const useImageUploader = () => { const { toast } = useToastActionContext(); const [imageFile, setImageFile] = useState(null); + const [isImageUploading, setIsImageUploading] = useState(false); const [previewImage, setPreviewImage] = useState(''); - const uploadImage = (imageFile: File) => { + const uploadImage = async (imageFile: File) => { if (isImageFile(imageFile)) { toast.error('이미지 파일만 업로드 가능합니다.'); return; } setPreviewImage(URL.createObjectURL(imageFile)); - setImageFile(imageFile); + + try { + setIsImageUploading(true); + + const compressedFile = await imageCompression(imageFile, options); + const compressedImageFilePromise = imageCompression.getFilefromDataUrl( + await imageCompression.getDataUrlFromFile(compressedFile), + compressedFile.name + ); + compressedImageFilePromise + .then((result) => { + setImageFile(result); + }) + .then(() => { + setIsImageUploading(false); + toast.success('이미지가 성공적으로 등록 됐습니다'); + }); + } catch (error) { + console.log(error); + } }; const deleteImage = () => { @@ -27,6 +54,7 @@ const useImageUploader = () => { }; return { + isImageUploading, previewImage, imageFile, uploadImage,