Speed up timeline load with portrait thumbs, bootstrap API, and caching.
Add catalog bootstrap endpoint, portrait thumbnail pipeline, lazy queued timeline images, gzip compression, and 3D texture throttling with code-split VirtualGallery.
This commit is contained in:
@@ -10,7 +10,8 @@ import type {
|
||||
ArtistNavigation,
|
||||
MovementArtistGroup,
|
||||
} from '../types';
|
||||
import { galleryImageUrlWithRevision, imageUrl, api, preloadArtistImages } from '../api/client';
|
||||
import { galleryImageUrlWithRevision, imageUrl, api } from '../api/client';
|
||||
import { loadTextureQueued } from '../utils/textureLoadQueue';
|
||||
import { comparePaintingsChronological, paintingHasInfluenceLinks, paintingWallCaption } from '../utils/paintingUtils';
|
||||
import { cloneSurfaceTexture, getSurfaceTexture } from '../utils/galleryProceduralTextures';
|
||||
import { resolveMovementInteriorStyle, type MovementInteriorStyle, type GalleryWindowSpec } from '../data/movement-interior-styles';
|
||||
@@ -561,11 +562,9 @@ function usePaintingTexture(url: string | null) {
|
||||
setFailed(false);
|
||||
let disposed = false;
|
||||
let loaded: THREE.Texture | null = null;
|
||||
const loader = new THREE.TextureLoader();
|
||||
loader.setCrossOrigin('anonymous');
|
||||
loader.load(
|
||||
url,
|
||||
(tex) => {
|
||||
|
||||
loadTextureQueued(url)
|
||||
.then((tex) => {
|
||||
if (disposed) {
|
||||
tex.dispose();
|
||||
return;
|
||||
@@ -580,12 +579,10 @@ function usePaintingTexture(url: string | null) {
|
||||
tex.colorSpace = THREE.SRGBColorSpace;
|
||||
tex.anisotropy = 4;
|
||||
setTexture(tex);
|
||||
},
|
||||
undefined,
|
||||
() => {
|
||||
})
|
||||
.catch(() => {
|
||||
if (!disposed) setFailed(true);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return () => {
|
||||
disposed = true;
|
||||
@@ -1675,32 +1672,13 @@ export default function VirtualGallery(props: Props) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
const artistId = props.data.artist.id;
|
||||
(async () => {
|
||||
try {
|
||||
setSyncStatus('Syncing images…');
|
||||
const timeout = new Promise<void>((resolve) => setTimeout(resolve, 2000));
|
||||
await Promise.race([preloadArtistImages(artistId), timeout]);
|
||||
const fresh = await api.getArtist(artistId);
|
||||
if (!cancelled) {
|
||||
setPaintings(fresh.paintings);
|
||||
setPeriods(fresh.periods);
|
||||
const withImg = fresh.paintings.filter((p) => p.image_path || p.thumbnail_path).length;
|
||||
setSyncStatus(
|
||||
withImg < fresh.paintings.length
|
||||
? `${withImg} of ${fresh.paintings.length} works have images`
|
||||
: ''
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) setSyncStatus('');
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [hallKey, props.mode, initialPaintings.length, props.mode === 'artist' ? props.data.artist.id : null]);
|
||||
const withImg = initialPaintings.filter((p) => p.image_path || p.thumbnail_path).length;
|
||||
setSyncStatus(
|
||||
withImg < initialPaintings.length
|
||||
? `${withImg} of ${initialPaintings.length} works have images`
|
||||
: ''
|
||||
);
|
||||
}, [hallKey, props.mode, initialPaintings]);
|
||||
|
||||
const interiorStyle = useMemo(
|
||||
() => (isMovement ? resolveMovementInteriorStyle(props.data.movement) : undefined),
|
||||
@@ -2017,7 +1995,7 @@ export default function VirtualGallery(props: Props) {
|
||||
intensity={interiorStyle ? 0.85 : 0.65}
|
||||
color={interiorStyle?.sunLight ?? '#fff8f0'}
|
||||
castShadow
|
||||
shadow-mapSize={[2048, 2048]}
|
||||
shadow-mapSize={[1024, 1024]}
|
||||
/>
|
||||
<Suspense fallback={null}>
|
||||
<Environment
|
||||
|
||||
Reference in New Issue
Block a user