@@ -5,25 +5,15 @@ import { useDialogs } from "@/components/ui/dialog-provider";
55
66const LEAVE_MESSAGE = "You have unsaved changes. Leave this page without saving?" ;
77
8+ function getCurrentHistoryIndex ( ) {
9+ const historyState = window . history . state as { idx ?: number } | null ;
10+ return typeof historyState ?. idx === "number" ? historyState . idx : null ;
11+ }
12+
813export function useUnsavedChangesGuard ( isDirty : boolean ) {
914 const { confirm } = useDialogs ( ) ;
10- const isHistoryGuardActiveRef = useRef ( false ) ;
11- const skipNextPopStateRef = useRef ( false ) ;
12- const pendingNavigationRef = useRef < ( ( ) => void ) | null > ( null ) ;
13-
14- const clearUnsavedChangesGuard = useCallback ( ( ) => {
15- if ( ! isHistoryGuardActiveRef . current ) {
16- return Promise . resolve ( ) ;
17- }
18-
19- isHistoryGuardActiveRef . current = false ;
20-
21- return new Promise < void > ( ( resolve ) => {
22- pendingNavigationRef . current = resolve ;
23- skipNextPopStateRef . current = true ;
24- window . history . back ( ) ;
25- } ) ;
26- } , [ ] ) ;
15+ const historyIndexRef = useRef < number | null > ( null ) ;
16+ const isRevertingNavigationRef = useRef ( false ) ;
2717
2818 const confirmDiscardChanges = useCallback ( async ( ) => {
2919 if ( ! isDirty ) {
@@ -40,63 +30,59 @@ export function useUnsavedChangesGuard(isDirty: boolean) {
4030 return false ;
4131 }
4232
43- await clearUnsavedChangesGuard ( ) ;
4433 return true ;
45- } , [ clearUnsavedChangesGuard , confirm , isDirty ] ) ;
34+ } , [ confirm , isDirty ] ) ;
4635
4736 useEffect ( ( ) => {
4837 if ( ! isDirty ) {
4938 return ;
5039 }
5140
41+ historyIndexRef . current = getCurrentHistoryIndex ( ) ;
42+ isRevertingNavigationRef . current = false ;
43+
5244 const handleBeforeUnload = ( event : BeforeUnloadEvent ) => {
5345 event . preventDefault ( ) ;
5446 // Needed for Chrome version <=119 to show the confirmation dialog
5547 event . returnValue = true ;
5648 } ;
5749
5850 const handlePopState = ( ) => {
59- if ( skipNextPopStateRef . current ) {
60- skipNextPopStateRef . current = false ;
61- pendingNavigationRef . current ?.( ) ;
62- pendingNavigationRef . current = null ;
63- return ;
51+ const nextHistoryIndex = getCurrentHistoryIndex ( ) ;
52+
53+ if ( ! isRevertingNavigationRef . current ) {
54+ const shouldLeave = window . confirm ( LEAVE_MESSAGE ) ;
55+
56+ if ( ! shouldLeave ) {
57+ if (
58+ historyIndexRef . current !== null &&
59+ nextHistoryIndex !== null &&
60+ historyIndexRef . current !== nextHistoryIndex
61+ ) {
62+ isRevertingNavigationRef . current = true ;
63+ window . history . go ( historyIndexRef . current > nextHistoryIndex ? 1 : - 1 ) ;
64+ return ;
65+ }
66+
67+ console . warn ( "Unable to determine history direction for unsaved-changes guard" ) ;
68+ }
6469 }
6570
66- if ( ! isHistoryGuardActiveRef . current ) {
67- return ;
68- }
69-
70- const shouldLeave = window . confirm ( LEAVE_MESSAGE ) ;
71-
72- if ( ! shouldLeave ) {
73- window . history . pushState ( window . history . state , "" , window . location . href ) ;
74- isHistoryGuardActiveRef . current = true ;
75- return ;
76- }
77-
78- isHistoryGuardActiveRef . current = false ;
79- skipNextPopStateRef . current = true ;
80- window . history . back ( ) ;
71+ isRevertingNavigationRef . current = false ;
72+ historyIndexRef . current = nextHistoryIndex ;
8173 } ;
8274
83- if ( ! isHistoryGuardActiveRef . current ) {
84- window . history . pushState ( window . history . state , "" , window . location . href ) ;
85- isHistoryGuardActiveRef . current = true ;
86- }
87-
8875 window . addEventListener ( "beforeunload" , handleBeforeUnload ) ;
8976 window . addEventListener ( "popstate" , handlePopState ) ;
9077
9178 return ( ) => {
92- isHistoryGuardActiveRef . current = false ;
79+ isRevertingNavigationRef . current = false ;
9380 window . removeEventListener ( "beforeunload" , handleBeforeUnload ) ;
9481 window . removeEventListener ( "popstate" , handlePopState ) ;
9582 } ;
9683 } , [ isDirty ] ) ;
9784
9885 return {
99- clearUnsavedChangesGuard,
10086 confirmDiscardChanges,
10187 } ;
10288}
0 commit comments