Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / components / hooks / usePreventLeave.tsx
blob9211b4675c18608eb0a751e3f59efcedc7471530
1 import type { ReactNode } from 'react';
2 import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
4 import useBeforeUnload from './useBeforeUnload';
6 const PreventLeaveContext = createContext<{
7     clearPendingTasks: () => void;
8     preventLeave: <T>(task: Promise<T>) => Promise<T>;
9 } | null>(null);
11 export default function usePreventLeave() {
12     const preventLeaveState = useContext(PreventLeaveContext);
14     if (!preventLeaveState) {
15         throw new Error('PreventLeaveContext is not initialized, wrap the app with PreventLeaveProvider');
16     }
18     return preventLeaveState;
21 export const PreventLeaveProvider = ({ children }: { children: ReactNode }) => {
22     const pendingTasks = useRef(new Set<Promise<any>>());
23     const [hasPendingTasks, setHasPendingTasks] = useState(false);
25     const clearPendingTasks = useCallback(() => {
26         pendingTasks.current.clear();
27     }, []);
29     const preventLeave = useCallback(
30         <T,>(task: Promise<T>) => {
31             pendingTasks.current.add(task);
33             if (!hasPendingTasks) {
34                 setHasPendingTasks(true);
35             }
37             const cleanup = () => {
38                 pendingTasks.current.delete(task);
40                 if (!pendingTasks.current.size) {
41                     setHasPendingTasks(false);
42                 }
43             };
45             task.then(cleanup).catch(cleanup);
46             return task;
47         },
48         [hasPendingTasks]
49     );
51     useBeforeUnload(hasPendingTasks);
53     useEffect(() => {
54         return () => clearPendingTasks();
55     }, []);
57     return (
58         <PreventLeaveContext.Provider value={{ preventLeave, clearPendingTasks }}>
59             {children}
60         </PreventLeaveContext.Provider>
61     );