68 lines
1.8 KiB
TypeScript
68 lines
1.8 KiB
TypeScript
import { useContext, useEffect, useRef, useState } from 'react'
|
|
import { ClockContext } from '../components/ClockContext.js'
|
|
|
|
/**
|
|
* Returns the clock time, updating at the given interval.
|
|
* Subscribes as non-keepAlive — won't keep the clock alive on its own,
|
|
* but updates whenever a keepAlive subscriber (e.g. the spinner)
|
|
* is driving the clock.
|
|
*
|
|
* Use this to drive pure time-based computations (shimmer position,
|
|
* frame index) from the shared clock.
|
|
*/
|
|
export function useAnimationTimer(intervalMs: number): number {
|
|
const clock = useContext(ClockContext)
|
|
const [time, setTime] = useState(() => clock?.now() ?? 0)
|
|
|
|
useEffect(() => {
|
|
if (!clock) return
|
|
|
|
let lastUpdate = clock.now()
|
|
|
|
const onChange = (): void => {
|
|
const now = clock.now()
|
|
if (now - lastUpdate >= intervalMs) {
|
|
lastUpdate = now
|
|
setTime(now)
|
|
}
|
|
}
|
|
|
|
return clock.subscribe(onChange, false)
|
|
}, [clock, intervalMs])
|
|
|
|
return time
|
|
}
|
|
|
|
/**
|
|
* Interval hook backed by the shared Clock.
|
|
*
|
|
* Unlike `useInterval` from `usehooks-ts` (which creates its own setInterval),
|
|
* this piggybacks on the single shared clock so all timers consolidate into
|
|
* one wake-up. Pass `null` for intervalMs to pause.
|
|
*/
|
|
export function useInterval(
|
|
callback: () => void,
|
|
intervalMs: number | null,
|
|
): void {
|
|
const callbackRef = useRef(callback)
|
|
callbackRef.current = callback
|
|
|
|
const clock = useContext(ClockContext)
|
|
|
|
useEffect(() => {
|
|
if (!clock || intervalMs === null) return
|
|
|
|
let lastUpdate = clock.now()
|
|
|
|
const onChange = (): void => {
|
|
const now = clock.now()
|
|
if (now - lastUpdate >= intervalMs) {
|
|
lastUpdate = now
|
|
callbackRef.current()
|
|
}
|
|
}
|
|
|
|
return clock.subscribe(onChange, false)
|
|
}, [clock, intervalMs])
|
|
}
|