Reviewed-on: #397 Co-authored-by: miteruzo <miteruzo@naver.com> Co-committed-by: miteruzo <miteruzo@naver.com>
このコミットはPull リクエスト #397 でマージされました.
このコミットが含まれているのは:
@@ -0,0 +1,69 @@
|
||||
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<void> }
|
||||
|
||||
type UnsavedChangesGuardContextValue = {
|
||||
hasUnsavedChanges: boolean
|
||||
registerUnsavedChangesSource: (
|
||||
source: UnsavedChangesSource | null,
|
||||
) => void
|
||||
confirmDiscardNavigation: () => Promise<boolean> }
|
||||
|
||||
const UnsavedChangesGuardContext =
|
||||
createContext<UnsavedChangesGuardContextValue | null> (null)
|
||||
|
||||
|
||||
export const UnsavedChangesGuardProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
const dialogue = useDialogue ()
|
||||
const [source, setSource] = useState<UnsavedChangesSource | null> (null)
|
||||
|
||||
const registerUnsavedChangesSource = useCallback ((
|
||||
nextSource: UnsavedChangesSource | null,
|
||||
) => {
|
||||
setSource (nextSource)
|
||||
}, [])
|
||||
|
||||
const confirmDiscardNavigation = useCallback (async (): Promise<boolean> => {
|
||||
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<UnsavedChangesGuardContextValue> (() => ({
|
||||
hasUnsavedChanges: source?.dirty === true,
|
||||
registerUnsavedChangesSource,
|
||||
confirmDiscardNavigation,
|
||||
}), [confirmDiscardNavigation, registerUnsavedChangesSource, source?.dirty])
|
||||
|
||||
return (
|
||||
<UnsavedChangesGuardContext.Provider value={value}>
|
||||
{children}
|
||||
</UnsavedChangesGuardContext.Provider>)
|
||||
}
|
||||
|
||||
|
||||
export const useUnsavedChangesGuard = (): UnsavedChangesGuardContextValue => {
|
||||
const context = useContext (UnsavedChangesGuardContext)
|
||||
|
||||
if (context == null)
|
||||
throw new Error ('UnsavedChangesGuardProvider が必要です.')
|
||||
|
||||
return context
|
||||
}
|
||||
新しい課題から参照
ユーザをブロックする