fix: monter la carte une seule fois après chargement du style

Le style basculait de positron (URL) vers cartoonizeStyle() ~500 ms
après le premier rendu. Ce setStyle() déclenchait une réinscription
des sources/layers (react-map-gl v8 / maplibre-gl v5) qui échouait
silencieusement — les bancs de sargasses devenaient invisibles.

La carte est désormais montée UNE SEULE FOIS quand le style est prêt
(avec timeout 4 s → fallback positron). Plus de transition post-mount,
plus de race condition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gwadaking
2026-04-14 02:48:08 -04:00
parent 295548b04a
commit 79f834e1f0
2 changed files with 20 additions and 4 deletions

View File

@@ -1,3 +1,10 @@
/* ── Placeholder avant chargement du style ── */
.smap-placeholder {
width: 100%;
height: 100vh;
background: #3ab5e6; /* couleur océan = pas de flash blanc */
}
/* ── Logo ── */ /* ── Logo ── */
.smap-logo { .smap-logo {
position: absolute; position: absolute;

View File

@@ -154,7 +154,7 @@ export default function SargassesMap({ onSpotSelect, selectedSpotId, activeStep,
const [spotScores, setSpotScores] = useState({}); const [spotScores, setSpotScores] = useState({});
const [viewState, setViewState] = useState(INITIAL_VIEW); const [viewState, setViewState] = useState(INITIAL_VIEW);
const [layerOpacity, setLayerOpacity] = useState(1); const [layerOpacity, setLayerOpacity] = useState(1);
const [mapStyle, setMapStyle] = useState(FALLBACK_STYLE); const [mapStyle, setMapStyle] = useState(null); // null = style not yet ready
const [dataDate, setDataDate] = useState(null); const [dataDate, setDataDate] = useState(null);
const tilesOrigin = window.location.origin; const tilesOrigin = window.location.origin;
@@ -170,12 +170,17 @@ export default function SargassesMap({ onSpotSelect, selectedSpotId, activeStep,
.catch(() => {}); .catch(() => {});
}, []); }, []);
// Load cartoon map style // Load cartoon map style once — map is only mounted when style is ready,
// preventing the setStyle() race condition that silently drops custom sources.
useEffect(() => { useEffect(() => {
fetch('https://tiles.openfreemap.org/styles/bright') const ctrl = new AbortController();
const timeout = setTimeout(() => ctrl.abort(), 4000); // 4 s timeout
fetch('https://tiles.openfreemap.org/styles/bright', { signal: ctrl.signal })
.then(r => r.json()) .then(r => r.json())
.then(style => setMapStyle(cartoonizeStyle(style))) .then(style => setMapStyle(cartoonizeStyle(style)))
.catch(() => {}); // keep positron on error .catch(() => setMapStyle(FALLBACK_STYLE)) // positron fallback on error/timeout
.finally(() => clearTimeout(timeout));
return () => ctrl.abort();
}, []); }, []);
// Load coastal spots once // Load coastal spots once
@@ -221,6 +226,10 @@ export default function SargassesMap({ onSpotSelect, selectedSpotId, activeStep,
const OBS_STYLES = makeLayerStyles(false, layerOpacity); const OBS_STYLES = makeLayerStyles(false, layerOpacity);
const FORE_STYLES = makeLayerStyles(true, layerOpacity); const FORE_STYLES = makeLayerStyles(true, layerOpacity);
if (!mapStyle) {
return <div className="smap-placeholder" />;
}
return ( return (
<Map <Map
ref={mapRef} ref={mapRef}