Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useCallback, useMemo } from 'react'
import { LayerGroup, LayersControl } from 'react-leaflet'
import PropTypes from 'prop-types'

import * as NodeDef from '@core/survey/nodeDef'
import { ClusterMarker, useFlyToPoint } from '../common'
import { ClusterMarker, useFlyToPoint, useLayerRegistration } from '../common'
import { CoordinateAttributeMarker } from './CoordinateAttributeMarker'
import { useGeoAttributeDataLayer } from './useGeoAttributeDataLayer'
import { applySortOrder, useMapLayersPanel } from '../MapLayersPanel/MapLayersPanelContext'
Expand All @@ -23,8 +23,8 @@ export const GeoAttributeDataLayer = (props) => {
points,
} = useGeoAttributeDataLayer(props)

const { registerLayer, unregisterLayer, selectPoint, layerSortOrders } = useMapLayersPanel()
const layerKey = NodeDef.getUuid(attributeDef)
const { selectPoint, layerSortOrders } = useMapLayersPanel()
const sortOrder = layerSortOrders[layerKey] ?? 'none'
const sortedPoints = useMemo(() => applySortOrder(points, sortOrder), [points, sortOrder])

Expand All @@ -42,20 +42,7 @@ export const GeoAttributeDataLayer = (props) => {
const onMarkerPopupOpen = useCallback((key) => selectPoint(key), [selectPoint])
const onMarkerPopupClose = useCallback(() => selectPoint(null), [selectPoint])

// Stable wrapper so the panel always calls the latest flyToPoint without re-registering on popup state changes
const flyToPointRef = useRef(flyToPoint)
useEffect(() => {
flyToPointRef.current = flyToPoint
}, [flyToPoint])
const stableFlyToPoint = useCallback((point) => flyToPointRef.current(point), [])

useEffect(() => {
if (points.length === 0) {
unregisterLayer({ key: layerKey })
return
}
registerLayer({ key: layerKey, layerName: layerInnerName, points, flyToPoint: stableFlyToPoint })
}, [layerKey, layerInnerName, points, registerLayer, stableFlyToPoint, unregisterLayer])
useLayerRegistration({ layerKey, layerName: layerInnerName, points, flyToPoint })

return (
<LayersControl.Overlay name={layerName}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { useCallback, useMemo } from 'react'
import { LayersControl, LayerGroup } from 'react-leaflet'
import PropTypes from 'prop-types'

import { ClusterMarker, useFlyToPoint } from '../common'
import { ClusterMarker, useFlyToPoint, useLayerRegistration } from '../common'
import { applySortOrder, useMapLayersPanel } from '../MapLayersPanel/MapLayersPanelContext'
import { useSamplingPointDataLayer } from './useSamplingPointDataLayer'
import { SamplingPointDataMarker } from './SamplingPointDataMarker'

export const SamplingPointDataLayer = (props) => {
const { onRecordEditClick, createRecordFromSamplingPointDataItem } = props
const { levelIndex = 0, onRecordEditClick, createRecordFromSamplingPointDataItem } = props

const {
clusters,
clusterExpansionZoomExtractor,
clusterIconCreator,
getClusterLeaves,
isLayerActive,
overlayInnerName,
overlayName,
currentMarkersColor,
totalPoints,
points,
} = useSamplingPointDataLayer(props)

const layerKey = `sampling-point-data-${levelIndex}`
const { selectPoint, layerSortOrders } = useMapLayersPanel()
const sortOrder = layerSortOrders[layerKey] ?? 'none'
const sortedPoints = useMemo(() => applySortOrder(points, sortOrder), [points, sortOrder])

const {
currentPointShown,
currentPointPopupOpen,
Expand All @@ -28,7 +37,12 @@ export const SamplingPointDataLayer = (props) => {
onCurrentPointPopupClose,
openPopupOfPoint,
setMarkerByKey,
} = useFlyToPoint({ points, onRecordEditClick })
} = useFlyToPoint({ points: sortedPoints, onRecordEditClick })

const onMarkerPopupOpen = useCallback((key) => selectPoint(key), [selectPoint])
const onMarkerPopupClose = useCallback(() => selectPoint(null), [selectPoint])

useLayerRegistration({ active: isLayerActive, layerKey, layerName: overlayInnerName, points, flyToPoint })

return (
<LayersControl.Overlay name={overlayName}>
Expand Down Expand Up @@ -62,6 +76,8 @@ export const SamplingPointDataLayer = (props) => {
flyToNextPoint={flyToNextPoint}
flyToPreviousPoint={flyToPreviousPoint}
markersColor={currentMarkersColor}
onPopupClose={onMarkerPopupClose}
onPopupOpen={onMarkerPopupOpen}
onRecordEditClick={onRecordEditClick}
pointFeature={cluster}
setMarkerByKey={setMarkerByKey}
Expand All @@ -75,7 +91,11 @@ export const SamplingPointDataLayer = (props) => {
flyToNextPoint={flyToNextPoint}
flyToPreviousPoint={flyToPreviousPoint}
markersColor={currentMarkersColor}
onPopupClose={onCurrentPointPopupClose}
onPopupClose={() => {
onCurrentPointPopupClose()
onMarkerPopupClose()
}}
onPopupOpen={onMarkerPopupOpen}
onRecordEditClick={onRecordEditClick}
pointFeature={currentPointShown}
popupOpen={currentPointPopupOpen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const SamplingPointDataMarker = (props) => {
flyToPreviousPoint,
markersColor,
onPopupClose,
onPopupOpen,
onRecordEditClick,
pointFeature,
popupOpen,
Expand All @@ -30,6 +31,10 @@ export const SamplingPointDataMarker = (props) => {
flyToPoint(pointFeature)
}, [flyToPoint, pointFeature])

const handlePopupOpen = useCallback(() => {
onPopupOpen?.(key)
}, [key, onPopupOpen])

const pathOptions = useMemo(
() => ({ color: markersColor, fillColor: markersColor, fillOpacity: 0.5 }),
[markersColor]
Expand All @@ -44,7 +49,7 @@ export const SamplingPointDataMarker = (props) => {
markerRef.current = ref
setMarkerByKey({ key, marker: ref })
}}
eventHandlers={{ dblclick: onDoubleClick, popupclose: onPopupClose }}
eventHandlers={{ dblclick: onDoubleClick, popupclose: onPopupClose, popupopen: handlePopupOpen }}
>
{showMarkersLabels && <MarkerTooltip color={markersColor}>{itemCodes.join(' - ')}</MarkerTooltip>}

Expand All @@ -66,6 +71,7 @@ SamplingPointDataMarker.propTypes = {
flyToPreviousPoint: PropTypes.func.isRequired,
markersColor: PropTypes.string,
onPopupClose: PropTypes.func,
onPopupOpen: PropTypes.func,
onRecordEditClick: PropTypes.func.isRequired,
pointFeature: PropTypes.object.isRequired,
popupOpen: PropTypes.bool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const _convertItemsToPoints = (items) => {
x: long,
y: lat,
properties: {
ancestorsKeys: itemCodes,
cluster: false,
itemUuid,
itemCodes,
Expand Down Expand Up @@ -102,6 +103,7 @@ export const useSamplingPointDataLayer = (props) => {
const map = useMap()

const fetchCancelRef = useRef(null)
const [isLayerActive, setIsLayerActive] = useState(false)
const [state, setState] = useState({ loaded: false, loading: false, points: [], items: [] })
const { loaded, loading, points, items } = state

Expand All @@ -113,10 +115,10 @@ export const useSamplingPointDataLayer = (props) => {
{ level: levelIndex + 1 }
)

const layerColorPickerId = `sampling-point-data-layer-color-picker-${levelIndex}`
const layerKey = `sampling-point-data-layer-color-picker-${levelIndex}`

const { layerName: overlayName, currentMarkersColor } = useLayerColorPicker({
colorPickerId: layerColorPickerId,
colorPickerId: layerKey,
innerName: overlayInnerName,
initialColor: markersColor,
})
Expand All @@ -139,13 +141,18 @@ export const useSamplingPointDataLayer = (props) => {

useMapLayerToggle({
layerName: overlayName,
layerKey,
onAdd: () => {
setIsLayerActive(true)
const shouldLoadItems = !loaded && !loading
if (shouldLoadItems) {
fetchItemsAndConvertIntoPoints()
}
setState((statePrev) => ({ ...statePrev, loading: shouldLoadItems }))
},
onRemove: () => {
setIsLayerActive(false)
},
})

// cancel items fetch (if any) on unmount
Comment thread
SteRiccio marked this conversation as resolved.
Expand All @@ -163,6 +170,8 @@ export const useSamplingPointDataLayer = (props) => {
clusterExpansionZoomExtractor,
clusterIconCreator,
getClusterLeaves,
isLayerActive,
overlayInnerName,
overlayName,
currentMarkersColor,
totalPoints: points.length,
Expand Down
1 change: 1 addition & 0 deletions webapp/views/App/views/Data/MapView/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export { MarkerTooltip } from './MarkerTooltip'
export { useFlyToPoint } from './useFlyToPoint'
export { useLayerColorPicker } from './useLayerColorPicker'
export { useLayerMarker } from './useLayerMarker'
export { useLayerRegistration } from './useLayerRegistration'
export { useMapClusters } from './useMapClusters'
export { useMapLayerToggle } from './useMapLayerToggle'
11 changes: 9 additions & 2 deletions webapp/views/App/views/Data/MapView/common/useFlyToPoint.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useRef, useState } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useMap } from 'react-leaflet'

import { Numbers } from '@openforis/arena-core'
Expand Down Expand Up @@ -87,10 +87,17 @@ export const useFlyToPoint = ({ points, onRecordEditClick = null, zoomToMaxLevel
[]
)

// Stable wrapper so the panel always calls the latest flyToPoint without re-registering on popup state changes
const flyToPointRef = useRef(flyToPoint)
useEffect(() => {
flyToPointRef.current = flyToPoint
}, [flyToPoint])
const stableFlyToPoint = useCallback((point) => flyToPointRef.current(point), [])

return {
currentPointShown,
currentPointPopupOpen,
flyToPoint,
flyToPoint: stableFlyToPoint,
flyToNextPoint,
flyToPreviousPoint,
onCurrentPointPopupClose,
Expand Down
27 changes: 27 additions & 0 deletions webapp/views/App/views/Data/MapView/common/useLayerRegistration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useEffect } from 'react'

import { useSystemConfigExperimentalFeatures } from '@webapp/store/system'
import { useMapLayersPanel } from '../MapLayersPanel/MapLayersPanelContext'

/**
* Registers or unregisters a map layer with the layers panel based on experimental features,
* layer activity, and point availability.
* @param {object} params - Parameters.
* @param {boolean} [params.active] - Whether the layer is currently active/visible.
* @param {Function} params.flyToPoint - Callback to fly the map to a specific point.
* @param {string} params.layerKey - Unique key identifying the layer.
* @param {string} params.layerName - Display name of the layer in the panel.
* @param {Array} params.points - Array of points belonging to this layer.
*/
export const useLayerRegistration = ({ active = true, flyToPoint, layerKey, layerName, points }) => {
const experimentalFeatures = useSystemConfigExperimentalFeatures()
const { registerLayer, unregisterLayer } = useMapLayersPanel()

useEffect(() => {
if (!experimentalFeatures || !active || points.length === 0) {
unregisterLayer({ key: layerKey })
return
}
registerLayer({ key: layerKey, layerName, points, flyToPoint })
}, [active, experimentalFeatures, flyToPoint, layerKey, layerName, points, registerLayer, unregisterLayer])
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { useMapEvents } from 'react-leaflet'

export const useMapLayerToggle = ({ layerName, onAdd, onRemove }) => {
export const useMapLayerToggle = ({ layerName, layerKey, onAdd, onRemove }) => {
const matchesLayer = (eventName) => (layerKey ? eventName.includes(layerKey) : eventName === layerName)

useMapEvents({
overlayadd(event) {
if (event.name === layerName && onAdd) {
if (onAdd && matchesLayer(event.name)) {
onAdd()
}
},
overlayremove(event) {
if (event.name === layerName && onRemove) {
if (onRemove && matchesLayer(event.name)) {
onRemove()
}
},
Expand Down
Loading