From ca9eb7833017db00bf73bddeeffc10a4e29494bb Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:45:02 +0200 Subject: [PATCH] added LockNavigationContextProvider.tsx --- .../FormikAutoLockNavigation.tsx | 27 ++++++ .../LockNavigationContext.ts | 27 ++++++ .../LockNavigationContextProvider.tsx | 91 +++++++++++++++++++ src/components/index.ts | 2 + .../AliasDetailRoute/AliasPreferencesForm.tsx | 2 + .../AuthenticateRoute/NavigationButton.tsx | 5 +- src/routes/AuthenticatedRoute.tsx | 81 +++++++++-------- 7 files changed, 195 insertions(+), 40 deletions(-) create mode 100644 src/LockNavigationContext/FormikAutoLockNavigation.tsx create mode 100644 src/LockNavigationContext/LockNavigationContext.ts create mode 100644 src/LockNavigationContext/LockNavigationContextProvider.tsx diff --git a/src/LockNavigationContext/FormikAutoLockNavigation.tsx b/src/LockNavigationContext/FormikAutoLockNavigation.tsx new file mode 100644 index 0000000..cb26872 --- /dev/null +++ b/src/LockNavigationContext/FormikAutoLockNavigation.tsx @@ -0,0 +1,27 @@ +import {FormikContextType} from "formik" +import {useContext, useEffect} from "react" + +import LockNavigationContext from "./LockNavigationContext" + +export interface LockNavigationContextProviderProps { + formik: FormikContextType +} + +export default function FormikAutoLockNavigation({ + formik, +}: LockNavigationContextProviderProps): null { + const {lock, release} = useContext(LockNavigationContext) + + const valuesStringified = JSON.stringify(formik.values) + const initialValuesStringified = JSON.stringify(formik.initialValues) + + useEffect(() => { + if (valuesStringified !== initialValuesStringified) { + lock() + } else { + release() + } + }, [lock, release, valuesStringified, initialValuesStringified]) + + return null +} diff --git a/src/LockNavigationContext/LockNavigationContext.ts b/src/LockNavigationContext/LockNavigationContext.ts new file mode 100644 index 0000000..ee386c9 --- /dev/null +++ b/src/LockNavigationContext/LockNavigationContext.ts @@ -0,0 +1,27 @@ +import {createContext} from "react" + +export interface LockNavigationContextType { + isLocked: boolean + lock: () => void + release: () => void + navigate: (path: string) => void + handleAnchorClick: (event: React.MouseEvent) => void +} + +const LockNavigationContext = createContext({ + isLocked: false, + lock: () => { + throw new Error("lock() not implemented") + }, + release: () => { + throw new Error("release() not implemented") + }, + navigate: () => { + throw new Error("navigate() not implemented") + }, + handleAnchorClick: () => { + throw new Error("handleAnchorClick() not implemented") + }, +}) + +export default LockNavigationContext diff --git a/src/LockNavigationContext/LockNavigationContextProvider.tsx b/src/LockNavigationContext/LockNavigationContextProvider.tsx new file mode 100644 index 0000000..04229fa --- /dev/null +++ b/src/LockNavigationContext/LockNavigationContextProvider.tsx @@ -0,0 +1,91 @@ +import {TiCancel} from "react-icons/ti" +import {ReactNode, useMemo, useState} from "react" +import {useNavigate} from "react-router-dom" +import {MdLogout} from "react-icons/md" + +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from "@mui/material" + +import LockNavigationContext, { + LockNavigationContextType, +} from "./LockNavigationContext" + +export interface LockNavigationContextProviderProps { + children: ReactNode +} + +export default function LockNavigationContextProvider({ + children, +}: LockNavigationContextProviderProps): JSX.Element { + const navigate = useNavigate() + + const [isLocked, setIsLocked] = useState(false) + const [nextPath, setNextPath] = useState(null) + + const showDialog = Boolean(nextPath) + + const value = useMemo( + (): LockNavigationContextType => ({ + isLocked, + navigate: (path: string) => { + if (isLocked) { + setNextPath(path) + } else { + setNextPath(null) + navigate(path) + } + }, + handleAnchorClick: (event: React.MouseEvent) => { + if (isLocked) { + event.preventDefault() + setNextPath(event.currentTarget.href) + } + }, + lock: () => setIsLocked(true), + release: () => setIsLocked(false), + }), + [isLocked], + ) + + return ( + <> + + {children} + + setNextPath(null)}> + Are you sure you want to go? + + + You have unsaved changes. If you leave this page, your + changes will be lost. + + + + + + + + + ) +} diff --git a/src/components/index.ts b/src/components/index.ts index e10ea74..8410efa 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -20,3 +20,5 @@ export * from "./ErrorLoadingDataMessage" export {default as ErrorLoadingDataMessage} from "./ErrorLoadingDataMessage" export * from "./DecryptReport" export {default as DecryptReport} from "./DecryptReport" +export * from "./SimplePage" +export {default as SimplePage} from "./SimplePage" diff --git a/src/route-widgets/AliasDetailRoute/AliasPreferencesForm.tsx b/src/route-widgets/AliasDetailRoute/AliasPreferencesForm.tsx index 8b77504..dee1f1b 100644 --- a/src/route-widgets/AliasDetailRoute/AliasPreferencesForm.tsx +++ b/src/route-widgets/AliasDetailRoute/AliasPreferencesForm.tsx @@ -20,6 +20,7 @@ import { import {UpdateAliasData, updateAlias} from "~/apis" import {ErrorSnack, SuccessSnack} from "~/components" import {parseFastAPIError} from "~/utils" +import FormikAutoLockNavigation from "~/LockNavigationContext/FormikAutoLockNavigation" import SelectField from "~/route-widgets/SettingsRoute/SelectField" export interface AliasPreferencesFormProps { @@ -171,6 +172,7 @@ export default function AliasPreferencesForm({ + = { export default function NavigationButton({ section, }: NavigationButtonProps): ReactElement { + const {handleAnchorClick} = useContext(LockNavigationContext) const location = useLocation() const currentSection = PATH_SECTION_MAP[location.pathname.split("/")[1]] @@ -61,6 +63,7 @@ export default function NavigationButton({ path => PATH_SECTION_MAP[path] === section, ) ?? "/" } + onClick={handleAnchorClick} > {text} diff --git a/src/routes/AuthenticatedRoute.tsx b/src/routes/AuthenticatedRoute.tsx index f7eee28..8e955fb 100644 --- a/src/routes/AuthenticatedRoute.tsx +++ b/src/routes/AuthenticatedRoute.tsx @@ -4,6 +4,7 @@ import {Outlet} from "react-router-dom" import {Box, Grid, List, ListItem, Paper, useTheme} from "@mui/material" import {useUser} from "~/hooks" +import LockNavigationContextProvider from "~/LockNavigationContext/LockNavigationContextProvider" import NavigationButton, { NavigationSection, } from "~/route-widgets/AuthenticateRoute/NavigationButton" @@ -18,55 +19,57 @@ export default function AuthenticatedRoute(): ReactElement { useUser() return ( - + - - - - - {sections.map(key => ( - - - - ))} - - - - - + + - + + {sections.map(key => ( + + + + ))} + - + + + + + + + + - + - + ) }