import { createContext, useCallback, useContext, useMemo, useState } from 'react' import { useDialogue } from '@/components/dialogues/DialogueProvider' import type { FC, PropsWithChildren } from 'react' type UnsavedChangesSource = { dirty: boolean discard: () => void | Promise } type UnsavedChangesGuardContextValue = { hasUnsavedChanges: boolean registerUnsavedChangesSource: ( source: UnsavedChangesSource | null, ) => void confirmDiscardNavigation: () => Promise } const UnsavedChangesGuardContext = createContext (null) export const UnsavedChangesGuardProvider: FC = ({ children }) => { const dialogue = useDialogue () const [source, setSource] = useState (null) const registerUnsavedChangesSource = useCallback (( nextSource: UnsavedChangesSource | null, ) => { setSource (nextSource) }, []) const confirmDiscardNavigation = useCallback (async (): Promise => { if (!(source?.dirty)) return true const confirmed = await dialogue.confirm ({ title: '未保存の変更があります', description: 'このまま移動すると、保存していない変更は失われます。', cancelText: 'このページに残る', confirmText: '変更を破棄して移動', variant: 'danger' }) if (!(confirmed)) return false await source.discard () return true }, [dialogue, source]) const value = useMemo (() => ({ hasUnsavedChanges: source?.dirty === true, registerUnsavedChangesSource, confirmDiscardNavigation, }), [confirmDiscardNavigation, registerUnsavedChangesSource, source?.dirty]) return ( {children} ) } export const useUnsavedChangesGuard = (): UnsavedChangesGuardContextValue => { const context = useContext (UnsavedChangesGuardContext) if (context == null) throw new Error ('UnsavedChangesGuardProvider が必要です.') return context }