56 lines
1.9 KiB
TypeScript
56 lines
1.9 KiB
TypeScript
/** Pull every card thumbnail into the browser's caches once, while the kiosk is idle.
|
|
*
|
|
* Cover URLs carry a version and are served `immutable`, so once a thumbnail has been
|
|
* fetched the browser never asks for it again - but the *first* search over the whole
|
|
* library would otherwise fetch, and decode, hundreds of them while the user waits. This
|
|
* does that work up front, a few at a time in idle periods. The `Image`s are held so
|
|
* their decoded bitmaps stay resident: RAM is not the constraint on the machine this is
|
|
* for (`?pi=1`, see lib/lowPower.ts), the main thread during a keystroke is.
|
|
*/
|
|
|
|
import { useEffect } from "react";
|
|
|
|
import { coverUrl } from "../api/client";
|
|
import type { Album } from "../api/types";
|
|
|
|
const BATCH = 6;
|
|
|
|
const idle = (run: () => void): void => {
|
|
if (typeof requestIdleCallback === "function") requestIdleCallback(run, { timeout: 2000 });
|
|
else setTimeout(run, 50);
|
|
};
|
|
|
|
/** Module-level on purpose: dropping these would let the browser discard what they hold. */
|
|
const held = new Map<string, HTMLImageElement>();
|
|
|
|
export function useCoverWarmup(albums: Album[], enabled: boolean): void {
|
|
useEffect(() => {
|
|
if (!enabled) return;
|
|
let cancelled = false;
|
|
const pending = albums.filter((a) => a.has_cover && !a.locked);
|
|
let next = 0;
|
|
|
|
const step = () => {
|
|
if (cancelled || next >= pending.length) return;
|
|
const batch = pending.slice(next, next + BATCH);
|
|
next += BATCH;
|
|
void Promise.all(
|
|
batch.map((album) => {
|
|
const url = coverUrl(album.id, { size: "thumb", version: album.cover_v });
|
|
if (held.has(url)) return undefined;
|
|
const img = new Image();
|
|
img.decoding = "async";
|
|
img.src = url;
|
|
held.set(url, img);
|
|
return img.decode().catch(() => undefined);
|
|
}),
|
|
).then(() => idle(step));
|
|
};
|
|
idle(step);
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [albums, enabled]);
|
|
}
|