Add movement artist filter modal and fix large-hall texture blanks
Clicking a movement opens ArtistFilterModal to choose artists before the gallery. Large halls no longer permanently blank frames when texture loads exceed the overlay deadline; fall back thumb to full to on-demand API. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
ad1572b3aa
commit
0a5918c4dd
@@ -12,12 +12,14 @@ import InfluencesPage from '../pages/InfluencesPage';
|
||||
import ToursPage from '../pages/ToursPage';
|
||||
import UsersPage from '../pages/UsersPage';
|
||||
import CuratorLoginModal from '../components/CuratorLoginModal';
|
||||
import ArtistFilterModal from '../components/ArtistFilterModal';
|
||||
import ToursPopup from '../components/ToursPopup';
|
||||
import CatalogSearchBar from '../components/CatalogSearchBar';
|
||||
import LocaleSwitcher from '../components/LocaleSwitcher';
|
||||
import GalleryLoadingMarker from '../components/GalleryLoadingMarker';
|
||||
import '../components/CatalogSearchBar.css';
|
||||
import '../components/CuratorLoginModal.css';
|
||||
import '../components/ArtistFilterModal.css';
|
||||
import '../components/ToursPopup.css';
|
||||
import '../components/LocaleSwitcher.css';
|
||||
import '../pages/TranslationsPage.css';
|
||||
@@ -34,6 +36,7 @@ import type {
|
||||
PaintingDetail,
|
||||
MovementGalleryDetail,
|
||||
TourGalleryDetail,
|
||||
ArtistSummary,
|
||||
} from '../types';
|
||||
import { createViewChangeScheduler } from '../utils/timelineView';
|
||||
import { sortArtistPaintingsChronological } from '../utils/paintingUtils';
|
||||
@@ -178,6 +181,11 @@ export default function HomePage() {
|
||||
const [loginOpen, setLoginOpen] = useState(false);
|
||||
const [loginRedirect, setLoginRedirect] = useState<CuratorLoginRedirect>(null);
|
||||
const [toursPopupOpen, setToursPopupOpen] = useState(false);
|
||||
const [artistFilterModal, setArtistFilterModal] = useState<{
|
||||
movementId: number;
|
||||
movementName: string;
|
||||
artists: ArtistSummary[];
|
||||
} | null>(null);
|
||||
const effectiveDebugMode = debugMode && canImages;
|
||||
const [galleryRevision, setGalleryRevision] = useState(0);
|
||||
const viewRef = useRef(view);
|
||||
@@ -639,11 +647,43 @@ export default function HomePage() {
|
||||
};
|
||||
|
||||
const handleMovementClick = async (movementId: number) => {
|
||||
setGalleryEntryLoading('Loading artists…');
|
||||
try {
|
||||
const artists = await api.getMovementArtistsSummary(movementId);
|
||||
if (artists.length === 0) {
|
||||
setError('No artists in this movement.');
|
||||
return;
|
||||
}
|
||||
const movement = timelineData.movements.find((m) => m.id === movementId);
|
||||
setArtistFilterModal({
|
||||
movementId,
|
||||
movementName: movement?.name ?? 'Movement',
|
||||
artists,
|
||||
});
|
||||
} catch {
|
||||
setError('Failed to load movement artists.');
|
||||
} finally {
|
||||
setGalleryEntryLoading(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleArtistFilterProceed = async (selectedIds: Set<number>) => {
|
||||
if (!artistFilterModal) return;
|
||||
const { movementId } = artistFilterModal;
|
||||
setArtistFilterModal(null);
|
||||
setGalleryEntryLoading('Opening movement gallery…');
|
||||
try {
|
||||
await api.preloadMovementImages(movementId).catch(() => undefined);
|
||||
const data = await api.getMovementGallery(movementId);
|
||||
openMovementGallery(movementId, data);
|
||||
const filtered: MovementGalleryDetail = {
|
||||
...data,
|
||||
paintings: data.paintings.filter((p) => selectedIds.has(Number(p.artist_id))),
|
||||
};
|
||||
if (filtered.paintings.length === 0) {
|
||||
setError('No paintings for the selected artists.');
|
||||
return;
|
||||
}
|
||||
openMovementGallery(movementId, filtered);
|
||||
} catch {
|
||||
setError('Failed to load movement gallery.');
|
||||
} finally {
|
||||
@@ -1117,6 +1157,17 @@ export default function HomePage() {
|
||||
onSelectTour={(tourId) => void handleSelectPublishedTour(tourId)}
|
||||
/>
|
||||
|
||||
{artistFilterModal && (
|
||||
<ArtistFilterModal
|
||||
open
|
||||
movementName={artistFilterModal.movementName}
|
||||
artists={artistFilterModal.artists}
|
||||
portraitRevisions={portraitRevisions}
|
||||
onClose={() => setArtistFilterModal(null)}
|
||||
onProceed={(selectedIds) => void handleArtistFilterProceed(selectedIds)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{view.type === 'timeline' && (
|
||||
<div className="home-page">
|
||||
<header className="site-header">
|
||||
|
||||
Reference in New Issue
Block a user