Improve timeline UX and add catalog restore scripts for paintings and portraits.
Load the home-page catalog once with a lightweight artists API, batch pan/zoom updates per frame, use dynamic year labels, and speed up movement-flow zoom. Add sync-image-paths and fetch-artist-images plus docs for the post-seed pipeline.
This commit is contained in:
@@ -8,6 +8,7 @@ import ArtistBio from '../components/ArtistBio';
|
||||
import CheckupPage from '../pages/CheckupPage';
|
||||
import { api, type FixPaintingImageResult, type FixArtistPortraitResult } from '../api/client';
|
||||
import type { TimelineData, Artist, ArtistDetail, Painting, PaintingDetail, MovementGalleryDetail } from '../types';
|
||||
import { createViewChangeScheduler } from '../utils/timelineView';
|
||||
import { sortArtistPaintingsChronological } from '../utils/paintingUtils';
|
||||
import { readDebugMode, readDebugShowMore, writeDebugMode, writeDebugShowMore } from '../utils/debugMode';
|
||||
import './HomePage.css';
|
||||
@@ -119,18 +120,6 @@ export default function HomePage() {
|
||||
} | null>(null);
|
||||
const detailReturnToRef = useRef<View>({ type: 'timeline' });
|
||||
|
||||
useEffect(() => {
|
||||
api.getBounds()
|
||||
.then((b) => {
|
||||
const min = b.min_year ?? -800;
|
||||
const max = b.max_year ?? 2025;
|
||||
setBounds({ min, max });
|
||||
setViewStart(min);
|
||||
setViewEnd(max);
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (view.type === 'gallery') {
|
||||
setGallerySession({ kind: 'artist', artistId: view.artistId, data: view.data });
|
||||
@@ -141,31 +130,55 @@ export default function HomePage() {
|
||||
}
|
||||
}, [view]);
|
||||
|
||||
const loadTimelineData = useCallback(async (start: number, end: number) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const [timeline, artistList] = await Promise.all([
|
||||
api.getTimeline(start, end),
|
||||
api.getArtists(start, end),
|
||||
]);
|
||||
setTimelineData(timeline);
|
||||
setArtists(artistList);
|
||||
setError(null);
|
||||
} catch {
|
||||
setError('Could not load gallery data. Is the server running?');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
// Load full catalog once — pan/zoom filters client-side (MovementBands, Timeline).
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const b = await api.getBounds();
|
||||
const min = b.min_year ?? -800;
|
||||
const max = b.max_year ?? 2025;
|
||||
if (cancelled) return;
|
||||
|
||||
setBounds({ min, max });
|
||||
setViewStart(min);
|
||||
setViewEnd(max);
|
||||
|
||||
const [timeline, artistList] = await Promise.all([
|
||||
api.getTimeline(min, max),
|
||||
api.getTimelineArtists(),
|
||||
]);
|
||||
if (cancelled) return;
|
||||
|
||||
setTimelineData(timeline);
|
||||
setArtists(artistList);
|
||||
setError(null);
|
||||
} catch {
|
||||
if (!cancelled) setError('Could not load gallery data. Is the server running?');
|
||||
} finally {
|
||||
if (!cancelled) setLoading(false);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadTimelineData(viewStart, viewEnd);
|
||||
}, [viewStart, viewEnd, loadTimelineData]);
|
||||
const viewChangeScheduler = useRef(
|
||||
createViewChangeScheduler((start, end) => {
|
||||
setViewStart(start);
|
||||
setViewEnd(end);
|
||||
})
|
||||
);
|
||||
|
||||
const handleViewChange = (start: number, end: number) => {
|
||||
setViewStart(start);
|
||||
setViewEnd(end);
|
||||
};
|
||||
useEffect(() => () => viewChangeScheduler.current.cancel(), []);
|
||||
|
||||
const handleViewChange = useCallback((start: number, end: number) => {
|
||||
viewChangeScheduler.current.schedule(start, end);
|
||||
}, []);
|
||||
|
||||
const toggleDebugMode = () => {
|
||||
setDebugMode((prev) => {
|
||||
@@ -683,7 +696,7 @@ export default function HomePage() {
|
||||
|
||||
{error && <div className="error-banner">{error}</div>}
|
||||
|
||||
{loading ? (
|
||||
{loading && timelineData.movements.length === 0 ? (
|
||||
<div className="loading home-movements-section">Loading art history...</div>
|
||||
) : (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user