From 4c6a3173ef7511c307b6a4de3d7a6a7d7b4b92cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A4=EC=A0=95=EB=AF=BC?= Date: Mon, 13 May 2024 00:13:53 +0900 Subject: [PATCH] =?UTF-8?q?Refactor/#568=20useModal=EC=9D=84=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EB=B6=80=EB=B6=84=EC=9D=84=20overlay?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#569)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: LoginModal을 overlay로 구현 * feat: storybook에 OverlayProvider 적용 * refactor: useModal을 사용한 부분 overlay로 변경 * refactor: useLoginModal 훅 이름 변경 --- frontend/.storybook/preview.tsx | 14 ++--- .../features/auth/hooks/LoginPopUpContext.tsx | 46 ---------------- .../auth/hooks/useLoginModalByError.tsx | 24 +++++++++ .../comments/components/CommentForm.tsx | 28 +++++++--- .../comments/components/CommentList.tsx | 52 +++++++++++-------- .../songs/components/KillingPartTrack.tsx | 29 ++++++----- .../songs/components/KillingPartTrackList.tsx | 27 ++++++---- frontend/src/router.tsx | 5 +- frontend/src/shared/hooks/useFetch.ts | 6 +-- frontend/src/shared/hooks/useMutation.ts | 6 +-- 10 files changed, 118 insertions(+), 119 deletions(-) delete mode 100644 frontend/src/features/auth/hooks/LoginPopUpContext.tsx create mode 100644 frontend/src/features/auth/hooks/useLoginModalByError.tsx diff --git a/frontend/.storybook/preview.tsx b/frontend/.storybook/preview.tsx index 4f5adb4d..c9962b1a 100644 --- a/frontend/.storybook/preview.tsx +++ b/frontend/.storybook/preview.tsx @@ -1,12 +1,12 @@ import { initialize, mswLoader } from 'msw-storybook-addon'; import type { Preview } from '@storybook/react'; -import GlobalStyles from '../src/shared/styles/GlobalStyles'; import { ThemeProvider } from 'styled-components'; -import theme from '../src/shared/styles/theme'; import { BrowserRouter } from 'react-router-dom'; import AuthProvider from '@/features/auth/components/AuthProvider'; -import LoginPopupProvider from '@/features/auth/hooks/LoginPopUpContext'; import handlers from '@/mocks/handlers'; +import { OverlayProvider } from '@/shared/hooks/useOverlay'; +import GlobalStyles from '@/shared/styles/GlobalStyles'; +import theme from '@/shared/styles/theme'; const customViewport = { xxl: { @@ -87,14 +87,14 @@ const preview: Preview = { decorators: [ (Story) => ( - - + + - - + + ), ], diff --git a/frontend/src/features/auth/hooks/LoginPopUpContext.tsx b/frontend/src/features/auth/hooks/LoginPopUpContext.tsx deleted file mode 100644 index 16172bb8..00000000 --- a/frontend/src/features/auth/hooks/LoginPopUpContext.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { createContext, useContext, useState } from 'react'; -import LoginModal from '@/features/auth/components/LoginModal'; -import useModal from '@/shared/components/Modal/hooks/useModal'; -import type { PropsWithChildren } from 'react'; - -interface LoginPopUpContextProps { - popupLoginModal: (errorCode: number) => void; -} - -const LoginPopUpContext = createContext(null); - -export const useLoginPopup = () => { - const contextValue = useContext(LoginPopUpContext); - - if (contextValue === null) throw new Error('LoginPopUpContext에 값이 제공되지 않았습니다.'); - - return contextValue; -}; - -const LoginPopupProvider = ({ children }: PropsWithChildren) => { - const { isOpen, closeModal, openModal } = useModal(false); - const [message, setMessage] = useState('로그인 하시겠습니까?'); - - const popupLoginModal = (errorCode: number) => { - // accessToken 관련 에러 코드 - if ([1000, 1003, 1004].includes(errorCode)) { - setMessage('로그인 정보가 부정확하여 재로그인이 필요합니다.\n다시 로그인하시겠습니까?'); - } - // refreshToken 관련 에러 코드 - if ([1011, 1012, 1007].includes(errorCode)) { - setMessage('로그인 정보가 만료되었습니다.\n다시 로그인하시겠습니까?'); - } - - setMessage('로그인 하시겠습니까?'); - openModal(); - }; - - return ( - - {children} - - - ); -}; - -export default LoginPopupProvider; diff --git a/frontend/src/features/auth/hooks/useLoginModalByError.tsx b/frontend/src/features/auth/hooks/useLoginModalByError.tsx new file mode 100644 index 00000000..06bbe109 --- /dev/null +++ b/frontend/src/features/auth/hooks/useLoginModalByError.tsx @@ -0,0 +1,24 @@ +import { useOverlay } from '@/shared/hooks/useOverlay'; +import LoginModal from '../components/LoginModal'; + +export const useLoginModalByError = () => { + const overlay = useOverlay(); + + const openLoginModalByError = (errorCode: number) => { + overlay.open(({ isOpen, close }) => ( + + )); + }; + + return { openLoginModalByError }; +}; diff --git a/frontend/src/features/comments/components/CommentForm.tsx b/frontend/src/features/comments/components/CommentForm.tsx index 56eb9aba..3fda9f16 100644 --- a/frontend/src/features/comments/components/CommentForm.tsx +++ b/frontend/src/features/comments/components/CommentForm.tsx @@ -5,8 +5,8 @@ import shookshook from '@/assets/icon/shookshook.svg'; import { useAuthContext } from '@/features/auth/components/AuthProvider'; import LoginModal from '@/features/auth/components/LoginModal'; import Avatar from '@/shared/components/Avatar'; -import useModal from '@/shared/components/Modal/hooks/useModal'; import useToastContext from '@/shared/components/Toast/hooks/useToastContext'; +import { useOverlay } from '@/shared/hooks/useOverlay'; import { usePostCommentMutation } from '../queries'; interface CommentFormProps { @@ -16,7 +16,24 @@ interface CommentFormProps { const CommentForm = ({ songId, partId }: CommentFormProps) => { const [newComment, setNewComment] = useState(''); - const { isOpen, closeModal: closeLoginModal, openModal: openLoginModal } = useModal(); + const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); + const overlay = useOverlay(); + + const openLoginModal = () => { + setIsLoginModalOpen(true); + + overlay.open(({ isOpen, close }) => ( + { + setIsLoginModalOpen(false); + close(); + }} + message={'로그인하고 댓글을 작성해 보세요!'} + /> + )); + }; + const { user } = useAuthContext(); const isLoggedIn = !!user; @@ -70,12 +87,7 @@ const CommentForm = ({ songId, partId }: CommentFormProps) => { type="text" onFocus={openLoginModal} placeholder="댓글 추가..." - disabled={isOpen} - /> - )} diff --git a/frontend/src/features/comments/components/CommentList.tsx b/frontend/src/features/comments/components/CommentList.tsx index 1595853a..741ac7b8 100644 --- a/frontend/src/features/comments/components/CommentList.tsx +++ b/frontend/src/features/comments/components/CommentList.tsx @@ -1,12 +1,13 @@ import { styled } from 'styled-components'; import cancelIcon from '@/assets/icon/cancel.svg'; import BottomSheet from '@/shared/components/BottomSheet/BottomSheet'; -import useModal from '@/shared/components/Modal/hooks/useModal'; import Spacing from '@/shared/components/Spacing'; import SRHeading from '@/shared/components/SRHeading'; +import { useOverlay } from '@/shared/hooks/useOverlay'; import { useCommentsQuery } from '../queries'; import Comment from './Comment'; import CommentForm from './CommentForm'; +import type { Comment as CommentType } from '../types/comment.type'; interface CommentListProps { songId: number; @@ -14,7 +15,32 @@ interface CommentListProps { } const CommentList = ({ songId, partId }: CommentListProps) => { - const { isOpen, openModal, closeModal } = useModal(false); + const overlay = useOverlay(); + + const openBottomSheet = ({ comments }: { comments: CommentType[] }) => + overlay.open(({ isOpen, close }) => ( + + +
+ 댓글 {comments.length}개 + +
+ + + {comments.map(({ id, content, createdAt, writerNickname }) => ( + + ))} + + + +
+ )); + const { comments } = useCommentsQuery(songId, partId); if (!comments) { @@ -29,7 +55,7 @@ const CommentList = ({ songId, partId }: CommentListProps) => { 댓글 목록 댓글 {comments.length}개 - + openBottomSheet({ comments })}> {isEmptyComment ? ( ) : ( @@ -40,26 +66,6 @@ const CommentList = ({ songId, partId }: CommentListProps) => { /> )} - - -
- 댓글 {comments.length}개 - -
- - - {comments.map(({ id, content, createdAt, writerNickname }) => ( - - ))} - - - -
); }; diff --git a/frontend/src/features/songs/components/KillingPartTrack.tsx b/frontend/src/features/songs/components/KillingPartTrack.tsx index a1b79a5d..de30e1be 100644 --- a/frontend/src/features/songs/components/KillingPartTrack.tsx +++ b/frontend/src/features/songs/components/KillingPartTrack.tsx @@ -9,12 +9,12 @@ import LoginModal from '@/features/auth/components/LoginModal'; import { deleteMemberParts } from '@/features/member/remotes/memberParts'; import useVideoPlayerContext from '@/features/youtube/hooks/useVideoPlayerContext'; import { useConfirmContext } from '@/shared/components/ConfirmModal/hooks/useConfirmContext'; -import useModal from '@/shared/components/Modal/hooks/useModal'; import useTimerContext from '@/shared/components/Timer/hooks/useTimerContext'; import useToastContext from '@/shared/components/Toast/hooks/useToastContext'; import { GA_ACTIONS, GA_CATEGORIES } from '@/shared/constants/GAEventName'; import sendGAEvent from '@/shared/googleAnalytics/sendGAEvent'; import { useMutation } from '@/shared/hooks/useMutation'; +import { useOverlay } from '@/shared/hooks/useOverlay'; import { toPlayingTimeText } from '@/shared/utils/convertTime'; import copyClipboard from '@/shared/utils/copyClipBoard'; import formatOrdinals from '@/shared/utils/formatOrdinals'; @@ -53,11 +53,8 @@ const KillingPartTrack = ({ partId, }); const { countedTime: currentPlayTime } = useTimerContext(); - const { - isOpen: isLoginModalOpen, - closeModal: closeLoginModal, - openModal: openLoginModal, - } = useModal(); + const overlay = useOverlay(); + const { user } = useAuthContext(); const isLoggedIn = user !== null; @@ -65,6 +62,18 @@ const KillingPartTrack = ({ const playingTime = toPlayingTimeText(start, end); const partLength = end - start; + const openLoginModal = () => { + overlay.open(({ isOpen, close }) => ( + + )); + }; + const copyKillingPartUrl = async () => { sendGAEvent({ action: GA_ACTIONS.COPY_URL, @@ -218,14 +227,6 @@ const KillingPartTrack = ({ {isNowPlayingTrack && (