From c9a86f78ef6575ffe20eaf6056e852f4c7825f87 Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Sat, 29 Jul 2023 14:18:29 +0900 Subject: [PATCH 01/14] =?UTF-8?q?style:=20input=20hint=20global=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Input/Input.css.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/Input/Input.css.ts b/src/components/Input/Input.css.ts index bf9b0428..54012eb3 100644 --- a/src/components/Input/Input.css.ts +++ b/src/components/Input/Input.css.ts @@ -125,6 +125,10 @@ export const bottomSheetContent = style({ padding: `0 ${space.sm}`, }); +globalStyle(`::-webkit-input-placeholder`, { + color: color.hint, +}); + globalStyle(`${clearButton} > img`, { margin: '0 auto', backgroundPosition: 'center', From 87102b436aa9f60678041404c2d010073add0f05 Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Mon, 31 Jul 2023 23:54:00 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=ED=83=88=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/PreventModal/PreventModal.tsx | 25 ++ src/app/write/hooks/useCustomRouter.ts | 70 ++++++ src/app/write/hooks/useRouteChangeEvents.tsx | 225 ++++++++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 src/app/write/components/PreventModal/PreventModal.tsx create mode 100644 src/app/write/hooks/useCustomRouter.ts create mode 100644 src/app/write/hooks/useRouteChangeEvents.tsx diff --git a/src/app/write/components/PreventModal/PreventModal.tsx b/src/app/write/components/PreventModal/PreventModal.tsx new file mode 100644 index 00000000..185a49e0 --- /dev/null +++ b/src/app/write/components/PreventModal/PreventModal.tsx @@ -0,0 +1,25 @@ +import { Button, Modal } from '@/components'; + +interface Props { + onClose: () => void; + onClick: () => void; +} + +const PreventModal = ({ onClose, onClick }: Props) => { + return ( + + + 페이지를 나가면 작성 중인 먹팟이 삭제돼요. + + + + + + ); +}; + +export default PreventModal; diff --git a/src/app/write/hooks/useCustomRouter.ts b/src/app/write/hooks/useCustomRouter.ts new file mode 100644 index 00000000..4749911c --- /dev/null +++ b/src/app/write/hooks/useCustomRouter.ts @@ -0,0 +1,70 @@ +import { useEffect, useRef, useState } from 'react'; +import { useRouter as usePrimitiveRouter } from 'next/navigation'; +import { + triggerBeforeRouteChangeEvent, + triggerRouteChangeStartEvent, + useFreezeRequestsContext, +} from './useRouteChangeEvents'; + +interface NavigateOptions { + scroll?: boolean; +} + +type AppRouterInstance = ReturnType; + +const createRouterProxy = (router: AppRouterInstance, isFrozen: boolean, signal?: AbortSignal) => + new Proxy(router, { + get: (target, prop, receiver) => { + if (prop === 'push') { + return (href: string, options?: NavigateOptions) => { + const resolvePush = () => { + triggerRouteChangeStartEvent(href); + Reflect.apply(target.push, this, [href, options]); + }; + + if (isFrozen) { + window.addEventListener( + 'routeChangeConfirmationEvent', + (ev) => { + if (ev.detail.targetUrl === href) resolvePush(); + }, + { signal }, + ); + + triggerBeforeRouteChangeEvent(href); // NOTE: may wanna use a timeout here + + return; + } + resolvePush(); + }; + } + + return Reflect.get(target, prop, receiver); + }, + }); + +const useCustomRouter = (): AppRouterInstance => { + const router = usePrimitiveRouter(); + const { freezeRequests } = useFreezeRequestsContext(); + const abortControllerRef = useRef(new AbortController()); + const [routerProxy, setRouterProxy] = useState( + createRouterProxy(router, freezeRequests.length !== 0, abortControllerRef.current.signal), + ); + + useEffect(() => { + return () => abortControllerRef.current.abort(); + }, []); + + useEffect(() => { + abortControllerRef.current.abort(); + const abortController = new AbortController(); + + setRouterProxy(createRouterProxy(router, freezeRequests.length !== 0, abortController.signal)); + + return () => abortController.abort(); + }, [router, freezeRequests]); + + return routerProxy; +}; + +export default useCustomRouter; diff --git a/src/app/write/hooks/useRouteChangeEvents.tsx b/src/app/write/hooks/useRouteChangeEvents.tsx new file mode 100644 index 00000000..e67862dd --- /dev/null +++ b/src/app/write/hooks/useRouteChangeEvents.tsx @@ -0,0 +1,225 @@ +'use client'; + +import React, { useContext, useEffect, useRef, useState } from 'react'; +import { nanoid } from 'nanoid'; + +type HistoryURL = string | URL | null | undefined; + +type RouteChangeStartEvent = CustomEvent<{ targetUrl: string }>; +type RouteChangeEndEvent = CustomEvent<{ targetUrl: HistoryURL }>; +type ForceAnchorClickEvent = MouseEvent & { isForceAnchorClickEvent: true }; + +declare global { + interface WindowEventMap { + beforeRouteChangeEvent: RouteChangeStartEvent; + routeChangeConfirmationEvent: RouteChangeStartEvent; + routeChangeStartEvent: RouteChangeStartEvent; + routeChangeEndEvent: RouteChangeEndEvent; + } +} + +interface FreezeRequestsContextValue { + freezeRequests: string[]; + setFreezeRequests: React.Dispatch>; +} + +const isServer = typeof window === 'undefined'; + +const FreezeRequestsContext = React.createContext({ + freezeRequests: [], + // eslint-disable-next-line @typescript-eslint/no-empty-function + setFreezeRequests: () => {}, +}); + +export const useFreezeRequestsContext = () => { + const { freezeRequests, setFreezeRequests } = useContext(FreezeRequestsContext); + + return { + freezeRequests, + request: (sourceId: string) => { + // console.log('route change freeze requested by: ', sourceId) + setFreezeRequests([...freezeRequests, sourceId]); + }, + revoke: (sourceId: string) => { + // console.log('route change freeze revoked by: ', sourceId) + setFreezeRequests(freezeRequests.filter((x) => x !== sourceId)); + }, + }; +}; + +type PushStateInput = [data: unknown, unused: string, url: HistoryURL]; + +export const triggerRouteChangeStartEvent = (targetUrl: string): void => { + // console.log("registered route change start: ", targetUrl) + const ev = new CustomEvent('routeChangeStartEvent', { detail: { targetUrl } }); + if (!isServer) window.dispatchEvent(ev); +}; + +export const triggerRouteChangeEndEvent = (targetUrl: HistoryURL): void => { + // console.log("registered route change end: ", targetUrl) + const ev = new CustomEvent('routeChangeEndEvent', { detail: { targetUrl } }); + if (!isServer) window.dispatchEvent(ev); +}; + +export const triggerBeforeRouteChangeEvent = (targetUrl: string): void => { + // console.log("registered before route change event: ", targetUrl) + const ev = new CustomEvent('beforeRouteChangeEvent', { detail: { targetUrl } }); + if (!isServer) window.dispatchEvent(ev); +}; + +export const triggerRouteChangeConfirmationEvent = (targetUrl: string): void => { + // console.log("registered route change confirmation event: ", targetUrl) + const ev = new CustomEvent('routeChangeConfirmationEvent', { detail: { targetUrl } }); + if (!isServer) window.dispatchEvent(ev); +}; + +const createForceClickEvent = (event: MouseEvent): ForceAnchorClickEvent => { + const res = new MouseEvent('click', event) as ForceAnchorClickEvent; + res.isForceAnchorClickEvent = true; + return res; +}; + +export const RouteChangesProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [freezeRequests, setFreezeRequests] = useState([]); + + useEffect(() => { + const abortController = new AbortController(); + + const handleAnchorClick = (event: MouseEvent | ForceAnchorClickEvent) => { + const target = event.currentTarget as HTMLAnchorElement; + + const isFrozen = freezeRequests.length !== 0; + if (isFrozen && !(event as ForceAnchorClickEvent).isForceAnchorClickEvent) { + event.preventDefault(); + event.stopPropagation(); + + window.addEventListener( + 'routeChangeConfirmationEvent', + (ev) => { + if (ev.detail.targetUrl === target.href) { + const forceClickEvent = createForceClickEvent(event); + target.dispatchEvent(forceClickEvent); // NOTE: may want to use a timeout here + } + }, + { signal: abortController.signal }, + ); + + triggerBeforeRouteChangeEvent(target.href); + return; + } + + triggerRouteChangeStartEvent(target.href); + }; + + const handleAnchors = (anchors: NodeListOf) => { + anchors.forEach((a) => { + a.addEventListener('click', handleAnchorClick, { signal: abortController.signal, capture: true }); + }); + }; + + const handleMutation: MutationCallback = (mutationList) => { + mutationList.forEach((record) => { + if (record.type === 'childList' && record.target instanceof HTMLElement) { + const anchors: NodeListOf = record.target.querySelectorAll('a[href]'); + handleAnchors(anchors); + } + }); + }; + + const anchors: NodeListOf = document.querySelectorAll('a[href]'); + handleAnchors(anchors); + + const mutationObserver = new MutationObserver(handleMutation); + + mutationObserver.observe(document, { childList: true, subtree: true }); + + const pushStateProxy = new Proxy(window.history.pushState, { + apply: (target, thisArg, argArray: PushStateInput) => { + triggerRouteChangeEndEvent(argArray[2]); + return target.apply(thisArg, argArray); + }, + getPrototypeOf: (target) => { + return target; + }, + }); + + window.history.pushState = pushStateProxy; + + return () => { + mutationObserver.disconnect(); + abortController.abort(); + window.history.pushState = Object.getPrototypeOf(pushStateProxy); + }; + }, [freezeRequests]); + + return ( + + {children} + + ); +}; + +interface RouteChangeCallbacks { + onBeforeRouteChange?: (target: string) => boolean; // if `false` prevents a route change until `allowRouteChange` is called + onRouteChangeStart?: (target: string) => void; + onRouteChangeComplete?: (target: HistoryURL) => void; +} + +const useRouteChangeEvents = (callbacks: RouteChangeCallbacks) => { + const id = useRef(nanoid()); + const { request, revoke } = useFreezeRequestsContext(); + const [confrimationTarget, setConfirmationTarget] = useState(null); + + useEffect(() => { + request(id.current); + return () => revoke(id.current); + }, []); + + useEffect(() => { + const abortController = new AbortController(); + + window.addEventListener( + 'beforeRouteChangeEvent', + (ev) => { + const { targetUrl } = ev.detail; + const shouldProceed = callbacks.onBeforeRouteChange && callbacks.onBeforeRouteChange(targetUrl); + if (shouldProceed ?? true) { + triggerRouteChangeConfirmationEvent(targetUrl); + } else { + setConfirmationTarget(targetUrl); + } + }, + { signal: abortController.signal }, + ); + + window.addEventListener( + 'routeChangeEndEvent', + (ev) => { + callbacks.onRouteChangeComplete && callbacks.onRouteChangeComplete(ev.detail.targetUrl); + }, + { signal: abortController.signal }, + ); + + window.addEventListener( + 'routeChangeStartEvent', + (ev) => { + callbacks.onRouteChangeStart && callbacks.onRouteChangeStart(ev.detail.targetUrl); + }, + { signal: abortController.signal }, + ); + + return () => abortController.abort(); + }, [callbacks]); + + return { + allowRouteChange: () => { + if (!confrimationTarget) { + console.warn('allowRouteChange called for no specified confirmation target'); + return; + } + triggerRouteChangeConfirmationEvent(confrimationTarget); + }, + }; +}; + +export default useRouteChangeEvents; From c3c0389ec59515b567439d8fb8b995dcebed8670 Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Mon, 31 Jul 2023 23:55:45 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat:=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=ED=83=88=EB=B0=A9=EC=A7=80=20hook=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 --- .pnp.cjs | 10 +++++++- ...nanoid-npm-4.0.2-ae010cad86-747c399cea.zip | Bin 0 -> 10015 bytes package.json | 1 + src/app/layout.tsx | 5 +++- src/app/write/page.tsx | 24 ++++++++++++++++-- yarn.lock | 10 ++++++++ 6 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 .yarn/cache/nanoid-npm-4.0.2-ae010cad86-747c399cea.zip diff --git a/.pnp.cjs b/.pnp.cjs index 33da7cf8..826fee72 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -80,6 +80,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["lodash", "npm:4.17.21"],\ ["lottie-web", "npm:5.12.2"],\ ["msw", "virtual:d08515d9e4b27cdfeb9b2dde224a8841bd05f954e612ea2dd8b5b9cd65297eea34fcb753f3a56527367eedd956e930ca8b2ba53a7468cc42d099c8cdf5472f02#npm:1.2.2"],\ + ["nanoid", "npm:4.0.2"],\ ["next", "virtual:d08515d9e4b27cdfeb9b2dde224a8841bd05f954e612ea2dd8b5b9cd65297eea34fcb753f3a56527367eedd956e930ca8b2ba53a7468cc42d099c8cdf5472f02#npm:13.4.12"],\ ["postcss", "npm:8.4.25"],\ ["prettier", "npm:2.8.8"],\ @@ -10075,6 +10076,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["nanoid", "npm:3.3.6"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:4.0.2", {\ + "packageLocation": "./.yarn/cache/nanoid-npm-4.0.2-ae010cad86-747c399cea.zip/node_modules/nanoid/",\ + "packageDependencies": [\ + ["nanoid", "npm:4.0.2"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["natural-compare", [\ @@ -13488,7 +13496,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["lodash", "npm:4.17.21"],\ ["lottie-web", "npm:5.12.2"],\ ["msw", "virtual:d08515d9e4b27cdfeb9b2dde224a8841bd05f954e612ea2dd8b5b9cd65297eea34fcb753f3a56527367eedd956e930ca8b2ba53a7468cc42d099c8cdf5472f02#npm:1.2.2"],\ - ["next", "virtual:d08515d9e4b27cdfeb9b2dde224a8841bd05f954e612ea2dd8b5b9cd65297eea34fcb753f3a56527367eedd956e930ca8b2ba53a7468cc42d099c8cdf5472f02#npm:13.4.12"],\ + ["next", "virtual:d08515d9e4b27cdfeb9b2dde224a8841bd05f954e612ea2dd8b5b9cd65297eea34fcb753f3a56527367eedd956e930ca8b2ba53a7468cc42d099c8cdf5472f02#npm:13.4.10"],\ ["postcss", "npm:8.4.25"],\ ["prettier", "npm:2.8.8"],\ ["prop-types", "npm:15.8.1"],\ diff --git a/.yarn/cache/nanoid-npm-4.0.2-ae010cad86-747c399cea.zip b/.yarn/cache/nanoid-npm-4.0.2-ae010cad86-747c399cea.zip new file mode 100644 index 0000000000000000000000000000000000000000..b3384e963050ac863fc6a291dfecca6d7b3d1cfd GIT binary patch literal 10015 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 - {children} + + {children} + diff --git a/src/app/write/page.tsx b/src/app/write/page.tsx index 8fe8b344..095213fe 100644 --- a/src/app/write/page.tsx +++ b/src/app/write/page.tsx @@ -2,12 +2,30 @@ import FirstStep from './components/Form/FirstStep'; import SecondStep from './components/Form/SecondStep'; import { wrapper } from './style.css'; -import { useFunnel } from '@/hooks'; +import { useFunnel, useOverlay } from '@/hooks'; import { useProfile } from '@/api/hooks'; import WriteTitle from './components/WriteTitle/WriteTitle'; import useFormStore from './store/useFormStore'; -import { useEffect } from 'react'; +import { useEffect, useCallback } from 'react'; import { useRouter } from 'next/navigation'; +import PreventModal from '@/app/write/components/PreventModal/PreventModal'; +import useRouteChangeEvents from '@/app/write/hooks/useRouteChangeEvents'; + +const useLeaveConfirmation = (shouldPreventRouteChange: boolean) => { + const [openModal, closeModal] = useOverlay(); + console.log('useLeaveConfirmation'); + // const router = useRouter(); + const onBeforeRouteChange = useCallback(() => { + console.log('onBeforeRouteChange'); + if (shouldPreventRouteChange) { + openModal( allowRouteChange} />); + return false; + } + return true; + }, [shouldPreventRouteChange]); + + const { allowRouteChange } = useRouteChangeEvents({ onBeforeRouteChange }); +}; export default function Write() { const [step, { prevStep, nextStep }] = useFunnel(['1', '2']); @@ -32,6 +50,8 @@ export default function Write() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useLeaveConfirmation(true); + return (
diff --git a/yarn.lock b/yarn.lock index 4aad430c..47f5661f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7562,6 +7562,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^4.0.2": + version: 4.0.2 + resolution: "nanoid@npm:4.0.2" + bin: + nanoid: bin/nanoid.js + checksum: 747c399cea4664dd0be1d0ec498ffd1ef8f1f5221676fc8b577e3f46f66d9afcddb9595d63d19a2e78d0bc6cc33984f65e66bf1682c850b9e26288883d96b53f + languageName: node + linkType: hard + "natural-compare-lite@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare-lite@npm:1.4.0" @@ -10529,6 +10538,7 @@ __metadata: lodash: ^4.17.21 lottie-web: ^5.12.2 msw: ^1.2.1 + nanoid: ^4.0.2 next: 13.4.12 postcss: ^8.4.23 prettier: ^2.8.8 From 4981ac4d73f4c197f9915782054d7f36147d27c8 Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Tue, 1 Aug 2023 18:43:18 +0900 Subject: [PATCH 04/14] =?UTF-8?q?chore:=20file=20import=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/layout.tsx | 2 +- src/app/write/components/Form/FirstStep.tsx | 2 +- src/app/write/components/index.ts | 2 ++ src/app/write/hooks/index.ts | 4 +++ src/app/write/hooks/useCustomRouter.ts | 6 ++-- src/app/write/hooks/useLeaveModal.tsx | 33 +++++++++++++++++++++ src/app/write/page.tsx | 27 ++++------------- 7 files changed, 49 insertions(+), 27 deletions(-) create mode 100644 src/app/write/hooks/index.ts create mode 100644 src/app/write/hooks/useLeaveModal.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f6bf4dbf..7c266ee1 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,7 +4,7 @@ import Script from 'next/script'; import { OverlayProvider, QueryProvider } from '@/providers'; import { ProfileProvider } from '@/providers/server'; import Head from 'next/head'; -import { RouteChangesProvider } from './write/hooks/useRouteChangeEvents'; +import { RouteChangesProvider } from '@/app/write/contexts/RouteChangeProvider'; const pretendardFont = localFont({ src: [ diff --git a/src/app/write/components/Form/FirstStep.tsx b/src/app/write/components/Form/FirstStep.tsx index 43d3cd22..2fe30b4e 100644 --- a/src/app/write/components/Form/FirstStep.tsx +++ b/src/app/write/components/Form/FirstStep.tsx @@ -5,7 +5,7 @@ import { FormProvider } from 'react-hook-form'; import { Button, Input, InputSection, Typography } from '@/components'; import { InputDate, Counter, AgeModal, AgeBottomSheet, MapModal, TimeDropDown } from '@/app/write/components'; import { StepOneData } from '@/app/write/types'; -import { useWriteForm } from '@/app/write/hooks/useWriteForm'; +import { useWriteForm } from '@/app/write/hooks'; import { formWrapper, sectionGap, inputGap, submitButton, flexBetween, ageError } from './Form.css'; import { useIsMobile } from '@/hooks'; diff --git a/src/app/write/components/index.ts b/src/app/write/components/index.ts index 09f0aa70..c78c88dc 100644 --- a/src/app/write/components/index.ts +++ b/src/app/write/components/index.ts @@ -4,7 +4,9 @@ export { default as Counter } from './Counter/Counter'; export { default as InputDate } from './InputDate/InputDate'; export { default as FirstStep } from './Form/FirstStep'; export { default as SecondStep } from './Form/SecondStep'; +export { default as BoardForm } from './Form/BoardForm'; export { default as MapModal } from './MapModal/MapModal'; export { default as WriteTitle } from './WriteTitle/WriteTitle'; export { default as TimeDropDown } from './TimeDropDown/TimeDropDown'; export { default as InputLoading } from './InputLoading/InputLoading'; +export { default as FreezeModal } from './FreezeModal/FreezeModal'; diff --git a/src/app/write/hooks/index.ts b/src/app/write/hooks/index.ts new file mode 100644 index 00000000..78d98615 --- /dev/null +++ b/src/app/write/hooks/index.ts @@ -0,0 +1,4 @@ +export { default as useCustomRouter } from './useCustomRouter'; +export { default as useLeaveModal } from './useLeaveModal'; +export { default as useSetFormData } from './useSetFormData'; +export { default as useWriteForm } from './useWriteForm'; diff --git a/src/app/write/hooks/useCustomRouter.ts b/src/app/write/hooks/useCustomRouter.ts index 4749911c..4720e836 100644 --- a/src/app/write/hooks/useCustomRouter.ts +++ b/src/app/write/hooks/useCustomRouter.ts @@ -4,7 +4,7 @@ import { triggerBeforeRouteChangeEvent, triggerRouteChangeStartEvent, useFreezeRequestsContext, -} from './useRouteChangeEvents'; +} from '@/app/write/contexts/RouteChangeProvider'; interface NavigateOptions { scroll?: boolean; @@ -31,8 +31,7 @@ const createRouterProxy = (router: AppRouterInstance, isFrozen: boolean, signal? { signal }, ); - triggerBeforeRouteChangeEvent(href); // NOTE: may wanna use a timeout here - + triggerBeforeRouteChangeEvent(href); return; } resolvePush(); @@ -52,6 +51,7 @@ const useCustomRouter = (): AppRouterInstance => { ); useEffect(() => { + // eslint-disable-next-line react-hooks/exhaustive-deps return () => abortControllerRef.current.abort(); }, []); diff --git a/src/app/write/hooks/useLeaveModal.tsx b/src/app/write/hooks/useLeaveModal.tsx new file mode 100644 index 00000000..9ea9f78d --- /dev/null +++ b/src/app/write/hooks/useLeaveModal.tsx @@ -0,0 +1,33 @@ +import { useOverlay } from '@/hooks'; +import useRouteChangeEvents from '../contexts/RouteChangeProvider'; +import { useCallback } from 'react'; +import { useRouter } from 'next/navigation'; +import { FreezeModal } from '@/app/write/components'; + +const useLeaveModal = (shouldPreventRouteChange: boolean) => { + const [openModal, closeModal] = useOverlay(); + const router = useRouter(); + useRouteChangeEvents({ + onBeforeRouteChange: useCallback( + (targetUrl: string) => { + if (shouldPreventRouteChange) { + openModal( + { + router.push(targetUrl); + closeModal(); + }} + />, + ); + + return false; + } + return true; + }, + [closeModal, openModal, router, shouldPreventRouteChange], + ), + }); +}; + +export default useLeaveModal; diff --git a/src/app/write/page.tsx b/src/app/write/page.tsx index 095213fe..ad73971b 100644 --- a/src/app/write/page.tsx +++ b/src/app/write/page.tsx @@ -2,30 +2,13 @@ import FirstStep from './components/Form/FirstStep'; import SecondStep from './components/Form/SecondStep'; import { wrapper } from './style.css'; -import { useFunnel, useOverlay } from '@/hooks'; +import { useFunnel } from '@/hooks'; import { useProfile } from '@/api/hooks'; import WriteTitle from './components/WriteTitle/WriteTitle'; -import useFormStore from './store/useFormStore'; -import { useEffect, useCallback } from 'react'; +import useFormStore from '@/app/write/store/useFormStore'; +import { useEffect } from 'react'; import { useRouter } from 'next/navigation'; -import PreventModal from '@/app/write/components/PreventModal/PreventModal'; -import useRouteChangeEvents from '@/app/write/hooks/useRouteChangeEvents'; - -const useLeaveConfirmation = (shouldPreventRouteChange: boolean) => { - const [openModal, closeModal] = useOverlay(); - console.log('useLeaveConfirmation'); - // const router = useRouter(); - const onBeforeRouteChange = useCallback(() => { - console.log('onBeforeRouteChange'); - if (shouldPreventRouteChange) { - openModal( allowRouteChange} />); - return false; - } - return true; - }, [shouldPreventRouteChange]); - - const { allowRouteChange } = useRouteChangeEvents({ onBeforeRouteChange }); -}; +import useLeaveModal from '@/app/write/hooks/useLeaveModal'; export default function Write() { const [step, { prevStep, nextStep }] = useFunnel(['1', '2']); @@ -50,7 +33,7 @@ export default function Write() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useLeaveConfirmation(true); + useLeaveModal(true); return (
From 0a81deb75ddce9c726fe7456e680dddb79f4f493 Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Tue, 1 Aug 2023 18:46:06 +0900 Subject: [PATCH 05/14] =?UTF-8?q?chore:=20Provider=20tsx=EB=A1=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RouteChangeProvider.tsx} | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) rename src/app/write/{hooks/useRouteChangeEvents.tsx => contexts/RouteChangeProvider.tsx} (92%) diff --git a/src/app/write/hooks/useRouteChangeEvents.tsx b/src/app/write/contexts/RouteChangeProvider.tsx similarity index 92% rename from src/app/write/hooks/useRouteChangeEvents.tsx rename to src/app/write/contexts/RouteChangeProvider.tsx index e67862dd..bd0ddaa4 100644 --- a/src/app/write/hooks/useRouteChangeEvents.tsx +++ b/src/app/write/contexts/RouteChangeProvider.tsx @@ -37,11 +37,9 @@ export const useFreezeRequestsContext = () => { return { freezeRequests, request: (sourceId: string) => { - // console.log('route change freeze requested by: ', sourceId) setFreezeRequests([...freezeRequests, sourceId]); }, revoke: (sourceId: string) => { - // console.log('route change freeze revoked by: ', sourceId) setFreezeRequests(freezeRequests.filter((x) => x !== sourceId)); }, }; @@ -50,25 +48,21 @@ export const useFreezeRequestsContext = () => { type PushStateInput = [data: unknown, unused: string, url: HistoryURL]; export const triggerRouteChangeStartEvent = (targetUrl: string): void => { - // console.log("registered route change start: ", targetUrl) const ev = new CustomEvent('routeChangeStartEvent', { detail: { targetUrl } }); if (!isServer) window.dispatchEvent(ev); }; export const triggerRouteChangeEndEvent = (targetUrl: HistoryURL): void => { - // console.log("registered route change end: ", targetUrl) const ev = new CustomEvent('routeChangeEndEvent', { detail: { targetUrl } }); if (!isServer) window.dispatchEvent(ev); }; export const triggerBeforeRouteChangeEvent = (targetUrl: string): void => { - // console.log("registered before route change event: ", targetUrl) const ev = new CustomEvent('beforeRouteChangeEvent', { detail: { targetUrl } }); if (!isServer) window.dispatchEvent(ev); }; export const triggerRouteChangeConfirmationEvent = (targetUrl: string): void => { - // console.log("registered route change confirmation event: ", targetUrl) const ev = new CustomEvent('routeChangeConfirmationEvent', { detail: { targetUrl } }); if (!isServer) window.dispatchEvent(ev); }; @@ -87,18 +81,16 @@ export const RouteChangesProvider: React.FC<{ children: React.ReactNode }> = ({ const handleAnchorClick = (event: MouseEvent | ForceAnchorClickEvent) => { const target = event.currentTarget as HTMLAnchorElement; - const isFrozen = freezeRequests.length !== 0; if (isFrozen && !(event as ForceAnchorClickEvent).isForceAnchorClickEvent) { event.preventDefault(); event.stopPropagation(); - window.addEventListener( 'routeChangeConfirmationEvent', (ev) => { if (ev.detail.targetUrl === target.href) { const forceClickEvent = createForceClickEvent(event); - target.dispatchEvent(forceClickEvent); // NOTE: may want to use a timeout here + target.dispatchEvent(forceClickEvent); } }, { signal: abortController.signal }, @@ -183,7 +175,7 @@ const useRouteChangeEvents = (callbacks: RouteChangeCallbacks) => { (ev) => { const { targetUrl } = ev.detail; const shouldProceed = callbacks.onBeforeRouteChange && callbacks.onBeforeRouteChange(targetUrl); - if (shouldProceed ?? true) { + if (shouldProceed) { triggerRouteChangeConfirmationEvent(targetUrl); } else { setConfirmationTarget(targetUrl); @@ -208,7 +200,9 @@ const useRouteChangeEvents = (callbacks: RouteChangeCallbacks) => { { signal: abortController.signal }, ); - return () => abortController.abort(); + return () => { + abortController.abort(); + }; }, [callbacks]); return { From 8534f6fcd3fefc7485496aa4f9700a766638ccb9 Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Tue, 1 Aug 2023 18:46:52 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat:=20=EC=9D=B4=ED=83=88=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80=20FreezeModal=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FreezeModal.tsx} | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) rename src/app/write/components/{PreventModal/PreventModal.tsx => FreezeModal/FreezeModal.tsx} (52%) diff --git a/src/app/write/components/PreventModal/PreventModal.tsx b/src/app/write/components/FreezeModal/FreezeModal.tsx similarity index 52% rename from src/app/write/components/PreventModal/PreventModal.tsx rename to src/app/write/components/FreezeModal/FreezeModal.tsx index 185a49e0..341d1c92 100644 --- a/src/app/write/components/PreventModal/PreventModal.tsx +++ b/src/app/write/components/FreezeModal/FreezeModal.tsx @@ -2,24 +2,31 @@ import { Button, Modal } from '@/components'; interface Props { onClose: () => void; - onClick: () => void; + onClick?: () => void; + content?: string; + footer?: string | string[]; } -const PreventModal = ({ onClose, onClick }: Props) => { +const FreezeModal = ({ + content = '페이지를 나가면 작성 중인 먹팟이 삭제돼요.', + footer = ['나가기', '계속 작성하기'], + onClose, + onClick, +}: Props) => { return ( - 페이지를 나가면 작성 중인 먹팟이 삭제돼요. + {content} ); }; -export default PreventModal; +export default FreezeModal; From a6cbb8df41b529035c34e977920ecd416d8b139d Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Tue, 1 Aug 2023 20:04:40 +0900 Subject: [PATCH 07/14] =?UTF-8?q?chore:=20file=20import=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/write/[id]/page.tsx | 2 +- src/app/write/page.tsx | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/app/write/[id]/page.tsx b/src/app/write/[id]/page.tsx index 57da83ba..fa5697a3 100644 --- a/src/app/write/[id]/page.tsx +++ b/src/app/write/[id]/page.tsx @@ -1,7 +1,7 @@ import { notFound } from 'next/navigation'; import { InputLoading } from '../components'; import { Suspense } from 'react'; -import BoardForm from '../components/Form/BoardForm'; +import { BoardForm } from '@/app/write/components'; const BoardEditPage = async ({ params: { id }, diff --git a/src/app/write/page.tsx b/src/app/write/page.tsx index ad73971b..5143b3a2 100644 --- a/src/app/write/page.tsx +++ b/src/app/write/page.tsx @@ -1,14 +1,13 @@ 'use client'; -import FirstStep from './components/Form/FirstStep'; -import SecondStep from './components/Form/SecondStep'; -import { wrapper } from './style.css'; + +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import { FirstStep, SecondStep, WriteTitle } from '@/app/write/components'; import { useFunnel } from '@/hooks'; import { useProfile } from '@/api/hooks'; -import WriteTitle from './components/WriteTitle/WriteTitle'; import useFormStore from '@/app/write/store/useFormStore'; -import { useEffect } from 'react'; -import { useRouter } from 'next/navigation'; -import useLeaveModal from '@/app/write/hooks/useLeaveModal'; +import { useLeaveModal } from '@/app/write/hooks'; +import { wrapper } from './style.css'; export default function Write() { const [step, { prevStep, nextStep }] = useFunnel(['1', '2']); From be0f30c3d3f05778a8c2fc4bdba55d2689f136ab Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Tue, 1 Aug 2023 20:06:09 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=99=95=EC=9D=B8=20=EB=AA=A8=EB=8B=AC,?= =?UTF-8?q?=20=EC=9D=B4=ED=83=88=EB=B0=A9=EC=A7=80=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/write/components/Form/BoardForm.tsx | 15 ++++--- src/app/write/components/Form/SecondStep.tsx | 44 +++++++++++++------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/app/write/components/Form/BoardForm.tsx b/src/app/write/components/Form/BoardForm.tsx index 095d48bf..b84b07e6 100644 --- a/src/app/write/components/Form/BoardForm.tsx +++ b/src/app/write/components/Form/BoardForm.tsx @@ -1,15 +1,13 @@ 'use client'; -import FirstStep from './FirstStep'; -import SecondStep from './SecondStep'; -import { wrapper } from '../../style.css'; +import { useEffect } from 'react'; +import { useParams, useRouter } from 'next/navigation'; import { useFunnel } from '@/hooks'; import { useProfile } from '@/api/hooks'; -import { useParams, useRouter } from 'next/navigation'; -import WriteTitle from '../WriteTitle/WriteTitle'; -import useSetFormData from '../../hooks/useSetFormData'; -import useFormStore from '../../store/useFormStore'; -import { useEffect } from 'react'; +import { FirstStep, SecondStep, WriteTitle } from '@/app/write/components'; +import { useSetFormData, useLeaveModal } from '@/app/write/hooks'; +import { wrapper } from '../../style.css'; +import useFormStore from '@/app/write/store/useFormStore'; export default function BoardForm() { const { data } = useProfile(); @@ -35,6 +33,7 @@ export default function BoardForm() { if (!data) { router.push('/login'); } + useLeaveModal(true); return (
diff --git a/src/app/write/components/Form/SecondStep.tsx b/src/app/write/components/Form/SecondStep.tsx index 8ffff9dd..1e407be7 100644 --- a/src/app/write/components/Form/SecondStep.tsx +++ b/src/app/write/components/Form/SecondStep.tsx @@ -6,9 +6,10 @@ import { Button, Input, InputSection, TextArea, Toast, Typography } from '@/comp import { formWrapper, inputGap } from './Form.css'; import parseData from './util/parseData'; import { useRouter } from 'next/navigation'; -import { useWriteForm } from '@/app/write/hooks/useWriteForm'; +import { useWriteForm } from '@/app/write/hooks/'; import { useOverlay } from '@/hooks'; import { usePostBoard, usePatchBoard } from '@/api/write'; +import { FreezeModal } from '@/app/write/components'; type StepProps = { reset: () => void; @@ -21,6 +22,7 @@ const SecondStep = ({ reset, boardId, isPatch = false }: StepProps) => { const { mutate: patch } = usePatchBoard(boardId); const { stepTwoMethod } = useWriteForm(); const [openToast, closeToast] = useOverlay(); + const [openModal, closeModal] = useOverlay(); const router = useRouter(); const onSubmit: SubmitHandler = async (data: BoardData) => { @@ -46,21 +48,33 @@ const SecondStep = ({ reset, boardId, isPatch = false }: StepProps) => { ); } if (isPatch && boardId) { - patch( - { boardId: boardId, data: { ...parseData(data) } }, - { - onSuccess: () => { - openToast(); - router.push(`/board/${boardId}`); - reset(); + const freezeSubmit = () => { + closeModal(); + patch( + { boardId: boardId, data: { ...parseData(data) } }, + { + onSuccess: () => { + openToast(); + router.push(`/board/${boardId}`); + reset(); + }, + onError: (error) => { + openToast(); + if (error.response.status === 403) { + router.push('/login'); + } + }, }, - onError: (error) => { - openToast(); - if (error.response.status === 403) { - router.push('/login'); - } - }, - }, + ); + return; + }; + openModal( + , ); } }; From 8a7aaca5094bc10a42a2da3a9cf0f12936205f04 Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Tue, 1 Aug 2023 20:41:01 +0900 Subject: [PATCH 09/14] =?UTF-8?q?chore:=20useWriteForm=20default=20export?= =?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/app/write/hooks/useWriteForm.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/write/hooks/useWriteForm.ts b/src/app/write/hooks/useWriteForm.ts index 165772f6..eff5607a 100644 --- a/src/app/write/hooks/useWriteForm.ts +++ b/src/app/write/hooks/useWriteForm.ts @@ -3,7 +3,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import useFormStore from '@/app/write/store/useFormStore'; import { boardSchema, BoardSchema, StepOneSchema, stepOneSchema } from '@/app/write/lib/schema'; -export const useWriteForm = () => { +const useWriteForm = () => { const { stepOne, stepTwo } = useFormStore(); const stepOneMethod = useForm({ @@ -22,3 +22,5 @@ export const useWriteForm = () => { return { stepOneMethod, stepTwoMethod }; }; + +export default useWriteForm; From 1fc973126eb55bba2fcac9785be882b618855ae7 Mon Sep 17 00:00:00 2001 From: naro_Kim Date: Tue, 1 Aug 2023 22:29:24 +0900 Subject: [PATCH 10/14] =?UTF-8?q?style:=20FreezeModal=20content=20css=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 --- src/app/write/components/Form/SecondStep.tsx | 2 +- src/app/write/components/FreezeModal/FreezeModal.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/write/components/Form/SecondStep.tsx b/src/app/write/components/Form/SecondStep.tsx index 1e407be7..d9c01905 100644 --- a/src/app/write/components/Form/SecondStep.tsx +++ b/src/app/write/components/Form/SecondStep.tsx @@ -70,7 +70,7 @@ const SecondStep = ({ reset, boardId, isPatch = false }: StepProps) => { }; openModal( void; @@ -16,7 +16,11 @@ const FreezeModal = ({ return ( - {content} + + + {content} + +