mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-19 15:55:26 +02:00
added LockNavigationContextProvider.tsx
This commit is contained in:
parent
04ede26ac6
commit
ca9eb78330
27
src/LockNavigationContext/FormikAutoLockNavigation.tsx
Normal file
27
src/LockNavigationContext/FormikAutoLockNavigation.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import {FormikContextType} from "formik"
|
||||||
|
import {useContext, useEffect} from "react"
|
||||||
|
|
||||||
|
import LockNavigationContext from "./LockNavigationContext"
|
||||||
|
|
||||||
|
export interface LockNavigationContextProviderProps {
|
||||||
|
formik: FormikContextType<any>
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
27
src/LockNavigationContext/LockNavigationContext.ts
Normal file
27
src/LockNavigationContext/LockNavigationContext.ts
Normal file
@ -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<HTMLAnchorElement>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const LockNavigationContext = createContext<LockNavigationContextType>({
|
||||||
|
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
|
91
src/LockNavigationContext/LockNavigationContextProvider.tsx
Normal file
91
src/LockNavigationContext/LockNavigationContextProvider.tsx
Normal file
@ -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<boolean>(false)
|
||||||
|
const [nextPath, setNextPath] = useState<string | null>(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<HTMLAnchorElement>) => {
|
||||||
|
if (isLocked) {
|
||||||
|
event.preventDefault()
|
||||||
|
setNextPath(event.currentTarget.href)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lock: () => setIsLocked(true),
|
||||||
|
release: () => setIsLocked(false),
|
||||||
|
}),
|
||||||
|
[isLocked],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LockNavigationContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</LockNavigationContext.Provider>
|
||||||
|
<Dialog open={showDialog} onClose={() => setNextPath(null)}>
|
||||||
|
<DialogTitle>Are you sure you want to go?</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
You have unsaved changes. If you leave this page, your
|
||||||
|
changes will be lost.
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
startIcon={<TiCancel />}
|
||||||
|
onClick={() => setNextPath(null)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<MdLogout />}
|
||||||
|
onClick={() => {
|
||||||
|
const path = new URL(nextPath as string).pathname
|
||||||
|
setNextPath(null)
|
||||||
|
setIsLocked(false)
|
||||||
|
navigate(path)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Leave
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -20,3 +20,5 @@ export * from "./ErrorLoadingDataMessage"
|
|||||||
export {default as ErrorLoadingDataMessage} from "./ErrorLoadingDataMessage"
|
export {default as ErrorLoadingDataMessage} from "./ErrorLoadingDataMessage"
|
||||||
export * from "./DecryptReport"
|
export * from "./DecryptReport"
|
||||||
export {default as DecryptReport} from "./DecryptReport"
|
export {default as DecryptReport} from "./DecryptReport"
|
||||||
|
export * from "./SimplePage"
|
||||||
|
export {default as SimplePage} from "./SimplePage"
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
import {UpdateAliasData, updateAlias} from "~/apis"
|
import {UpdateAliasData, updateAlias} from "~/apis"
|
||||||
import {ErrorSnack, SuccessSnack} from "~/components"
|
import {ErrorSnack, SuccessSnack} from "~/components"
|
||||||
import {parseFastAPIError} from "~/utils"
|
import {parseFastAPIError} from "~/utils"
|
||||||
|
import FormikAutoLockNavigation from "~/LockNavigationContext/FormikAutoLockNavigation"
|
||||||
import SelectField from "~/route-widgets/SettingsRoute/SelectField"
|
import SelectField from "~/route-widgets/SettingsRoute/SelectField"
|
||||||
|
|
||||||
export interface AliasPreferencesFormProps {
|
export interface AliasPreferencesFormProps {
|
||||||
@ -171,6 +172,7 @@ export default function AliasPreferencesForm({
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</form>
|
</form>
|
||||||
|
<FormikAutoLockNavigation formik={formik} />
|
||||||
<ErrorSnack message={formik.errors.detail} />
|
<ErrorSnack message={formik.errors.detail} />
|
||||||
<SuccessSnack
|
<SuccessSnack
|
||||||
message={isSuccess && "Updated Alias successfully!"}
|
message={isSuccess && "Updated Alias successfully!"}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {ReactElement} from "react"
|
import {ReactElement, useContext} from "react"
|
||||||
import {BiStats} from "react-icons/bi"
|
import {BiStats} from "react-icons/bi"
|
||||||
import {MdSettings} from "react-icons/md"
|
import {MdSettings} from "react-icons/md"
|
||||||
import {FaMask} from "react-icons/fa"
|
import {FaMask} from "react-icons/fa"
|
||||||
@ -7,6 +7,7 @@ import {Link as RouterLink, useLocation} from "react-router-dom"
|
|||||||
import {Button} from "@mui/material"
|
import {Button} from "@mui/material"
|
||||||
import {mdiTextBoxMultiple} from "@mdi/js/commonjs/mdi"
|
import {mdiTextBoxMultiple} from "@mdi/js/commonjs/mdi"
|
||||||
import Icon from "@mdi/react"
|
import Icon from "@mdi/react"
|
||||||
|
import LockNavigationContext from "~/LockNavigationContext/LockNavigationContext"
|
||||||
|
|
||||||
export enum NavigationSection {
|
export enum NavigationSection {
|
||||||
Overview,
|
Overview,
|
||||||
@ -43,6 +44,7 @@ const PATH_SECTION_MAP: Record<string, NavigationSection> = {
|
|||||||
export default function NavigationButton({
|
export default function NavigationButton({
|
||||||
section,
|
section,
|
||||||
}: NavigationButtonProps): ReactElement {
|
}: NavigationButtonProps): ReactElement {
|
||||||
|
const {handleAnchorClick} = useContext(LockNavigationContext)
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
|
||||||
const currentSection = PATH_SECTION_MAP[location.pathname.split("/")[1]]
|
const currentSection = PATH_SECTION_MAP[location.pathname.split("/")[1]]
|
||||||
@ -61,6 +63,7 @@ export default function NavigationButton({
|
|||||||
path => PATH_SECTION_MAP[path] === section,
|
path => PATH_SECTION_MAP[path] === section,
|
||||||
) ?? "/"
|
) ?? "/"
|
||||||
}
|
}
|
||||||
|
onClick={handleAnchorClick}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -4,6 +4,7 @@ import {Outlet} from "react-router-dom"
|
|||||||
import {Box, Grid, List, ListItem, Paper, useTheme} from "@mui/material"
|
import {Box, Grid, List, ListItem, Paper, useTheme} from "@mui/material"
|
||||||
|
|
||||||
import {useUser} from "~/hooks"
|
import {useUser} from "~/hooks"
|
||||||
|
import LockNavigationContextProvider from "~/LockNavigationContext/LockNavigationContextProvider"
|
||||||
import NavigationButton, {
|
import NavigationButton, {
|
||||||
NavigationSection,
|
NavigationSection,
|
||||||
} from "~/route-widgets/AuthenticateRoute/NavigationButton"
|
} from "~/route-widgets/AuthenticateRoute/NavigationButton"
|
||||||
@ -18,6 +19,7 @@ export default function AuthenticatedRoute(): ReactElement {
|
|||||||
useUser()
|
useUser()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<LockNavigationContextProvider>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
@ -68,5 +70,6 @@ export default function AuthenticatedRoute(): ReactElement {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
</LockNavigationContextProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user