1 import { useCallback, useMemo, useRef, useState } from 'react';
3 import type { ModalProps } from '@proton/components/components/modalTwo/Modal';
4 import type { MaybePromise } from '@proton/pass/types';
5 import noop from '@proton/utils/noop';
7 import { useRerender } from './useRerender';
9 export class AsyncModalAbortedError extends Error {}
10 type ModalState<T> = T & Omit<ModalProps, 'onSubmit'> & { loading: boolean };
11 type HookOptions<T> = { getInitialModalState: () => T };
12 export type UseAsyncModalHandle<V, T> = (options: UseAsyncModalHandlerOptions<V, T>) => Promise<void>;
14 type UseAsyncModalHandlerOptions<V, T> = Partial<T> & {
15 onError?: (error: unknown) => MaybePromise<void>;
16 onAbort?: () => MaybePromise<void>;
17 onSubmit: (value: V) => MaybePromise<unknown>;
20 export const useAsyncModalHandles = <V, T = {}>(options: HookOptions<T>) => {
21 const [key, next] = useRerender();
22 const [state, setState] = useState<ModalState<T>>({
23 ...options.getInitialModalState(),
28 const resolver = useRef<(value: V) => void>(noop);
29 const rejector = useRef<(error: unknown) => void>(noop);
30 const resolve = useCallback((value: V) => resolver.current?.(value), []);
32 const abort = useCallback(() => rejector.current?.(new AsyncModalAbortedError()), []);
34 const handler = useCallback<UseAsyncModalHandle<V, T>>(
38 const { onSubmit, onError, onAbort, ...modalOptions } = opts;
39 setState({ ...options.getInitialModalState(), ...modalOptions, open: true, loading: false });
42 const value = await new Promise<V>((resolve, reject) => {
43 resolver.current = resolve;
44 rejector.current = reject;
47 setState((state) => ({ ...state, loading: true }));
48 await onSubmit(value);
50 setState((state) => ({ ...state, loading: false }));
52 if (error instanceof AsyncModalAbortedError) await onAbort?.();
53 else await onError?.(error);
55 setState((state) => ({ ...state, open: false, loading: false }));
58 [options.getInitialModalState]
61 return useMemo(() => ({ handler, abort, state, resolver: resolve, key }), [handler, abort, state, key]);