diff --git a/web/src/components/Map.tsx b/web/src/components/Map.tsx index 4b1cdf3..8f382f2 100644 --- a/web/src/components/Map.tsx +++ b/web/src/components/Map.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, useEffect, useMemo } from "react"; +import React, { useRef, useState, useEffect, useMemo, useCallback } from "react"; import ReactMap, { Source, Layer } from "react-map-gl/maplibre"; import type { FeatureCollection } from "geojson"; import "maplibre-gl/dist/maplibre-gl.css"; @@ -26,6 +26,8 @@ export const LocationMap: React.FC = ({ }) => { const containerRef = useRef(null); const [isVisible, setIsVisible] = useState(false); + const [mapLoaded, setMapLoaded] = useState(false); + const handleLoad = useCallback(() => setMapLoaded(true), []); // Only mount the WebGL map when the container enters the viewport. // This prevents exhausting the browser's WebGL context limit (~8-16) @@ -79,25 +81,30 @@ export const LocationMap: React.FC = ({ initialViewState={{ longitude, latitude, zoom: effectiveZoom }} style={{ width: "100%", height: "100%" }} attributionControl={{ compact: true }} + onLoad={handleLoad} > - {showAccuracyCircle && ( - - - - + {mapLoaded && ( + <> + {showAccuracyCircle && ( + + + + + )} + + + + )} - - - )} diff --git a/web/src/components/dashboard/GoogleMap.tsx b/web/src/components/dashboard/GoogleMap.tsx index 0bbe76b..2eef798 100644 --- a/web/src/components/dashboard/GoogleMap.tsx +++ b/web/src/components/dashboard/GoogleMap.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from "react"; +import React, { useMemo, useState } from "react"; import ReactMap, { Source, Layer } from "react-map-gl/maplibre"; import type { FeatureCollection } from "geojson"; import "maplibre-gl/dist/maplibre-gl.css"; @@ -30,14 +30,12 @@ export const NodeLocationMap: React.FC = ({ }) => { const accuracyMeters = calculateAccuracyFromPrecisionBits(precisionBits); const effectiveZoom = zoom ?? calculateZoomFromAccuracy(accuracyMeters); - const showCenterDot = true; + const [mapLoaded, setMapLoaded] = useState(false); const markerGeoJSON = useMemo((): FeatureCollection => ({ type: "FeatureCollection", - features: showCenterDot - ? [{ type: "Feature", geometry: { type: "Point", coordinates: [lng, lat] }, properties: {} }] - : [], - }), [lat, lng, showCenterDot]); + features: [{ type: "Feature", geometry: { type: "Point", coordinates: [lng, lat] }, properties: {} }], + }), [lat, lng]); const circleGeoJSON = useMemo((): FeatureCollection => ({ type: "FeatureCollection", @@ -59,33 +57,38 @@ export const NodeLocationMap: React.FC = ({ initialViewState={{ longitude: lng, latitude: lat, zoom: effectiveZoom }} style={{ width: "100%", height: "100%" }} attributionControl={{ compact: true }} + onLoad={() => setMapLoaded(true)} > - - - - + {mapLoaded && ( + <> + + + + - - - + + + + + )} ); diff --git a/web/src/components/dashboard/NetworkMap.tsx b/web/src/components/dashboard/NetworkMap.tsx index bfc3769..ff6816e 100644 --- a/web/src/components/dashboard/NetworkMap.tsx +++ b/web/src/components/dashboard/NetworkMap.tsx @@ -187,55 +187,58 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ onZoomStart={handleUserInteraction} onLoad={() => setMapLoaded(true)} > - {/* Topology links — always mounted, visibility controlled via layout property */} - - - + {/* Sources and layers — only after map style has loaded */} + {mapLoaded && ( + <> + + + - {/* Node circles */} - - - - + + + + + + )} {/* Node popup */} {selectedNode && (