packages/flexbox/src/hooks/useBusyTiming.tsx
import { delay } from '@xylabs/delay'
import { useAsyncEffect } from '@xylabs/react-async-effect'
import {
useCallback, useMemo, useState,
} from 'react'
export const useBusyTiming = (busy?: boolean, busyMinimum = 0) => {
const [internalBusy, setInternalBusy] = useState(false)
const [busyStart, setBusyStart] = useState(0)
const timer = useMemo(
() => ({
evaluate: !busy && busyStart > 0,
initialize: busy && busyStart === 0,
terminated: !busy && busyStart === 0,
}),
[busy, busyStart],
)
const evaluateTimer = useCallback(
async (mounted: () => boolean) => {
const busyDuration = Date.now() - busyStart
if (busyDuration < busyMinimum) {
await delay(busyMinimum - busyDuration)
// Verify busy hasn't changed to true during the delay
if (!busy && internalBusy && mounted()) {
setBusyStart(0)
setInternalBusy(false)
}
} else if (mounted()) {
// busyMinimum exceeded
setBusyStart(0)
}
},
[busy, busyMinimum, busyStart, internalBusy],
)
useAsyncEffect(
async (mounted) => {
if (mounted() && busyMinimum === 0) {
// sync busy values if no minimum is set
setInternalBusy(!!busy)
return
}
if (busyMinimum) {
const {
initialize, evaluate, terminated,
} = timer
if (initialize && mounted()) {
setBusyStart(Date.now())
setInternalBusy(true)
return
}
if (evaluate) {
await evaluateTimer(mounted)
return
}
if (terminated) {
setInternalBusy(false)
return
}
}
},
[busy, busyStart, busyMinimum, internalBusy, evaluateTimer, timer],
)
return internalBusy
}