From 23e0d28489226767274b4f942fdafa197b7bcc3b Mon Sep 17 00:00:00 2001 From: lemu Date: Mon, 22 Apr 2024 10:17:31 -0300 Subject: [PATCH] chore: prevent navigation (#8) * chore: prevent navigation component with useBlocker and modal * chore: prevent navigation component on bids, grants, and updates, remove usePreventNavigation hook * chore: add navigate to hooks deps * chore: remove unnecessary fragment * chore: internationalization --- src/components/Layout/ContentLayout.tsx | 26 ++++----- src/components/Layout/PreventNavigation.tsx | 30 +++++++++++ .../Submit/ProposalSubmitCatalystPage.tsx | 2 +- .../Submit/ProposalSubmitHiringPage.tsx | 2 +- .../Proposal/Submit/ProposalSubmitPoiPage.tsx | 2 +- src/hooks/usePreventNavigation.ts | 54 ------------------- src/intl/en.json | 7 ++- src/main.tsx | 4 +- src/pages/submit/ban-name.tsx | 2 +- src/pages/submit/bid.tsx | 12 ++--- src/pages/submit/draft.tsx | 2 +- src/pages/submit/governance.tsx | 2 +- src/pages/submit/grant.tsx | 16 +++--- src/pages/submit/index.tsx | 2 +- src/pages/submit/linked-wearables.tsx | 2 +- src/pages/submit/update.tsx | 5 +- src/pages/update.tsx | 2 +- 17 files changed, 67 insertions(+), 105 deletions(-) create mode 100644 src/components/Layout/PreventNavigation.tsx delete mode 100644 src/hooks/usePreventNavigation.ts diff --git a/src/components/Layout/ContentLayout.tsx b/src/components/Layout/ContentLayout.tsx index 03b48b9..ae5c1f5 100644 --- a/src/components/Layout/ContentLayout.tsx +++ b/src/components/Layout/ContentLayout.tsx @@ -4,39 +4,31 @@ import classNames from 'classnames' import { Back } from 'decentraland-ui/dist/components/Back/Back' import { Container } from 'decentraland-ui/dist/components/Container/Container' -import usePreventNavigation from '../../hooks/usePreventNavigation' -import locations from '../../utils/locations' - import './ContentLayout.css' +import PreventNavigation from './PreventNavigation.tsx' type Props = { className?: string small?: boolean children?: React.ReactNode - navigateHref?: string + navigateBackUrl?: string preventNavigation?: boolean } -export default function ContentLayout({ navigateHref, className, small, preventNavigation, children }: Props) { +export default function ContentLayout({ navigateBackUrl, className, small, preventNavigation, children }: Props) { const navigate = useNavigate() - const handleBack = () => { - if (preventNavigation) { - window.history.back() - } else { - navigate(navigateHref || locations.proposals()) - } - } - - usePreventNavigation(!!preventNavigation) return ( -
- -
+ {navigateBackUrl && ( +
+ navigate(navigateBackUrl)} /> +
+ )}
{children}
+
) } diff --git a/src/components/Layout/PreventNavigation.tsx b/src/components/Layout/PreventNavigation.tsx new file mode 100644 index 0000000..b3bda2f --- /dev/null +++ b/src/components/Layout/PreventNavigation.tsx @@ -0,0 +1,30 @@ +import { useBlocker } from 'react-router-dom' + +import useFormatMessage from '../../hooks/useFormatMessage.ts' +import ConfirmationModal from '../Modal/ConfirmationModal.tsx' + +type Props = { preventNavigation: boolean | undefined } + +export default function PreventNavigation({ preventNavigation }: Props) { + const t = useFormatMessage() + + const blocker = useBlocker( + ({ currentLocation, nextLocation }) => !!preventNavigation && currentLocation.pathname !== nextLocation.pathname + ) + + return blocker.state === 'blocked' ? ( +
+ blocker.proceed()} + onSecondaryClick={() => blocker.reset()} + onClose={() => blocker.reset()} + primaryButtonText={t('navigation.prevent_navigation.confirm')} + secondaryButtonText={t('navigation.prevent_navigation.cancel')} + /> +
+ ) : null +} diff --git a/src/components/Proposal/Submit/ProposalSubmitCatalystPage.tsx b/src/components/Proposal/Submit/ProposalSubmitCatalystPage.tsx index 04518b2..254c5cb 100644 --- a/src/components/Proposal/Submit/ProposalSubmitCatalystPage.tsx +++ b/src/components/Proposal/Submit/ProposalSubmitCatalystPage.tsx @@ -163,7 +163,7 @@ export default function ProposalSubmitCatalystPage({ catalystType }: Props) { } return ( - + + + { - const preventNavigation = (event?: BeforeUnloadEvent) => { - if (event) { - event.preventDefault() - event.returnValue = '' - } else if (!window.confirm(t('navigation.exit'))) { - navigate(navigateTo) - } else { - confirmBack.current = true - navigate(navigateTo) - } - } - - const handleBeforeUnload = (event: BeforeUnloadEvent) => { - if (shouldPrevent) { - preventNavigation(event) - } - } - - window.addEventListener('beforeunload', handleBeforeUnload) - - // const globalHistory = {} // todo: globalHistory was imported from reach/router - // const unsubscribe = globalHistory.listen(({ action }) => { - // const pathname = toGovernancePathname(navigateTo) - - // if ( - // shouldPrevent && - // (action === 'POP' || (action === 'PUSH' && pathname === locations.proposals() && !confirmBack.current)) - // ) { - // preventNavigation() - // } - // }) - - const unsubscribe = () => {} - - return () => { - unsubscribe() - window.removeEventListener('beforeunload', handleBeforeUnload) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [shouldPrevent]) -} - -export default usePreventNavigation diff --git a/src/intl/en.json b/src/intl/en.json index f063acb..c03bf93 100644 --- a/src/intl/en.json +++ b/src/intl/en.json @@ -89,7 +89,12 @@ "empty": "No notifications at this time", "load_more": "Load more" }, - "exit": "Are you sure you want to exit? You will lose the changes you have made." + "prevent_navigation": { + "title": "Are you sure you want to leave?", + "description": "You will lose the changes you have made.", + "confirm": "Yes, leave", + "cancel": "Cancel" + } }, "mobile_login": { "exclamation": "Oh oh...", diff --git a/src/main.tsx b/src/main.tsx index f74fe12..d178c58 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,7 +4,7 @@ import 'semantic-ui-css/semantic.min.css' import React from 'react' import ReactDOM from 'react-dom' import { IntlProvider } from 'react-intl' -import { Outlet, RouterProvider, createBrowserRouter } from 'react-router-dom' +import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom' import { QueryClient } from '@tanstack/query-core' import { QueryClientProvider } from '@tanstack/react-query' @@ -98,14 +98,12 @@ const component = ( function LayoutShell() { return ( - <> - ) } diff --git a/src/pages/submit/ban-name.tsx b/src/pages/submit/ban-name.tsx index bb0d9ec..99f9cac 100644 --- a/src/pages/submit/ban-name.tsx +++ b/src/pages/submit/ban-name.tsx @@ -80,7 +80,7 @@ export default function SubmitBanName() { } return ( - + { - if (preventNavigation.current) { - window.history.back() - } else { - navigate(locations.submit()) - } + navigate(locations.submit()) } const submit = useCallback(async () => { @@ -314,6 +308,8 @@ export default function SubmitBid() { )} + + ) } diff --git a/src/pages/submit/draft.tsx b/src/pages/submit/draft.tsx index e4e73a8..d1afa54 100644 --- a/src/pages/submit/draft.tsx +++ b/src/pages/submit/draft.tsx @@ -105,7 +105,7 @@ export default function SubmitDraftProposal() { } return ( - + + (initialState) const [validationState, setValidationState] = useState(initialValidationState) @@ -141,15 +141,9 @@ export default function SubmitGrant() { }, [grantRequest]) const handleCancel = () => { - if (preventNavigation.current) { - window.history.back() - } else { - navigate(locations.submit()) - } + navigate(locations.submit()) } - usePreventNavigation(!!preventNavigation.current) - if (!isGrantProposalSubmitEnabled(Date.now())) { navigate('/submit') } @@ -171,7 +165,7 @@ export default function SubmitGrant() { setIsFormDisabled(false) }) } - }, [allSectionsValid, grantRequest]) + }, [allSectionsValid, grantRequest, navigate]) useEffect(() => { if (typeof window !== 'undefined') { @@ -349,6 +343,8 @@ export default function SubmitGrant() { )} + + ) } diff --git a/src/pages/submit/index.tsx b/src/pages/submit/index.tsx index 4d29288..2f679d3 100644 --- a/src/pages/submit/index.tsx +++ b/src/pages/submit/index.tsx @@ -60,7 +60,7 @@ export default function SubmitPage() { description={t('page.submit.description')} links={[{ rel: 'canonical', href: locations.submit() }]} /> - +
{t('page.submit.title')}
diff --git a/src/pages/submit/linked-wearables.tsx b/src/pages/submit/linked-wearables.tsx index d165835..6c4b1fb 100644 --- a/src/pages/submit/linked-wearables.tsx +++ b/src/pages/submit/linked-wearables.tsx @@ -311,7 +311,7 @@ export default function SubmitLinkedWearables() { } return ( - + (vestingData ? getReleases(vestingData) : undefined), [vestingData]) const handleGeneralSectionValidation = useCallback( @@ -310,6 +308,7 @@ export default function SubmitUpdatePage({ isEdit }: Props) { } /> )} + ) } diff --git a/src/pages/update.tsx b/src/pages/update.tsx index b42aafc..f41f37c 100644 --- a/src/pages/update.tsx +++ b/src/pages/update.tsx @@ -54,7 +54,7 @@ export default function UpdateDetail() { description={update?.introduction} links={[{ rel: 'canonical', href: locations.update(update.id) }]} /> - + {t('page.update_detail.project_title', { title: {proposal?.title} })}