diff --git a/src/apis/post/queries.ts b/src/apis/post/queries.ts index bdf7f886..1a05776e 100644 --- a/src/apis/post/queries.ts +++ b/src/apis/post/queries.ts @@ -31,6 +31,7 @@ export const useGetPostQuery = (id: number) => useQuery({ queryKey: ['getPost', id], queryFn: () => getPost(id), + retry: false, enabled: typeof id === 'number', select: data => ({ ...data, diff --git a/src/constants/message.ts b/src/constants/message.ts index 272a42a8..626c04cd 100644 --- a/src/constants/message.ts +++ b/src/constants/message.ts @@ -4,3 +4,7 @@ export const VALID_NICKNAME_MESSAGE = { MIN_LENGTH_ERROR: '닉네임은 2자 이상 입력해주세요.', EMPTY_ERROR: '닉네임을 입력해주세요!' } as const + +export const ALERT_MESSAGE = { + LEAVE_PAGE: `사이트에서 나가시겠습니까?\n변경사항이 저장되지 않을 수 있습니다.` +} as const diff --git a/src/hooks/index.ts b/src/hooks/index.ts index bc8c8846..80fbad3a 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -3,3 +3,4 @@ export * from './useAuth' export * from './useModal' export * from './result/useCategoryFilterList' export * from './result/useSelectBoxFilter' +export * from './usePreventLeavePage' diff --git a/src/hooks/usePreventLeavePage.ts b/src/hooks/usePreventLeavePage.ts new file mode 100644 index 00000000..80a000e1 --- /dev/null +++ b/src/hooks/usePreventLeavePage.ts @@ -0,0 +1,36 @@ +import { useRouter } from 'next/router' +import { useEffect } from 'react' +import { ALERT_MESSAGE } from '@constants' + +const handleBeforeUnload = (e: BeforeUnloadEvent) => { + e.returnValue = ALERT_MESSAGE.LEAVE_PAGE + + return ALERT_MESSAGE.LEAVE_PAGE +} + +export const usePreventLeavePage = (isPrevent = true) => { + const router = useRouter() + + useEffect(() => { + const handleBeforeChangeRoute = (url: string) => { + if (router.pathname !== url && !confirm(ALERT_MESSAGE.LEAVE_PAGE)) { + router.events.emit('routeChangeError') + + throw `사이트 변경 취소` + } + } + + if (!isPrevent) { + return + } + + window.addEventListener('beforeunload', handleBeforeUnload) + router.events.on('routeChangeStart', handleBeforeChangeRoute) + + // eslint-disable-next-line consistent-return + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload) + router.events.off('routeChangeStart', handleBeforeChangeRoute) + } + }, [isPrevent]) +} diff --git a/src/pages/post/[postId]/index.tsx b/src/pages/post/[postId]/index.tsx index d6232a31..9e6d6df9 100644 --- a/src/pages/post/[postId]/index.tsx +++ b/src/pages/post/[postId]/index.tsx @@ -46,8 +46,8 @@ const PostDetailPage = ({ postId }: Props): ReactElement => { const updateTradeStatusMutation = useUpdateTradeStatusMutation() const deletePostMutation = useDeletePostMutation(postId) - const [tradeStatus, setTradeStatus] = useState() const router = useRouter() + const [tradeStatus, setTradeStatus] = useState() const tradeStatusDialog = useModal() const deleteModal = useModal() @@ -74,6 +74,12 @@ const PostDetailPage = ({ postId }: Props): ReactElement => { router.replace('/') } + if (getPostQuery.isError) { + router.push('/403') + + return <> + } + return ( <> diff --git a/src/pages/post/index.tsx b/src/pages/post/index.tsx index a5f165a9..b7fe37aa 100644 --- a/src/pages/post/index.tsx +++ b/src/pages/post/index.tsx @@ -11,6 +11,7 @@ import { Divider } from '@offer-ui/react' import type { ImageInfo, InputProps } from '@offer-ui/react' +import { isEqual } from 'lodash' import type { GetServerSideProps } from 'next' import { useRouter } from 'next/router' import type { ReactElement } from 'react' @@ -26,7 +27,7 @@ import { } from '@apis' import { PostForm } from '@components' import { TRADE_TYPES, PRODUCT_CONDITIONS, TRADE_STATUS } from '@constants' -import { useResponsive } from '@hooks' +import { useAuth, usePreventLeavePage, useResponsive } from '@hooks' type PostFormState = Partial< Omit @@ -77,9 +78,14 @@ const PostPage = ({ type, editPostId }: Props): ReactElement => { const createUploadImagesMutation = useCreateUploadImagesMutation() const getCategoriesQuery = useGetCategoriesQuery() const updatePostMutation = useUpdatePostMutation() - const router = useRouter() + const initialPostForm: PostFormState = getPostQuery.data?.postForm || {} const [postForm, setPostForm] = useState({}) + const hasChanged = !isEqual(initialPostForm, postForm) + const canPosting = hasChanged && isCompleteForm(postForm) + const router = useRouter() + const { user } = useAuth() + usePreventLeavePage(hasChanged) const InputSize = useResponsive({ desktop: '278px', @@ -97,7 +103,7 @@ const PostPage = ({ type, editPostId }: Props): ReactElement => { } const handlePostProduct = async () => { - if (!isCompleteForm(postForm)) { + if (!canPosting) { return } @@ -143,10 +149,23 @@ const PostPage = ({ type, editPostId }: Props): ReactElement => { useEffect(() => { if (getPostQuery.data) { + const { seller } = getPostQuery.data + + if (seller.id !== user.id) { + router.push('/403') + return + } + setPostForm(getPostQuery.data.postForm) } }, [getPostQuery.data]) + if (getPostQuery.isError) { + router.push('/403') + + return <> + } + return ( @@ -246,7 +265,7 @@ const PostPage = ({ type, editPostId }: Props): ReactElement => {