Allow saved time unit redirect past blocker

This commit is contained in:
karilint
2026-05-27 18:47:37 +03:00
parent 699cbb125b
commit a4bf5c050a
3 changed files with 26 additions and 5 deletions
@@ -65,7 +65,10 @@ export const TimeUnitDetails = ({
}) })
unsavedChanges?.setDirty(false) unsavedChanges?.setDirty(false)
notify('Edited item successfully.') notify('Edited item successfully.')
return () => navigate(`/time-unit/${tu_name}`) return () => {
unsavedChanges?.allowNextNavigation?.()
navigate(`/time-unit/${tu_name}`)
}
} catch (e) { } catch (e) {
if (data) { if (data) {
setEditData(makeEditData(data)) setEditData(makeEditData(data))
@@ -1,4 +1,4 @@
import { useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react' import { useCallback, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
import { UNSAFE_DataRouterContext, useBlocker } from 'react-router-dom' import { UNSAFE_DataRouterContext, useBlocker } from 'react-router-dom'
import { UnsavedChangesDialog } from './UnsavedChangesDialog' import { UnsavedChangesDialog } from './UnsavedChangesDialog'
@@ -20,12 +20,13 @@ const unblockedState = {
} }
type BlockerState = typeof unblockedState | ReturnType<typeof useBlocker> type BlockerState = typeof unblockedState | ReturnType<typeof useBlocker>
type BlockerPredicate = Parameters<typeof useBlocker>[0]
const BlockerBridge = ({ const BlockerBridge = ({
shouldBlock, shouldBlock,
onChange, onChange,
}: { }: {
shouldBlock: boolean shouldBlock: BlockerPredicate
onChange: (blocker: ReturnType<typeof useBlocker>) => void onChange: (blocker: ReturnType<typeof useBlocker>) => void
}) => { }) => {
const blocker = useBlocker(shouldBlock) const blocker = useBlocker(shouldBlock)
@@ -45,6 +46,7 @@ export const UnsavedChangesProvider = ({
const [isDirty, setDirty] = useState(false) const [isDirty, setDirty] = useState(false)
const [message, setMessage] = useState(defaultMessage) const [message, setMessage] = useState(defaultMessage)
const [blocker, setBlocker] = useState<BlockerState>(unblockedState) const [blocker, setBlocker] = useState<BlockerState>(unblockedState)
const allowNextNavigationRef = useRef(false)
const dataRouterContext = useContext(UNSAFE_DataRouterContext) const dataRouterContext = useContext(UNSAFE_DataRouterContext)
const isSamePathNavigation = blocker.state === 'blocked' && blocker.location?.pathname === window.location.pathname const isSamePathNavigation = blocker.state === 'blocked' && blocker.location?.pathname === window.location.pathname
@@ -54,6 +56,20 @@ export const UnsavedChangesProvider = ({
setMessage(defaultMessage) setMessage(defaultMessage)
}, [defaultMessage]) }, [defaultMessage])
const allowNextNavigation = useCallback(() => {
allowNextNavigationRef.current = true
setDirty(false)
}, [])
const shouldBlockNavigation = useCallback(() => {
if (!isDirty) return false
if (allowNextNavigationRef.current) {
allowNextNavigationRef.current = false
return false
}
return true
}, [isDirty])
const handleConfirm = useCallback(() => { const handleConfirm = useCallback(() => {
if (blocker.state === 'blocked') { if (blocker.state === 'blocked') {
setDirty(false) setDirty(false)
@@ -87,13 +103,14 @@ export const UnsavedChangesProvider = ({
setDirty, setDirty,
setMessage, setMessage,
resetMessage, resetMessage,
allowNextNavigation,
}), }),
[isDirty, message, resetMessage] [allowNextNavigation, isDirty, message, resetMessage]
) )
return ( return (
<UnsavedChangesContext.Provider value={value}> <UnsavedChangesContext.Provider value={value}>
{dataRouterContext ? <BlockerBridge shouldBlock={isDirty} onChange={setBlocker} /> : null} {dataRouterContext ? <BlockerBridge shouldBlock={shouldBlockNavigation} onChange={setBlocker} /> : null}
{children} {children}
<UnsavedChangesDialog <UnsavedChangesDialog
open={showDialog} open={showDialog}
@@ -6,6 +6,7 @@ export type UnsavedChangesContextValue = {
setDirty: (dirty: boolean) => void setDirty: (dirty: boolean) => void
setMessage: (message: string) => void setMessage: (message: string) => void
resetMessage: () => void resetMessage: () => void
allowNextNavigation?: () => void
} }
export const UnsavedChangesContext = createContext<UnsavedChangesContextValue | null>(null) export const UnsavedChangesContext = createContext<UnsavedChangesContextValue | null>(null)