Preload images, video, and artifact thumbnails on mount.

This commit is contained in:
2026-06-11 11:07:25 -04:00
parent 87e98cdd16
commit 7e75a9da61
3 changed files with 58 additions and 2 deletions
+53
View File
@@ -0,0 +1,53 @@
import type { MediaConfig } from './mediaConfig';
import { preloadAudioAssets } from './mediaSounds';
import { ARTIFACTS_BASE_URL } from './puzzle2Config';
const preloadedUrls = new Set<string>();
function preloadOnce(url: string, load: () => void): void {
if (preloadedUrls.has(url)) return;
preloadedUrls.add(url);
load();
}
export function preloadImage(url: string): void {
preloadOnce(url, () => {
const img = new Image();
img.src = url;
});
}
export function preloadVideo(url: string): void {
preloadOnce(url, () => {
const video = document.createElement('video');
video.preload = 'auto';
video.src = url;
video.load();
});
}
/** Warm cache for config-driven audio, images, and video. */
export function preloadMediaAssets(config: MediaConfig): void {
preloadAudioAssets(config);
preloadImage(config.mapImageUrl);
preloadImage(config.tedCelebrateImageUrl);
preloadVideo(config.unlockVideoUrl);
}
export function preloadArtifactImages(entries: readonly { file: string }[]): void {
for (const entry of entries) {
preloadImage(`${ARTIFACTS_BASE_URL}/${encodeURIComponent(entry.file)}`);
}
}
/** Fetch artifact manifest and preload all puzzle-2 thumbnails. */
export async function preloadArtifactManifest(): Promise<void> {
try {
const res = await fetch(`${ARTIFACTS_BASE_URL}/manifest.json`);
if (!res.ok) return;
const data = (await res.json()) as { file: string }[];
preloadArtifactImages(data);
} catch {
/* manifest unavailable until setup script runs */
}
}
+2
View File
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { preloadArtifactImages } from './mediaPreload';
import { ARTIFACTS_BASE_URL } from './puzzle2Config';
export interface ArtifactEntry {
@@ -19,6 +20,7 @@ export function useArtifactManifest(): ArtifactEntry[] | null {
.then((res) => (res.ok ? res.json() : null))
.then((data: ArtifactEntry[] | null) => {
if (cancelled || !data) return;
preloadArtifactImages(data);
setManifest(data);
})
.catch(() => {
+3 -2
View File
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { DEFAULT_MEDIA_CONFIG, mergeMediaConfig, type MediaConfig } from './mediaConfig';
import { preloadAudioAssets } from './mediaSounds';
import { preloadArtifactManifest, preloadMediaAssets } from './mediaPreload';
export function useMediaConfig(): MediaConfig {
const [config, setConfig] = useState<MediaConfig>(DEFAULT_MEDIA_CONFIG);
@@ -24,7 +24,8 @@ export function useMediaConfig(): MediaConfig {
}, []);
useEffect(() => {
preloadAudioAssets(config);
preloadMediaAssets(config);
void preloadArtifactManifest();
}, [config]);
return config;