From 21aed370ced8a40fa3a0cc7e5c54bf87a7035e5a Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 19 Feb 2024 18:18:53 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feature-078:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=ED=9B=84=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=B5=9C?= =?UTF-8?q?=EC=8B=A0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useUpdateCategories.ts | 14 +++++++++++++- src/pages/SignInPage.tsx | 15 +++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/hooks/useUpdateCategories.ts b/src/hooks/useUpdateCategories.ts index 50e8fd9..c54b9d5 100644 --- a/src/hooks/useUpdateCategories.ts +++ b/src/hooks/useUpdateCategories.ts @@ -6,9 +6,21 @@ import { useSetRecoilState } from 'recoil'; const useUpdateCategories = () => { const setCategories = useSetRecoilState(categoryState); const { initializeCategory } = handleCategory(); + let failCount = 0; const updateCategories = async () => { await getCategories() - .then((res) => setCategories(initializeCategory(res.result))) + .then((res) => { + if (res.result === undefined) { + failCount++; + if (failCount >= 3) { + alert('카테고리를 가져오는 도중 오류가 발생했습니다.'); + return; + } + updateCategories(); + return; + } + setCategories(initializeCategory(res.result)); + }) .catch((err) => console.log(err)); }; diff --git a/src/pages/SignInPage.tsx b/src/pages/SignInPage.tsx index b5a94e8..8dce99b 100644 --- a/src/pages/SignInPage.tsx +++ b/src/pages/SignInPage.tsx @@ -12,7 +12,7 @@ import smallLogo from '@/assets/logo-dark.png'; import lineImg from '@/assets/line_img.png'; import errorImg from '@/assets/Error.png'; import signupImg from '@/assets/before-login.png'; -import ImageSlider from "@/components/ImageSlider"; +import ImageSlider from '@/components/ImageSlider'; import NaverLogoImage from '@/assets/naver-logo.png'; import KakaoLogoImage from '@/assets/kakao-logo.png'; @@ -22,7 +22,6 @@ import { LoginRequest } from '@/models/user'; import { userTokenState } from '@/stores/user'; import { BlurBackground } from '@/styles/modals/common.style'; -import useUpdateCategories from '@/hooks/useUpdateCategories'; const SignInPage: React.FC = () => { const navigate = useNavigate(); @@ -31,7 +30,6 @@ const SignInPage: React.FC = () => { const [isOpenErrorModal, setIsOpenErrorModal] = useState(false); const [isOpenSignUpModal, setIsOpenSignUpModal] = useState(false); const setUserToken = useSetRecoilState(userTokenState); - const { updateCategories } = useUpdateCategories(); const [loginInfo, setLoginInfo] = useState({ email: '', @@ -64,10 +62,11 @@ const SignInPage: React.FC = () => { const handleClickLoginButton = async () => { try { - const { token } = (await loginAPI(loginInfo)).data.result; - await updateCategories(); - setUserToken(token); - navigate('/'); + const res = await loginAPI(loginInfo); + if (res.data.success) { + setUserToken(res.data.result.token); + navigate('/'); + } } catch (error) { if (error instanceof AxiosError) { const { message } = error.response?.data as APIBaseResponse; @@ -223,7 +222,7 @@ const SignInPage: React.FC = () => { - + {isOpenErrorModal && ( From 912d7ba9b7b927dbd15142f02480034144b5971b Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 19 Feb 2024 18:55:23 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feature-078:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=88=98=EC=A0=95=20=EC=97=AC=EB=9F=AC=20=EA=B0=9C?= =?UTF-8?q?=20=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/category/EditCategoryName.tsx | 4 +- src/components/layout/sideBar/SubCategory.tsx | 45 +++++++----- src/components/layout/sideBar/TopCategory.tsx | 73 ++++++++++--------- src/components/layout/sideBar/UserMode.tsx | 10 ++- src/components/modals/AddCategoryModal.tsx | 1 + src/styles/category/EditCategoryName.style.ts | 1 + src/utils/handleEdit.ts | 9 ++- 7 files changed, 80 insertions(+), 63 deletions(-) diff --git a/src/components/category/EditCategoryName.tsx b/src/components/category/EditCategoryName.tsx index c33e9e2..f434873 100644 --- a/src/components/category/EditCategoryName.tsx +++ b/src/components/category/EditCategoryName.tsx @@ -9,7 +9,9 @@ interface IEditCategoryNameProps { categoryId: number; edit: string; setEdit: React.Dispatch>; - setIsEditing: React.Dispatch>; + setIsEditing: React.Dispatch< + React.SetStateAction<{ activated: boolean; categoryId: number }> + >; } const EditCategoryName = ({ diff --git a/src/components/layout/sideBar/SubCategory.tsx b/src/components/layout/sideBar/SubCategory.tsx index 0a1d891..16350e3 100644 --- a/src/components/layout/sideBar/SubCategory.tsx +++ b/src/components/layout/sideBar/SubCategory.tsx @@ -10,10 +10,13 @@ import EditCategoryName from '@/components/category/EditCategoryName'; interface ISubCategoryProps { topId: number; subId: number; - categoryId: number; - name: string; - setIsDeleteModalOpen: React.Dispatch>; + subFolder: ISubFolderProps; grabedCategory: React.MutableRefObject; + isEditing: { activated: boolean; categoryId: number }; + setIsEditing: React.Dispatch< + React.SetStateAction<{ activated: boolean; categoryId: number }> + >; + setIsDeleteModalOpen: React.Dispatch>; putCategoryFolder: () => void; setCategoryId: React.Dispatch>; } @@ -21,17 +24,17 @@ interface ISubCategoryProps { const SubCategory = ({ topId, subId, - categoryId, - name, - setIsDeleteModalOpen, + subFolder, grabedCategory, + isEditing, + setIsEditing, + setIsDeleteModalOpen, putCategoryFolder, setCategoryId, }: ISubCategoryProps) => { const [subFolderOptionModalOpen, setSubFolderOptionModalOpen] = useState(false); - const [isEditing, setIsEditing] = useState(false); - const [edit, setEdit] = useState(name); + const [edit, setEdit] = useState(subFolder.name); const [beforeEdit, setBeforeEdit] = useState(edit); const [subFolderOptionModalRef] = useOutsideClick(() => @@ -43,15 +46,15 @@ const SubCategory = ({ const handleOptionClick = (e: React.MouseEvent, option: string) => { e.stopPropagation(); if (option === '수정') { - setIsEditing(true); + setIsEditing({ activated: true, categoryId: subFolder.categoryId }); setBeforeEdit(edit); } else if (option === '삭제') { - if (name === '기타') { + if (subFolder.name === '기타') { alert(`'기타' 폴더는 삭제할 수 없습니다.`); setSubFolderOptionModalOpen(false); return; } - setCategoryId(categoryId); + setCategoryId(subFolder.categoryId); setIsDeleteModalOpen(true); } setSubFolderOptionModalOpen(false); @@ -65,8 +68,8 @@ const SubCategory = ({ const handleDragStart = () => (grabedCategory.current = { - categoryId: categoryId, - name, + categoryId: subFolder.categoryId, + name: subFolder.name, topCategoryId: topId, }); @@ -79,10 +82,10 @@ const SubCategory = ({ onDragStart={handleDragStart} onDragEnd={putCategoryFolder} > - {isEditing ? ( + {isEditing.activated && isEditing.categoryId === subFolder.categoryId ? (
{edit} - - - + {!isEditing.activated && ( + + + + )} {subFolderOptionModalOpen && (
diff --git a/src/components/category/Card.tsx b/src/components/category/Card.tsx index 3cc084f..099e02f 100644 --- a/src/components/category/Card.tsx +++ b/src/components/category/Card.tsx @@ -33,6 +33,7 @@ const Card: React.FC = ({ const [selectedCategoryId, setSelectedCategoryId] = useState( category.length ? category[0].categoryId : -1, ); + const [startSelect, setStartSelect] = useState(false); const onFileClickWithProps = (categoryId: number, categoryName?: string) => { setSelectedCategoryId(categoryId); @@ -76,6 +77,8 @@ const Card: React.FC = ({ diff --git a/src/components/layout/sideBar/SubCategory.tsx b/src/components/layout/sideBar/SubCategory.tsx index 16350e3..e3f4b78 100644 --- a/src/components/layout/sideBar/SubCategory.tsx +++ b/src/components/layout/sideBar/SubCategory.tsx @@ -46,6 +46,11 @@ const SubCategory = ({ const handleOptionClick = (e: React.MouseEvent, option: string) => { e.stopPropagation(); if (option === '수정') { + if (subFolder.name === '기타') { + alert(`'기타' 폴더는 수정할 수 없습니다.`); + setSubFolderOptionModalOpen(false); + return; + } setIsEditing({ activated: true, categoryId: subFolder.categoryId }); setBeforeEdit(edit); } else if (option === '삭제') { diff --git a/src/components/modals/AddCategoryModal.tsx b/src/components/modals/AddCategoryModal.tsx index bcac278..a07c012 100644 --- a/src/components/modals/AddCategoryModal.tsx +++ b/src/components/modals/AddCategoryModal.tsx @@ -38,8 +38,8 @@ const AddCategoryModal = ({ const [isFocused, setIsFocused] = useState(false); const categoryNameRegex = /^[a-zA-Z0-9가-힣\s]*$/; - const testCategoryNameRegex = categoryNameRegex.test(categoryName); - const addEnabled = categoryName.length > 0 && testCategoryNameRegex; + const checkCategoryNameRegex = categoryNameRegex.test(categoryName); + const addEnabled = categoryName.length > 0 && checkCategoryNameRegex; const onCloseModal = () => { isTopCategoryModalOpen @@ -54,6 +54,10 @@ const AddCategoryModal = ({ editText(e, setCategoryName); const addCategory = async (e: React.MouseEvent) => { + if (categoryName === '기타') { + alert(`'기타' 이름은 사용하실 수 없어요`); + return; + } const response = isTopCategoryModalOpen ? await postTopCategroy(categoryName) : await postSubCategroy(categoryName, topCategoryId); @@ -101,7 +105,7 @@ const AddCategoryModal = ({ /10(공백포함) - {!testCategoryNameRegex && ( + {!checkCategoryNameRegex && ( *아쉽지만,이모티콘은 사용할 수 없어요 diff --git a/src/styles/category/Card.style.ts b/src/styles/category/Card.style.ts index 73f64db..abfae83 100644 --- a/src/styles/category/Card.style.ts +++ b/src/styles/category/Card.style.ts @@ -30,14 +30,12 @@ export const DropdownWrap = styled.div` width: 40px; height: 40px; border-radius: 8px; - cursor: pointer; - - &.selected { - background-color: ${(props) => props.theme.color.green400}; - } + cursor: default; + background-color: ${theme.color.gray100}; - &.not-selected { - background-color: ${(props) => props.theme.color.gray200}; + &.start-select { + cursor: pointer; + background-color: ${theme.color.green400}; } &.disabled svg { From d3b9eb2b503839b2926ec35eb3fe46058f240a65 Mon Sep 17 00:00:00 2001 From: gs0428 Date: Mon, 19 Feb 2024 19:44:03 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feature-078:=20alert->createToast=EB=A1=9C?= =?UTF-8?q?=20=EB=AA=A8=EB=91=90=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/NicknameModal.tsx | 279 +++++++++--------- .../SummaryDetailBox/SummaryDetailBox.tsx | 8 +- .../SummaryScriptBox/ToolBox/ToolBox.tsx | 8 +- src/components/layout/footer/SendEmail.tsx | 4 +- src/components/layout/sideBar/SubCategory.tsx | 6 +- src/components/layout/sideBar/UserMode.tsx | 4 +- src/components/modals/AddCategoryModal.tsx | 4 +- src/hooks/useCreateToast.ts | 13 + src/hooks/useMoveCategory.ts | 6 +- src/hooks/useUpdateCategories.ts | 4 +- src/pages/CategoryPage.tsx | 4 +- src/pages/HomePage.tsx | 10 +- src/pages/SignUpPage.tsx | 12 +- 13 files changed, 185 insertions(+), 177 deletions(-) create mode 100644 src/hooks/useCreateToast.ts diff --git a/src/components/NicknameModal.tsx b/src/components/NicknameModal.tsx index 7a45e92..adbc6f2 100644 --- a/src/components/NicknameModal.tsx +++ b/src/components/NicknameModal.tsx @@ -6,14 +6,16 @@ import nameImg from '@/assets/name.png'; import { BlurBackground } from '@/styles/modals/common.style'; import { userInfoState } from '@/stores/user'; import { useSetRecoilState } from 'recoil'; +import useCreateToast from '@/hooks/useCreateToast'; const NicknameModal = () => { - const [inputCount, setInputCount] = useState(0); - const [name, setName] = useState(""); - - const setUserInfo = useSetRecoilState(userInfoState); + const [inputCount, setInputCount] = useState(0); + const [name, setName] = useState(''); + const { createToast } = useCreateToast(); - const refreshMyInfo = async () => { + const setUserInfo = useSetRecoilState(userInfoState); + + const refreshMyInfo = async () => { try { const { result } = (await getMyInfoAPI()).data; @@ -21,100 +23,91 @@ const NicknameModal = () => { } catch (e) { console.error(e); } - } + }; - const onChangeName = (e: React.ChangeEvent) => { - const target = e.currentTarget; - if (target.value.length > 7) { - target.value = target.value.slice(0, 7); - } - setName(target.value); - setInputCount( - target.value.replace(/[\0-\x7f]|([0-\u07ff]|(.))/g, "$&$1$2").length - ); - setInputCount(target.value.length); - }; - - const onApply = () => { - if (name) { - // 서버에 데이터 전송 - onRegisterNicknameInfo(); - } else { - alert('입력값을 확인해주세요.'); - } - }; - - const onRegisterNicknameInfo = async () => { - try { - const response = (await nickNameAPI({ - nick_name : name, - })).data - refreshMyInfo(); - console.log(response); - } catch (err) { - console.log(err); - } - }; - - return ( - - - -
- signup -

어떤 이름으로 불러드릴까요?

- - vino에 오신걸 환영합니다! 원하시는 이름으로 불러드릴게요 - -
- - - - - - - {inputCount} - - /7(공백포함) - - - {name ? ( - - 등록하기 - ) : ( - ) - } -
-
+ const onChangeName = (e: React.ChangeEvent) => { + const target = e.currentTarget; + if (target.value.length > 7) { + target.value = target.value.slice(0, 7); + } + setName(target.value); + setInputCount( + target.value.replace(/[\0-\x7f]|([0-\u07ff]|(.))/g, '$&$1$2').length, ); + setInputCount(target.value.length); + }; + + const onApply = () => { + if (name) { + // 서버에 데이터 전송 + onRegisterNicknameInfo(); + } else { + createToast('입력값을 확인해주세요.'); + } + }; + + const onRegisterNicknameInfo = async () => { + try { + const response = ( + await nickNameAPI({ + nick_name: name, + }) + ).data; + refreshMyInfo(); + console.log(response); + } catch (err) { + console.log(err); + } }; - - export default NicknameModal; - const ModalDiv = styled.div` + return ( + + +
+ signup +

어떤 이름으로 불러드릴까요?

+ + vino에 오신걸 환영합니다! 원하시는 이름으로 불러드릴게요 + +
+ + + + + {inputCount} + /7(공백포함) + + + {name ? ( + + 등록하기 + + ) : ( + + )} +
+
+ ); +}; + +export default NicknameModal; + +const ModalDiv = styled.div` padding: 40px 50px; display: flex; flex-direction: column; @@ -136,52 +129,52 @@ const NicknameModal = () => { `; const InputBox = styled.input` - width: 202px; - height: 56px; - background-color: #F3F3F3; - padding: 0px 0px 0px 20px; - display: flex; - align-items: center; - justify-content: center; - gap: 20px; - flex: 1 0 0; - font-style: normal; - border: none; - border-radius: 12px; - color: var(--Main, #1E1E1E); - font-family: Pretendard; - ${theme.typography.Body1}; - &:focus { - outline: none; - } - - &::placeholder { + width: 202px; + height: 56px; + background-color: #f3f3f3; + padding: 0px 0px 0px 20px; + display: flex; + align-items: center; + justify-content: center; + gap: 20px; + flex: 1 0 0; + font-style: normal; + border: none; + border-radius: 12px; + color: var(--Main, #1e1e1e); + font-family: Pretendard; + ${theme.typography.Body1}; + &:focus { + outline: none; + } + + &::placeholder { color: #bbb; ${theme.typography.Body1}; } `; const SucButton = styled.button` - width: 100%; - height: 56px; - border: none; - border-radius: 12px; - background-color : #1E1E1E; - color: #fff; - text-align: center; - ${theme.typography.Body1}; - cursor: pointer; + width: 100%; + height: 56px; + border: none; + border-radius: 12px; + background-color: #1e1e1e; + color: #fff; + text-align: center; + ${theme.typography.Body1}; + cursor: pointer; `; const Button = styled.button` - width: 100%; - height: 56px; - border: none; - border-radius: 12px; - background-color : #F3F3F3; - color: #BBBBBB; - text-align: center; - ${theme.typography.Body1}; + width: 100%; + height: 56px; + border: none; + border-radius: 12px; + background-color: #f3f3f3; + color: #bbbbbb; + text-align: center; + ${theme.typography.Body1}; `; const InputNickNameMessage = styled.span` @@ -195,14 +188,14 @@ const InputNickNameLength = styled.span` `; const TextDiv = styled.div` - position : relative; - width : 600px; - height : 56px; - align-items: center; - justify-content: center; - background-color: #F3F3F3; - display: flex; - flex-direction: row; - border-radius: 12px; - margin-top: 48px; - ` \ No newline at end of file + position: relative; + width: 600px; + height: 56px; + align-items: center; + justify-content: center; + background-color: #f3f3f3; + display: flex; + flex-direction: row; + border-radius: 12px; + margin-top: 48px; +`; diff --git a/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx b/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx index 8358d15..0c9e9ed 100644 --- a/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx +++ b/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx @@ -12,7 +12,6 @@ import { summaryVideoState, summaryVideoTimeState, } from '@/stores/summary'; -import { toastListState } from '@/stores/toast'; import { DetailBox } from '@/styles/SummaryPage'; @@ -21,6 +20,7 @@ import { formatDate } from '@/utils/date'; import { CategorySelectBox } from './CategorySelectBox'; import { NoteBox } from './NoteBox'; import { DescriptionBox } from './DescriptionBox'; +import useCreateToast from '@/hooks/useCreateToast'; type Props = { onRefresh: () => void; @@ -29,6 +29,7 @@ type Props = { const SummaryDetailBox = ({ onRefresh }: Props) => { const player = useRef(); + const { createToast } = useCreateToast(); const summaryVideo = useRecoilValue(summaryVideoState) as IVideo; const summaryUpdateVideo = useRecoilValue(summaryUpdateVideoState); const setSummaryVideoTime = useSetRecoilState(summaryVideoTimeState); @@ -36,16 +37,11 @@ const SummaryDetailBox = ({ onRefresh }: Props) => { const [playSubHeadingId, setPlaySubHeadingId] = useRecoilState( summaryPlaySubHeadingIdState, ); - const [toastList, setToastList] = useRecoilState(toastListState); const subHeading = isEditingView ? summaryUpdateVideo?.subHeading || [] : summaryVideo.subHeading; - const createToast = (content: string) => { - setToastList([...toastList, { id: Date.now(), content }]); - }; - const handleSelectCategory = async (category_id: number, name?: string) => { try { await updateVideoCategoryIdAPI(category_id, { diff --git a/src/components/SummaryPage/SummaryScriptBox/ToolBox/ToolBox.tsx b/src/components/SummaryPage/SummaryScriptBox/ToolBox/ToolBox.tsx index 1967139..f219031 100644 --- a/src/components/SummaryPage/SummaryScriptBox/ToolBox/ToolBox.tsx +++ b/src/components/SummaryPage/SummaryScriptBox/ToolBox/ToolBox.tsx @@ -13,11 +13,11 @@ import { summaryUpdateVideoState, summaryVideoState, } from '@/stores/summary'; -import { toastListState } from '@/stores/toast'; import Indicator from './Indicator'; import { SearchKeyword } from './SearchKeyword'; import { ChangeKeyword } from './ChangeKeyword'; +import useCreateToast from '@/hooks/useCreateToast'; type Props = { onRefresh: () => void; @@ -33,14 +33,10 @@ const ToolBox = ({ onRefresh, onChangeKeyword }: Props) => { const [isEditingView, setIsEditingView] = useRecoilState( summaryIsEditingViewState, ); - const [toastList, setToastList] = useRecoilState(toastListState); + const { createToast } = useCreateToast(); const [originalSummary, setOriginalSummary] = useState(null); - const createToast = (content: string) => { - setToastList([...toastList, { id: Date.now(), content }]); - }; - const handleClickModifyIcon = () => { setPlaySubHeadingId(-1); setIsEditingView(true); diff --git a/src/components/layout/footer/SendEmail.tsx b/src/components/layout/footer/SendEmail.tsx index 08520a0..85522c4 100644 --- a/src/components/layout/footer/SendEmail.tsx +++ b/src/components/layout/footer/SendEmail.tsx @@ -4,10 +4,12 @@ import { useState } from 'react'; import SendEmailImage from '@/assets/mail.png'; import SuccessSendEmailImage from '@/assets/success-mail.png'; import { postFeedback } from '@/apis/feedback'; +import useCreateToast from '@/hooks/useCreateToast'; const SendEmail = () => { const [feedback, setFeedback] = useState(''); const [successSend, setSuccessSend] = useState(false); + const { createToast } = useCreateToast(); const handleInputFeedback = (e: React.ChangeEvent) => setFeedback(e.target.value); @@ -19,7 +21,7 @@ const SendEmail = () => { setSuccessSend(true); return; } - alert('피드백을 전송하는 과정에서 오류가 발생했습니다.'); + createToast('피드백을 전송하는 과정에서 오류가 발생했습니다.'); }; return ( diff --git a/src/components/layout/sideBar/SubCategory.tsx b/src/components/layout/sideBar/SubCategory.tsx index e3f4b78..dfcca2c 100644 --- a/src/components/layout/sideBar/SubCategory.tsx +++ b/src/components/layout/sideBar/SubCategory.tsx @@ -6,6 +6,7 @@ import Option from './Option'; import handleDrag from '@/utils/handleDrag'; import { ISubFolderProps } from 'types/category'; import EditCategoryName from '@/components/category/EditCategoryName'; +import useCreateToast from '@/hooks/useCreateToast'; interface ISubCategoryProps { topId: number; @@ -36,6 +37,7 @@ const SubCategory = ({ useState(false); const [edit, setEdit] = useState(subFolder.name); const [beforeEdit, setBeforeEdit] = useState(edit); + const { createToast } = useCreateToast(); const [subFolderOptionModalRef] = useOutsideClick(() => setSubFolderOptionModalOpen(false), @@ -47,7 +49,7 @@ const SubCategory = ({ e.stopPropagation(); if (option === '수정') { if (subFolder.name === '기타') { - alert(`'기타' 폴더는 수정할 수 없습니다.`); + createToast(`'기타' 폴더는 수정할 수 없습니다.`); setSubFolderOptionModalOpen(false); return; } @@ -55,7 +57,7 @@ const SubCategory = ({ setBeforeEdit(edit); } else if (option === '삭제') { if (subFolder.name === '기타') { - alert(`'기타' 폴더는 삭제할 수 없습니다.`); + createToast(`'기타' 폴더는 삭제할 수 없습니다.`); setSubFolderOptionModalOpen(false); return; } diff --git a/src/components/layout/sideBar/UserMode.tsx b/src/components/layout/sideBar/UserMode.tsx index 3580510..429c3e6 100644 --- a/src/components/layout/sideBar/UserMode.tsx +++ b/src/components/layout/sideBar/UserMode.tsx @@ -13,6 +13,7 @@ import { IFolderProps, ISubFolderProps } from 'types/category'; import useMoveCategory from '@/hooks/useMoveCategory'; import { deleteCategory } from '@/apis/category'; import useUpdateCategories from '@/hooks/useUpdateCategories'; +import useCreateToast from '@/hooks/useCreateToast'; const UserMode = () => { const isTopCategoryModalOpen = useRecoilValue(topCategoryModalState); @@ -26,6 +27,7 @@ const UserMode = () => { const [isSubCategoryModalOpen, setIsSubCategoryModalOpen] = useState(false); const grabedCategory = useRef(undefined); const dropedCategory = useRef(undefined); + const { createToast } = useCreateToast(); const [isEditing, setIsEditing] = useState({ activated: false, categoryId: 0, @@ -67,7 +69,7 @@ const UserMode = () => { grabedCategory.current = undefined; dropedCategory.current = undefined; } else { - alert('카테고리를 옮기는데 오류가 발생했습니다.'); + createToast('카테고리를 옮기는데 오류가 발생했습니다.'); } }; return ( diff --git a/src/components/modals/AddCategoryModal.tsx b/src/components/modals/AddCategoryModal.tsx index a07c012..fccc42f 100644 --- a/src/components/modals/AddCategoryModal.tsx +++ b/src/components/modals/AddCategoryModal.tsx @@ -14,6 +14,7 @@ import { ICommonModalProps } from 'types/modal'; import handleEdit from '@/utils/handleEdit'; import { postSubCategroy, postTopCategroy } from '@/apis/category'; import useUpdateCategories from '@/hooks/useUpdateCategories'; +import useCreateToast from '@/hooks/useCreateToast'; interface IAddTopCategoryModalProps extends ICommonModalProps { isTopCategoryModalOpen: boolean; @@ -32,6 +33,7 @@ const AddCategoryModal = ({ setTo, }: IAddTopCategoryModalProps) => { const setIsTopCategoryModalOpen = useSetRecoilState(topCategoryModalState); + const { createToast } = useCreateToast(); const { updateCategories } = useUpdateCategories(); const { editText } = handleEdit(); @@ -55,7 +57,7 @@ const AddCategoryModal = ({ const addCategory = async (e: React.MouseEvent) => { if (categoryName === '기타') { - alert(`'기타' 이름은 사용하실 수 없어요`); + createToast(`'기타' 이름은 사용하실 수 없어요`); return; } const response = isTopCategoryModalOpen diff --git a/src/hooks/useCreateToast.ts b/src/hooks/useCreateToast.ts new file mode 100644 index 0000000..ff7b918 --- /dev/null +++ b/src/hooks/useCreateToast.ts @@ -0,0 +1,13 @@ +import { toastListState } from '@/stores/toast'; +import { useRecoilState } from 'recoil'; + +const useCreateToast = () => { + const [toastList, setToastList] = useRecoilState(toastListState); + + const createToast = (content: string) => { + setToastList([...toastList, { id: Date.now(), content }]); + }; + return { createToast }; +}; + +export default useCreateToast; diff --git a/src/hooks/useMoveCategory.ts b/src/hooks/useMoveCategory.ts index e1d61f2..4557cda 100644 --- a/src/hooks/useMoveCategory.ts +++ b/src/hooks/useMoveCategory.ts @@ -6,9 +6,11 @@ import { import { useNavigate } from 'react-router-dom'; import { ISubFolderProps } from 'types/category'; import useUpdateCategories from './useUpdateCategories'; +import useCreateToast from './useCreateToast'; const useMoveCategory = () => { const navigate = useNavigate(); + const { createToast } = useCreateToast(); const { updateCategories } = useUpdateCategories(); const subToOtherTop = async ( @@ -16,7 +18,7 @@ const useMoveCategory = () => { grabedCategory: React.MutableRefObject, ) => { if (grabedCategory.current?.name === '기타') { - alert(`'기타' 폴더는 이동할 수 없습니다.`); + createToast(`'기타' 폴더는 이동할 수 없습니다.`); return; } // 하위에 있는 폴더를 다른 상위 폴더로 이동하는 기능 @@ -36,7 +38,7 @@ const useMoveCategory = () => { grabedCategory: React.MutableRefObject, ) => { if (grabedCategory.current?.name === '기타') { - alert(`'기타' 폴더는 이동할 수 없습니다.`); + createToast(`'기타' 폴더는 이동할 수 없습니다.`); return; } // 하위에 있는 폴더를 상위로 올리는 기능 diff --git a/src/hooks/useUpdateCategories.ts b/src/hooks/useUpdateCategories.ts index c54b9d5..ea358fa 100644 --- a/src/hooks/useUpdateCategories.ts +++ b/src/hooks/useUpdateCategories.ts @@ -2,10 +2,12 @@ import { getCategories } from '@/apis/category'; import { categoryState } from '@/stores/category'; import handleCategory from '@/utils/handleCategory'; import { useSetRecoilState } from 'recoil'; +import useCreateToast from './useCreateToast'; const useUpdateCategories = () => { const setCategories = useSetRecoilState(categoryState); const { initializeCategory } = handleCategory(); + const { createToast } = useCreateToast(); let failCount = 0; const updateCategories = async () => { await getCategories() @@ -13,7 +15,7 @@ const useUpdateCategories = () => { if (res.result === undefined) { failCount++; if (failCount >= 3) { - alert('카테고리를 가져오는 도중 오류가 발생했습니다.'); + createToast('카테고리를 가져오는 도중 오류가 발생했습니다.'); return; } updateCategories(); diff --git a/src/pages/CategoryPage.tsx b/src/pages/CategoryPage.tsx index e28de6b..15eab88 100644 --- a/src/pages/CategoryPage.tsx +++ b/src/pages/CategoryPage.tsx @@ -14,6 +14,7 @@ import { putVideoToOtherCategory } from '@/apis/category'; import handleVideo from '@/utils/handleVideo'; import CategoryPageSkeleton from '@/components/skeleton/CategoryPageSkeleton'; import DefaultMenu from '@/components/category/DefaultMenu'; +import useCreateToast from '@/hooks/useCreateToast'; const CategoryTitle = React.lazy( () => import('@/components/category/CategoryTitle'), @@ -31,6 +32,7 @@ const CategoryPage = () => { const [checkedVideos, setCheckedVideos] = useState([]); const [selectedTags, setSelectedTags] = useState([]); const categories = useRecoilValue(categoryState); + const { createToast } = useCreateToast(); const toggleRecentRegisterMode = () => setRecentRegisterMode(!recentRegisterMode); @@ -58,7 +60,7 @@ const CategoryPage = () => { setVideos(existVideos); setCheckedVideos([]); } else { - alert('비디오를 삭제하는데 실패했습니다.'); + createToast('비디오를 삭제하는데 실패했습니다.'); } }; diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index a4f855e..0f6b545 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from 'react'; -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { IVideoProps } from 'types/videos'; import { @@ -18,8 +18,8 @@ import { HomePageContainer } from '@/styles/HomepageStyle'; import { userTokenState } from '@/stores/user'; import { recommendationModalState } from '@/stores/modal'; -import { toastListState } from '@/stores/toast'; import { isSideBarOpenState } from '@/stores/ui'; +import useCreateToast from '@/hooks/useCreateToast'; export interface Video { id: string; @@ -35,11 +35,7 @@ const HomePage: React.FC = () => { const isOpenModal = useRecoilValue(recommendationModalState); const [recentVideos, setRecentVideos] = useState([]); const [dummyVideos, setDummyVideos] = useState([]); - const [toastList, setToastList] = useRecoilState(toastListState); - - const createToast = (content: string) => { - setToastList([...toastList, { id: Date.now(), content }]); - }; + const { createToast } = useCreateToast(); const onFileClick = async ( videoId: number, diff --git a/src/pages/SignUpPage.tsx b/src/pages/SignUpPage.tsx index 9575e26..6caae34 100644 --- a/src/pages/SignUpPage.tsx +++ b/src/pages/SignUpPage.tsx @@ -13,6 +13,7 @@ import Calendar from '@/components/Calendar'; import ImageSlider from '@/components/ImageSlider'; import PhoneCheck from '@/components/PhoneCheck'; import { Link } from 'react-router-dom'; +import useCreateToast from '@/hooks/useCreateToast'; const SignUp = () => { const [name, setName] = useState(''); @@ -40,6 +41,7 @@ const SignUp = () => { const [isEmailSuccess, setIsEmailSuccess] = useState(false); const [isOpenOverlapModal, setIsOpenOverlapModal] = useState(false); + const { createToast } = useCreateToast(); const onChangeName = (e: React.ChangeEvent) => { setName(e.target.value); @@ -86,7 +88,7 @@ const SignUp = () => { setPassword(passwordCurrent); if (!passwordRegex.test(passwordCurrent)) { setPasswordMessage( - '*8자 이상으로 입력 *대문자 사용 *숫자 사용 *특수문자 사용' + '*8자 이상으로 입력 *대문자 사용 *숫자 사용 *특수문자 사용', ); setIsPassword(false); } else { @@ -99,14 +101,12 @@ const SignUp = () => { setPasswordCheck(e.target.value); if (e.target.value === '') { setPasswordCheckMessage('비밀번호를 재입력해주세요'); - } - else if (password && e.target.value !== password) { + } else if (password && e.target.value !== password) { setMismatchError(true); setPasswordCheckMessage( '비밀번호가 일치하지 않습니다. 다시 확인해주세요.', ); - } - else { + } else { setMismatchError(false); setPasswordCheckMessage('비밀번호가 일치합니다.'); } @@ -134,7 +134,7 @@ const SignUp = () => { onRegisterUserInfo(); navigate('/sign-up/success'); } else { - alert('입력값을 확인해주세요.'); + createToast('입력값을 확인해주세요.'); } };