diff --git a/front/app/components/EsriMap/index.tsx b/front/app/components/EsriMap/index.tsx index d4a3ec1c6007..d9a943615587 100644 --- a/front/app/components/EsriMap/index.tsx +++ b/front/app/components/EsriMap/index.tsx @@ -25,6 +25,7 @@ import { getDefaultBasemapType, getDefaultBasemap, handleWebMapReferenceLayers, + createUserLocationGraphic, } from './utils'; // Custom Esri styles @@ -71,6 +72,7 @@ export type EsriMapProps = { onClick?: (event: any, mapView: MapView) => void; onHover?: (event: any, mapView: MapView) => void; globalMapSettings: AppConfigurationMapSettings; + showUserLocation?: boolean; }; const EsriMap = ({ @@ -84,6 +86,7 @@ const EsriMap = ({ webMapId, initialData, globalMapSettings, + showUserLocation = false, }: EsriMapProps) => { const locale = useLocale(); const isMobileOrSmaller = useBreakpoint('phone'); @@ -268,6 +271,55 @@ const EsriMap = ({ } }, [appConfig?.data.attributes.settings.esri_integration?.api_key]); + // Handle user location + useEffect(() => { + if (!showUserLocation || !mapView) return; + + let currentLocationGraphic: Graphic | null = null; + let watchId: number | null = null; + + const updateUserLocation = (position: GeolocationPosition) => { + const { longitude, latitude } = position.coords; + + // Remove existing location graphic if it exists + if (currentLocationGraphic) { + mapView.graphics.remove(currentLocationGraphic); + } + + // Create new user location graphic + const locationGraphic = createUserLocationGraphic(longitude, latitude); + mapView.graphics.add(locationGraphic); + currentLocationGraphic = locationGraphic; + }; + + const handleLocationError = (error: GeolocationPositionError) => { + console.warn('Error getting user location:', error); + }; + + if ('geolocation' in navigator) { + // Use watchPosition for continuous location updates + watchId = navigator.geolocation.watchPosition( + updateUserLocation, + handleLocationError, + { + enableHighAccuracy: true, + timeout: 10000, + maximumAge: 30000, // Cache location for 30 seconds + } + ); + } + + // Cleanup function to remove user location graphic and stop watching + return () => { + if (currentLocationGraphic) { + mapView.graphics.remove(currentLocationGraphic); + } + if (watchId !== null) { + navigator.geolocation.clearWatch(watchId); + } + }; + }, [showUserLocation, mapView]); + return ( <> { + const point = new Point({ + longitude, + latitude, + }); + + const symbol = new SimpleMarkerSymbol({ + color: [51, 119, 255], // Blue color + size: 12, + outline: { + color: [255, 255, 255], // White outline + width: 2, + }, + style: 'circle', + }); + + return new Graphic({ + geometry: point, + symbol, + attributes: { + id: 'user-location', + title: 'Your Location', + }, + }); +}; diff --git a/front/app/components/IdeasMap/index.tsx b/front/app/components/IdeasMap/index.tsx index 3c175773630f..a1903094e384 100644 --- a/front/app/components/IdeasMap/index.tsx +++ b/front/app/components/IdeasMap/index.tsx @@ -127,6 +127,7 @@ const IdeasMap = memo( const { data: authUser } = useAuthUser(); const [searchParams] = useSearchParams(); const isMobileOrSmaller = useBreakpoint('phone'); + const isTabletOrSmaller = useBreakpoint('tablet'); // Create div elements to use for inserting React components into Esri map popup // Docs: https://developers.arcgis.com/javascript/latest/custom-ui/#introduction @@ -164,23 +165,23 @@ const IdeasMap = memo( // Map icon for ideas const ideaIcon = useMemo(() => { return getShapeSymbol({ - shape: 'circle', + shape: isTabletOrSmaller ? 'triangle' : 'circle', color: theme.colors.tenantPrimary, outlineColor: colors.white, outlineWidth: 2, sizeInPx: 18, }); - }, [theme.colors.tenantPrimary]); + }, [theme.colors.tenantPrimary, isTabletOrSmaller]); const ideaIconSecondary = useMemo(() => { return getShapeSymbol({ - shape: 'circle', + shape: isTabletOrSmaller ? 'triangle' : 'circle', color: theme.colors.tenantSecondary, outlineColor: colors.white, outlineWidth: 2, sizeInPx: 18, }); - }, [theme.colors.tenantSecondary]); + }, [theme.colors.tenantSecondary, isTabletOrSmaller]); // Existing handling for dynamic container width const { windowWidth } = useWindowSize(); @@ -419,12 +420,11 @@ const IdeasMap = memo( geometry, symbol: ideaIconSecondary, }); - mapView.graphics.removeAll(); // Add the graphic to the map for a few seconds to highlight the clicked point mapView.graphics.add(graphic); setTimeout(() => { - mapView.graphics.removeAll(); + mapView.graphics.remove(graphic); }, 2000); } }); @@ -537,6 +537,8 @@ const IdeasMap = memo( onHover={onMapHover} onClick={onMapClick} id="e2e-ideas-map" + // Only show user location on mobile screens + showUserLocation={isTabletOrSmaller} />