fix: map black screen — move Leaflet CSS to main.jsx, add ErrorBoundary

Leaflet CSS inside a lazy component has a load-order race with the map
initialization. Moving it to main.jsx ensures it's always ready.

Also adds a MapErrorBoundary so any Leaflet crash shows an error message
instead of unmounting the whole app (black screen), and filters out any
non-finite GPS coordinates before passing them to the map.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-08-18 12:19:27 -04:00
1 parent 26ef65a27e
commit e6e19ebaea
2 files changed
+26 -7

No files matched your search

+25 -7
View File
@@ -1,10 +1,22 @@
import { useEffect, useState, useMemo } from 'react';
import { useEffect, useState, useMemo, Component } from 'react';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import MarkerClusterGroup from 'react-leaflet-cluster';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { fetchMapPhotos } from '../api/photos.js';
class MapErrorBoundary extends Component {
state = { error: null };
static getDerivedStateFromError(e) { return { error: e }; }
render() {
if (this.state.error) return (
<div style={{ padding: 24, color: '#f87171', fontSize: 14 }}>
Map failed to load: {this.state.error.message}
</div>
);
return this.props.children;
}
}
// Custom dot icon — avoids Leaflet's broken default asset URLs in Vite
const dotIcon = (color = '#4d9eff') =>
L.divIcon({
@@ -37,10 +49,14 @@ export default function MapView({ onOpenPhoto }) {
.catch(e => setError(e.message));
}, []);
const validPoints = useMemo(() =>
(points ?? []).filter(p => Number.isFinite(p.lat) && Number.isFinite(p.lon)),
[points]);
const center = useMemo(() => {
if (!points?.length) return [20, 0];
return centroid(points);
}, [points]);
if (!validPoints.length) return [20, 0];
return centroid(validPoints);
}, [validPoints]);
if (error) return (
<p style={{ color: '#f87171', padding: 24, fontSize: 13 }}>{error}</p>
@@ -50,7 +66,7 @@ export default function MapView({ onOpenPhoto }) {
<p style={{ color: '#777', padding: 24, fontSize: 13 }}>Loading map…</p>
);
if (points.length === 0) return (
if (validPoints.length === 0) return (
<div style={{ textAlign: 'center', paddingTop: 60, color: '#777' }}>
<p style={{ fontSize: 15, marginBottom: 8 }}>No geotagged photos.</p>
<p style={{ fontSize: 13 }}>Photos with GPS metadata will appear here once indexed.</p>
@@ -58,6 +74,7 @@ export default function MapView({ onOpenPhoto }) {
);
return (
<MapErrorBoundary>
<div style={{ height: 'calc(100vh - 52px)' }}>
<MapContainer
center={center}
@@ -71,7 +88,7 @@ export default function MapView({ onOpenPhoto }) {
/>
<MarkerClusterGroup chunkedLoading>
{points.map(p => (
{validPoints.map(p => (
<Marker key={p.id} position={[p.lat, p.lon]} icon={ICON}>
<Popup maxWidth={220} className="luma-popup">
<div style={{ margin: 0, padding: 0 }}>
@@ -101,5 +118,6 @@ export default function MapView({ onOpenPhoto }) {
</MarkerClusterGroup>
</MapContainer>
</div>
</MapErrorBoundary>
);
}
+1
View File
@@ -1,6 +1,7 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import 'leaflet/dist/leaflet.css';
import App from './App.jsx';
createRoot(document.getElementById('root')).render(