Preload images, video, and artifact thumbnails on mount.
This commit is contained in:
@@ -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 */
|
||||
}
|
||||
}
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user