From 37d6c2031dda12afc8e3cdc8dfe7f6032e378988 Mon Sep 17 00:00:00 2001 From: Iva Date: Mon, 27 Oct 2025 10:38:17 +0200 Subject: [PATCH 1/7] Add user location to map --- front/app/components/EsriMap/index.tsx | 37 ++++++++++++++++++++++++++ front/app/components/EsriMap/utils.tsx | 30 +++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/front/app/components/EsriMap/index.tsx b/front/app/components/EsriMap/index.tsx index d4a3ec1c6007..139edf535bbd 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 = true, }: EsriMapProps) => { const locale = useLocale(); const isMobileOrSmaller = useBreakpoint('phone'); @@ -268,6 +271,40 @@ const EsriMap = ({ } }, [appConfig?.data.attributes.settings.esri_integration?.api_key]); + // Handle user location + useEffect(() => { + if (!showUserLocation || !mapView) return; + + let currentLocationGraphic: Graphic | null = null; + + const getCurrentLocation = () => { + if ('geolocation' in navigator) { + navigator.geolocation.getCurrentPosition((position) => { + const { longitude, latitude } = position.coords; + + // Create new user location graphic + const locationGraphic = createUserLocationGraphic( + longitude, + latitude + ); + mapView.graphics.add(locationGraphic); + currentLocationGraphic = locationGraphic; + }); + } else { + console.warn('Geolocation is not supported by this browser.'); + } + }; + + getCurrentLocation(); + + // Cleanup function to remove user location graphic + return () => { + if (currentLocationGraphic) { + mapView.graphics.remove(currentLocationGraphic); + } + }; + }, [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', + }, + }); +}; From 6ae499c40ed0611fbdbdc448d25ad33eb6dbb88f Mon Sep 17 00:00:00 2001 From: Iva Date: Mon, 27 Oct 2025 10:40:33 +0200 Subject: [PATCH 2/7] Only show it on idea map --- front/app/components/EsriMap/index.tsx | 2 +- front/app/components/IdeasMap/index.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/front/app/components/EsriMap/index.tsx b/front/app/components/EsriMap/index.tsx index 139edf535bbd..14e9ec313fd5 100644 --- a/front/app/components/EsriMap/index.tsx +++ b/front/app/components/EsriMap/index.tsx @@ -86,7 +86,7 @@ const EsriMap = ({ webMapId, initialData, globalMapSettings, - showUserLocation = true, + showUserLocation = false, }: EsriMapProps) => { const locale = useLocale(); const isMobileOrSmaller = useBreakpoint('phone'); diff --git a/front/app/components/IdeasMap/index.tsx b/front/app/components/IdeasMap/index.tsx index 3c175773630f..d793292113d5 100644 --- a/front/app/components/IdeasMap/index.tsx +++ b/front/app/components/IdeasMap/index.tsx @@ -537,6 +537,7 @@ const IdeasMap = memo( onHover={onMapHover} onClick={onMapClick} id="e2e-ideas-map" + showUserLocation={true} /> Date: Mon, 27 Oct 2025 10:45:43 +0200 Subject: [PATCH 3/7] Use watch position to ensure continous updates --- front/app/components/EsriMap/index.tsx | 51 +++++++++++++++++--------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/front/app/components/EsriMap/index.tsx b/front/app/components/EsriMap/index.tsx index 14e9ec313fd5..28133677ab36 100644 --- a/front/app/components/EsriMap/index.tsx +++ b/front/app/components/EsriMap/index.tsx @@ -276,32 +276,49 @@ const EsriMap = ({ if (!showUserLocation || !mapView) return; let currentLocationGraphic: Graphic | null = null; + let watchId: number | null = null; - const getCurrentLocation = () => { - if ('geolocation' in navigator) { - navigator.geolocation.getCurrentPosition((position) => { - const { longitude, latitude } = position.coords; - - // Create new user location graphic - const locationGraphic = createUserLocationGraphic( - longitude, - latitude - ); - mapView.graphics.add(locationGraphic); - currentLocationGraphic = locationGraphic; - }); - } else { - console.warn('Geolocation is not supported by this browser.'); + 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; }; - getCurrentLocation(); + const handleLocationError = (error: GeolocationPositionError) => { + console.warn('Error getting user location:', error); + }; - // Cleanup function to remove user location graphic + 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 + } + ); + } else { + console.warn('Geolocation is not supported by this browser.'); + } + + // 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]); From 49844c90bcc21b17727edb5dd98efb20a7e03a47 Mon Sep 17 00:00:00 2001 From: Iva Date: Mon, 27 Oct 2025 10:50:11 +0200 Subject: [PATCH 4/7] Simplify useEffect to handle user location --- front/app/components/EsriMap/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/front/app/components/EsriMap/index.tsx b/front/app/components/EsriMap/index.tsx index 28133677ab36..d9a943615587 100644 --- a/front/app/components/EsriMap/index.tsx +++ b/front/app/components/EsriMap/index.tsx @@ -307,8 +307,6 @@ const EsriMap = ({ maximumAge: 30000, // Cache location for 30 seconds } ); - } else { - console.warn('Geolocation is not supported by this browser.'); } // Cleanup function to remove user location graphic and stop watching From 84021d082ee8abccfe7c254012a157b01435870a Mon Sep 17 00:00:00 2001 From: Iva Date: Mon, 27 Oct 2025 19:11:17 +0200 Subject: [PATCH 5/7] Show location only on small screens and change idea shape to make it different from location --- front/app/components/IdeasMap/index.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/front/app/components/IdeasMap/index.tsx b/front/app/components/IdeasMap/index.tsx index d793292113d5..b6bc91918120 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,7 +165,7 @@ const IdeasMap = memo( // Map icon for ideas const ideaIcon = useMemo(() => { return getShapeSymbol({ - shape: 'circle', + shape: 'triangle', color: theme.colors.tenantPrimary, outlineColor: colors.white, outlineWidth: 2, @@ -174,7 +175,7 @@ const IdeasMap = memo( const ideaIconSecondary = useMemo(() => { return getShapeSymbol({ - shape: 'circle', + shape: 'triangle', color: theme.colors.tenantSecondary, outlineColor: colors.white, outlineWidth: 2, @@ -537,7 +538,8 @@ const IdeasMap = memo( onHover={onMapHover} onClick={onMapClick} id="e2e-ideas-map" - showUserLocation={true} + // Only show user location on mobile screens + showUserLocation={isTabletOrSmaller} /> Date: Mon, 27 Oct 2025 19:11:37 +0200 Subject: [PATCH 6/7] Only change idea shape for smaller screens --- front/app/components/IdeasMap/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/front/app/components/IdeasMap/index.tsx b/front/app/components/IdeasMap/index.tsx index b6bc91918120..01f882b9d506 100644 --- a/front/app/components/IdeasMap/index.tsx +++ b/front/app/components/IdeasMap/index.tsx @@ -165,23 +165,23 @@ const IdeasMap = memo( // Map icon for ideas const ideaIcon = useMemo(() => { return getShapeSymbol({ - shape: 'triangle', + 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: 'triangle', + 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(); From bacb07c69cf35631d24db0098c891b7acc8b55bd Mon Sep 17 00:00:00 2001 From: Iva Date: Thu, 6 Nov 2025 11:54:25 +0200 Subject: [PATCH 7/7] Fix disappearing location point --- front/app/components/IdeasMap/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/front/app/components/IdeasMap/index.tsx b/front/app/components/IdeasMap/index.tsx index 01f882b9d506..a1903094e384 100644 --- a/front/app/components/IdeasMap/index.tsx +++ b/front/app/components/IdeasMap/index.tsx @@ -420,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); } });