diff --git a/src/components/maps/GlobalMap.tsx b/src/components/maps/GlobalMap.tsx index 663952a05..d97661d39 100644 --- a/src/components/maps/GlobalMap.tsx +++ b/src/components/maps/GlobalMap.tsx @@ -15,6 +15,9 @@ import { MapToolbar } from './MapToolbar' import { SelectedFeature } from './AreaActiveMarker' import { useRouter } from 'next/navigation' import { useUrlParams } from '@/js/hooks/useUrlParams' +import Spinner from '@/components/ui/Spinner' +import { Button } from '@/components/ui/Button' +import { toast } from 'react-toastify' export interface CameraInfo { center: { @@ -68,6 +71,10 @@ export const GlobalMap: React.FC = ({ heatmap: false, crags: true }) + + const [isLoading, setIsLoading] = useState(true) + const [hasError, setHasError] = useState(false) + const router = useRouter() const urlParams = useUrlParams() @@ -95,6 +102,9 @@ export const GlobalMap: React.FC = ({ if (e.target == null) return setMapInstance(e.target) + setIsLoading(false) + setHasError(false) + // Only apply jumpTo if initial values are defined if (initialCenter != null && initialZoom != null) { e.target.jumpTo({ center: initialCenter, zoom: initialZoom ?? 6 }) @@ -103,6 +113,39 @@ export const GlobalMap: React.FC = ({ } }, [initialCenter, initialZoom, initialViewState]) + useEffect(() => { + if (!isLoading) return + const timeout = setTimeout(() => { + setHasError(true) + setIsLoading(false) + toast.error('Map load timeout') + }, 10000) + return () => clearTimeout(timeout) + }, [isLoading]) + + useEffect(() => { + if (!mapInstance) return + const handleError = (err: any) => { + console.error('Map error:', err) + setHasError(true) + setIsLoading(false) + toast.error('Failed to load map') + } + mapInstance.on('error', handleError) + return () => { + mapInstance.off('error', handleError) + } + }, [mapInstance]) + + const handleReload = () => { + setIsLoading(true) + setHasError(false) + if (mapInstance) { + mapInstance.remove() + setMapInstance(null) + } + } + /** * Handle click event on the map. Place a marker on the map and activate the side drawer. */ @@ -215,7 +258,34 @@ export const GlobalMap: React.FC = ({ return (
- +
+ + + +

+ Error loading map +

+

+ Please try reloading the map. +

+
+
+ )} + + {!hasError && setCursor('move')} @@ -235,6 +305,12 @@ export const GlobalMap: React.FC = ({ cooperativeGestures={showFullscreenControl} interactiveLayerIds={['crag-markers', 'crag-name-labels', 'area-boundaries', 'organizations']} > + {isLoading && ( +
+ +

Loading map...

+
+ )} @@ -253,7 +329,7 @@ export const GlobalMap: React.FC = ({ )} {children} -
+ } ) }