import { useAuthState } from "@kobra-dev/react-firebase-auth-hooks/auth" ; import React , { createContext } from "react" ; import { forwardRef } from "react" ; import { useCallback } from "react" ; import { useEffect } from "react" ; import { useImperativeHandle } from "react" ; import { useContext } from "react" ; import { useState } from "react" ; import firebase from "../utils/firebase" ; type SaveFunction = { ( ) : void ; } ; const SaveContext = createContext < SaveFunction > ( ( ) => { } ) ; const AutosaveStatusContext = createContext < { loading : boolean ; lastSaveTime : Date ; haveAttemptedToSave : boolean ; } > ( { loading : false , lastSaveTime : new Date ( ) , haveAttemptedToSave : false } ) ; const BATCH_DURATION = 1000 ; let onFinishSave = undefined ; type ProvidedSaveFn = ( ) => Promise < boolean > ; let providedSaveFn = undefined ; export type AutosaverProviderRef = { finishSave : ( ) => Promise < void > ; reset : ( ) => void ; fakeSave : ( ) => void ; save : ( ) => void ; } ; function AutosaverProvider ( props , ref ) { const [ collectingBatch , setCollectingBatch ] = useState ( false ) ; const [ loading , setLoading ] = useState ( false ) ; const [ lastSaveTime , setLastSaveTime ] = useState < Date | undefined > ( undefined ) ; const [ haveAttemptedToSave , setHaveAttemptedToSave ] = useState ( false ) ; const [ user ] = useAuthState ( firebase . auth ( ) ) ; useEffect ( ( ) => { if ( ! user ) { setHaveAttemptedToSave ( loading ) ; setLastSaveTime ( undefined ) ; } else if ( haveAttemptedToSave && ! lastSaveTime ) { save ( ) ; } } , [ user ] ) ; const save = useCallback ( ( ) => { setHaveAttemptedToSave ( true ) ; if ( ! props . canSave ) return ; if ( ! collectingBatch ) { setLoading ( true ) ; setCollectingBatch ( true ) ; setTimeout ( async ( ) => { setCollectingBatch ( false ) ; const newLastSaveTime = new Date ( ) ; if ( await providedSaveFn ( ) ) { setLastSaveTime ( newLastSaveTime ) ; } setLoading ( false ) ; if ( onFinishSave ) { onFinishSave ( ) ; onFinishSave = undefined ; } } , BATCH_DURATION ) ; } } , [ collectingBatch , setLoading , setCollectingBatch , setLastSaveTime , props . canSave ] ) ; useImperativeHandle < unknown , AutosaverProviderRef > ( ref , ( ) => ( { finishSave ( ) { return new Promise < void > ( ( res ) => { if ( ! loading ) { res ( ) ; } else { onFinishSave = res ; } } ) ; } , reset ( ) { setLastSaveTime ( undefined ) ; setHaveAttemptedToSave ( false ) ; } , fakeSave ( ) { setLastSaveTime ( new Date ( ) ) ; setHaveAttemptedToSave ( true ) ; } , save } ) , [ loading , setLastSaveTime , save ] ) ; useEffect ( ( ) => { providedSaveFn = props . saveFn ; } , [ props . saveFn ] ) ; return ( < SaveContext . Provider value = { save } > < AutosaveStatusContext . Provider value = { { loading , lastSaveTime , haveAttemptedToSave } } > { props . children } < /AutosaveStatusContext.Provider> < /SaveContext.Provider>); } export default forwardRef ( AutosaverProvider ) ; export const useSave = ( ) => useContext ( SaveContext ) ; export const useAutosaveStatus = ( ) => useContext ( AutosaveStatusContext ) ;