fix: moving focus as episodes load, loading indicator
This commit is contained in:
46
src/hooks/useHeldFlag.ts
Normal file
46
src/hooks/useHeldFlag.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* useHeldFlag — keep a boolean true for a minimum time after it falls.
|
||||
*
|
||||
* A warm-cache fetch-more load begins and ends between two renderer
|
||||
* frames: the raw `isLoadingMore` signal flips true→false without a
|
||||
* single paint, so the "[Fetch More]" → spinner swap never appears and
|
||||
* the press looks like a no-op. Components rendering a spinner for such
|
||||
* bursts read through this hook instead of the raw signal — the spinner
|
||||
* stays up (and animating) for at least `minMs` after the load ends,
|
||||
* guaranteeing several painted frames.
|
||||
*
|
||||
* Deliberately a display-layer concern: the store's own `isLoadingMore`
|
||||
* keeps its exact load-window semantics (guards, tests) and only the
|
||||
* rendered indicators are held.
|
||||
*/
|
||||
import { createSignal, createEffect, onCleanup } from "solid-js";
|
||||
|
||||
export function useHeldFlag(
|
||||
source: () => boolean,
|
||||
minMs = 250,
|
||||
): () => boolean {
|
||||
const [held, setHeld] = createSignal(false);
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
const clearTimer = () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
if (source()) {
|
||||
// (Re)rising edge: show immediately; a pending fall from an
|
||||
// earlier burst is cancelled.
|
||||
clearTimer();
|
||||
if (!held()) setHeld(true);
|
||||
} else if (held()) {
|
||||
// Falling edge: hold the flag up for the remaining window.
|
||||
clearTimer();
|
||||
timer = setTimeout(() => setHeld(false), minMs);
|
||||
}
|
||||
});
|
||||
|
||||
onCleanup(clearTimer);
|
||||
return held;
|
||||
}
|
||||
Reference in New Issue
Block a user