mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 20:05:06 +03:00
## Problem We want `ui-patterns` to contain only visual components but https://github.com/supabase/supabase/pull/43577 introduced a component with logic about navigation ## Solution Extract the logic part into a hook and use it in the component. Next step will be to remove this component and use `<DiscardChangesConfirmationDialog>` directly along the hook where needed ## How to test Check that the fixes from #43577 still work: On staging, for each case: - Authentication email templates - Observability reports - Edge functions creation - Edge functions edition Test: - Modify the template/report/function - Navigate away using either the sidebar link, the browser back button or closing the tab - Cancel navigation in the confirmation dialog - Navigation should be prevented and you should not loose your changes Test: - Modify the template/report/function - Navigate away using either the sidebar link, the browser back button or closing the tab - Confirm navigation in the confirmation dialog - Navigation should not be prevented and you should have lost your changes
92 lines
2.7 KiB
TypeScript
92 lines
2.7 KiB
TypeScript
import { useRouter } from 'next/router'
|
|
import { useEffect, useMemo, useState } from 'react'
|
|
|
|
import { useStaticEffectEvent } from '../useStaticEffectEvent'
|
|
import { BASE_PATH } from '@/lib/constants'
|
|
|
|
interface UsePreventNavigationOnUnsavedChangesOptions {
|
|
/*
|
|
* Boolean indicating whether there are changes that would be lost if users navigate to another
|
|
* page or close the browser tab
|
|
*/
|
|
hasChanges: boolean
|
|
}
|
|
|
|
interface UsePreventNavigationOnUnsavedChangesReturn {
|
|
/*
|
|
* Cancel the navigation and keep the changes
|
|
*/
|
|
handleCancel: () => void
|
|
/*
|
|
* Confirm the navigation and lose the changes
|
|
*/
|
|
handleConfirm: () => void
|
|
/*
|
|
* Boolean indicating whether UI to request users confirmation for the navigation should be
|
|
* displayed
|
|
*/
|
|
shouldConfirm: boolean
|
|
}
|
|
|
|
/*
|
|
* Hook that prevents navigation when users could lose their changes.
|
|
* It prevents both NextJS and browser navigation (such as when closing the tab)
|
|
*/
|
|
export const usePreventNavigationOnUnsavedChanges = ({
|
|
hasChanges,
|
|
}: UsePreventNavigationOnUnsavedChangesOptions): UsePreventNavigationOnUnsavedChangesReturn => {
|
|
const router = useRouter()
|
|
const [navigateUrl, setNavigateUrl] = useState<string>()
|
|
const [confirmNavigate, setConfirmNavigate] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
|
if (hasChanges) {
|
|
e.preventDefault()
|
|
e.returnValue = '' // deprecated, but older browsers still require this
|
|
}
|
|
}
|
|
|
|
const handleBrowseAway = (url: string) => {
|
|
if (hasChanges && !confirmNavigate) {
|
|
setNavigateUrl(url)
|
|
throw 'Route change declined' // Just to prevent the route change
|
|
return
|
|
}
|
|
setNavigateUrl(undefined)
|
|
}
|
|
window.addEventListener('beforeunload', handleBeforeUnload)
|
|
router.events.on('routeChangeStart', handleBrowseAway)
|
|
|
|
return () => {
|
|
window.removeEventListener('beforeunload', handleBeforeUnload)
|
|
router.events.off('routeChangeStart', handleBrowseAway)
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [confirmNavigate, hasChanges])
|
|
|
|
const handleCancel = useStaticEffectEvent(() => {
|
|
setNavigateUrl(undefined)
|
|
})
|
|
|
|
const handleConfirm = useStaticEffectEvent(() => {
|
|
setConfirmNavigate(true)
|
|
let urlToNavigate = navigateUrl ?? '/'
|
|
if (BASE_PATH && urlToNavigate.startsWith(BASE_PATH)) {
|
|
urlToNavigate = urlToNavigate.slice(BASE_PATH.length) || '/'
|
|
}
|
|
if (!urlToNavigate.startsWith('/')) urlToNavigate = `/${urlToNavigate}`
|
|
setNavigateUrl(undefined)
|
|
router.push(urlToNavigate)
|
|
})
|
|
|
|
return useMemo(
|
|
() => ({
|
|
handleCancel,
|
|
handleConfirm,
|
|
shouldConfirm: !!navigateUrl,
|
|
}),
|
|
[navigateUrl, handleCancel, handleConfirm]
|
|
)
|
|
}
|