diff --git a/ui/src/types/layer.ts b/ui/src/types/layer.ts index 2db37d3a..e1ade05a 100644 --- a/ui/src/types/layer.ts +++ b/ui/src/types/layer.ts @@ -16,11 +16,10 @@ export type Feature = { geometry: Geometry; properties: GeoJsonProperties; createdAt: Date; - updatedAt: Date; - deletedAt: Date; + updatedAt: Date | null; + deletedAt: Date | null; }; - export interface GetLayersData { layers: Layer[]; } @@ -29,7 +28,6 @@ export interface GetLayersVars { incidentId: string; } - export interface AddFeatureVars { layerId: string; geometry: Geometry; @@ -37,13 +35,21 @@ export interface AddFeatureVars { id: string | number | undefined; } +export interface AddFeatureResponse { + insertFeaturesOne: Feature | undefined; +} + export interface ModifyFeatureVars { id: string | number | undefined; geometry: Geometry; properties: GeoJsonProperties; } +export interface ModifyFeatureResponse { + updateFeaturesByPk: Feature | undefined; +} + export interface DeleteFeatureVars { id: string | number | undefined; deletedAt: Date; -} \ No newline at end of file +} diff --git a/ui/src/views/map/LayerContext.tsx b/ui/src/views/map/LayerContext.tsx index 1cadda36..b002a650 100644 --- a/ui/src/views/map/LayerContext.tsx +++ b/ui/src/views/map/LayerContext.tsx @@ -1,52 +1,46 @@ +import React, { createContext, useReducer } from "react"; +import { LayersAction, layersReducer, selectedFeatureReducer, activeLayerReducer, drawReducer } from "./reducer"; +import { Layer } from "types/layer"; +import MapboxDraw from "@mapbox/mapbox-gl-draw"; -import React, { createContext, useReducer } from 'react'; -import { LayersAction, layersReducer, selectedFeatureReducer, activeLayerReducer, drawReducer } from './reducer'; -import { Layer } from 'types/layer'; -import MapboxDraw from '@mapbox/mapbox-gl-draw'; - -export type SelectedFeatureState = string | number | undefined; +export type SelectedFeatureState = string | undefined; export type LayersState = Layer[]; export type ActiveLayerState = string | undefined; export type DrawState = MapboxDraw | undefined; - export type LayerState = { - layers: LayersState; - activeLayer: string | undefined - selectedFeature: SelectedFeatureState; - draw: DrawState; -} + layers: LayersState; + activeLayer: string | undefined; + selectedFeature: SelectedFeatureState; + draw: DrawState; +}; const initialState: LayerState = { - layers: [], - activeLayer: undefined, - selectedFeature: 0, - draw: undefined, -} + layers: [], + activeLayer: undefined, + selectedFeature: undefined, + draw: undefined, +}; const LayerContext = createContext<{ - state: LayerState; - dispatch: React.Dispatch; + state: LayerState; + dispatch: React.Dispatch; }>({ - state: initialState, - dispatch: () => null, + state: initialState, + dispatch: () => null, }); const mainReducer = ({ layers, activeLayer, selectedFeature, draw }: LayerState, action: LayersAction) => ({ - layers: layersReducer(layers, action), - activeLayer: activeLayerReducer(activeLayer, action), - selectedFeature: selectedFeatureReducer(selectedFeature, action), - draw: drawReducer(draw, action), + layers: layersReducer(layers, action), + activeLayer: activeLayerReducer(activeLayer, action), + selectedFeature: selectedFeatureReducer(selectedFeature, action), + draw: drawReducer(draw, action), }); const LayersProvider = ({ children }: { children: React.ReactNode }) => { - const [state, dispatch] = useReducer(mainReducer, initialState); + const [state, dispatch] = useReducer(mainReducer, initialState); - return ( - - {children} - - ) -} + return {children}; +}; -export { LayerContext, LayersProvider }; \ No newline at end of file +export { LayerContext, LayersProvider }; diff --git a/ui/src/views/map/Map.tsx b/ui/src/views/map/Map.tsx index eb8514bc..3fee0ada 100644 --- a/ui/src/views/map/Map.tsx +++ b/ui/src/views/map/Map.tsx @@ -1,344 +1,410 @@ - -import './control-panel.css'; +import "./control-panel.css"; import "./Map.scss"; -import { AddFeatureToLayer, DeleteFeature, GetLayers, ModifyFeature } from './graphql'; -import { AddFeatureVars, DeleteFeatureVars, GetLayersData, GetLayersVars, Layer, ModifyFeatureVars } from 'types/layer'; -import { BabsIconController } from './controls/BabsIconController'; -import { CleanFeature, FilterActiveFeatures, LayerToFeatureCollection } from './utils'; -import { displayStyle, drawStyle } from './style'; +import { AddFeatureToLayer, DeleteFeature, GetLayers, ModifyFeature } from "./graphql"; +import { + AddFeatureResponse, + AddFeatureVars, + DeleteFeatureVars, + GetLayersData, + GetLayersVars, + Layer, + ModifyFeatureResponse, + ModifyFeatureVars, +} from "types/layer"; +import { BabsIconController } from "./controls/BabsIconController"; +import { CleanFeature, FilterActiveFeatures, LayerToFeatureCollection } from "./utils"; +import { displayStyle, drawStyle } from "./style"; import { Feature, Geometry, GeoJsonProperties, FeatureCollection } from "geojson"; -import { first } from 'lodash'; -import { FullscreenControl, Map, MapProvider, NavigationControl, ScaleControl, Source, useMap, Layer as MapLayer, AttributionControl } from 'react-map-gl/maplibre'; -import { LayerContext, LayersProvider } from './LayerContext'; -import { StyleController, selectedStyle } from './controls/StyleController'; -import { memo, useCallback, useContext, useEffect, useState } from 'react'; -import { useMutation, useQuery, useReactiveVar } from '@apollo/client'; -import { useParams } from 'react-router-dom'; +import { first } from "lodash"; +import { + FullscreenControl, + Map, + MapProvider, + NavigationControl, + ScaleControl, + Source, + useMap, + Layer as MapLayer, + AttributionControl, +} from "react-map-gl/maplibre"; +import { LayerContext, LayersProvider } from "./LayerContext"; +import { StyleController, selectedStyle } from "./controls/StyleController"; +import { memo, useCallback, useContext, useEffect, useState } from "react"; +import { useMutation, useQuery, useReactiveVar } from "@apollo/client"; +import { useParams } from "react-router-dom"; import bbox from "@turf/bbox"; -import DrawControl from './controls/DrawControl'; -import EnrichedLayerFeatures, { EnrichedSymbolSource } from 'components/map/EnrichedLayerFeatures'; -import ExportControl from './controls/ExportControl'; -import LayerControl from './controls/LayerControl'; -import MapboxDraw from '@mapbox/mapbox-gl-draw'; -import maplibregl from 'maplibre-gl'; -import classNames from 'classnames'; +import DrawControl from "./controls/DrawControl"; +import EnrichedLayerFeatures, { EnrichedSymbolSource } from "components/map/EnrichedLayerFeatures"; +import ExportControl from "./controls/ExportControl"; +import LayerControl from "./controls/LayerControl"; +import MapboxDraw from "@mapbox/mapbox-gl-draw"; +import maplibregl from "maplibre-gl"; +import classNames from "classnames"; const modes = { - ...MapboxDraw.modes, + ...MapboxDraw.modes, }; function MapView() { - - const mapStyle = useReactiveVar(selectedStyle); - maplibregl.setMaxParallelImageRequests(150); - maplibregl.setWorkerCount(6); - - const mapClass = classNames({ - 'maplibre': true, - "container-flex": true, - }); - - return ( - <> -

Lage

-
- console.log(e)} - initialViewState={{ - latitude: 46.87148, - longitude: 8.62994, - zoom: 5, - bearing: 0, - }} - attributionControl={false} - minZoom={9} - maxZoom={19} - mapStyle={mapStyle.uri} - scrollZoom={true} - reuseMaps={false} - > - - {/* All Map Controls */} - - - - - {/* Layersprovider and Draw */} - - -
- - ); + const mapStyle = useReactiveVar(selectedStyle); + maplibregl.setMaxParallelImageRequests(150); + maplibregl.setWorkerCount(6); + + const mapClass = classNames({ + maplibre: true, + "container-flex": true, + }); + + return ( + <> +

Lage

+
+ console.log(e)} + initialViewState={{ + latitude: 46.87148, + longitude: 8.62994, + zoom: 5, + bearing: 0, + }} + attributionControl={false} + minZoom={9} + maxZoom={19} + mapStyle={mapStyle.uri} + scrollZoom={true} + reuseMaps={false} + > + + {/* All Map Controls */} + + + + + {/* Layersprovider and Draw */} + + +
+ + ); } function Layers() { - const { state } = useContext(LayerContext); - - return ( - - -
- - -
- - {/* Active Layer */} - - - {/* Inactive Layers */} - l.id !== state.activeLayer) || []} /> -
- ) + const { state } = useContext(LayerContext); + + return ( + + +
+ + +
+ + {/* Active Layer */} + + + {/* Inactive Layers */} + l.id !== state.activeLayer) || []} /> +
+ ); } - // LayerFetcher polls from the layers and sets the layers from remote function LayerFetcher() { - const { incidentId } = useParams(); - const { state, dispatch } = useContext(LayerContext); - - const { data, loading } = useQuery(GetLayers, { - variables: { incidentId: incidentId || "" }, - pollInterval: 3000, - fetchPolicy: "cache-and-network", - }); - - useEffect(() => { - if (!loading && data && data.layers !== state.layers) { - dispatch({ type: "SET_LAYERS", payload: { layers: data.layers } }); - } - }, [data, dispatch, loading, state.activeLayer, state.layers]) - - return (<>) + const { incidentId } = useParams(); + const { state, dispatch } = useContext(LayerContext); + + const { data, loading } = useQuery(GetLayers, { + variables: { incidentId: incidentId || "" }, + pollInterval: 3000, + fetchPolicy: "cache-and-network", + }); + + useEffect(() => { + if (!loading && data && data.layers !== state.layers) { + dispatch({ type: "SET_LAYERS", payload: { layers: data.layers } }); + } + }, [data, dispatch, loading, state.activeLayer, state.layers]); + + return <>; } function ActiveLayer() { - const [initialized, setInitalized] = useState(false); - const { current: map } = useMap(); - const { state } = useContext(LayerContext); - const featureCollection = LayerToFeatureCollection(first(state.layers.filter(l => l.id === state.activeLayer))); - - useEffect(() => { - let fc = FilterActiveFeatures(featureCollection); - if (initialized || !map?.loaded) { - return - } - // only run this for the initialization as we don't want to continously - // change the map viewport on new features - if (map !== undefined && fc.features.length > 0) { - let bboxArray = bbox(fc); - map.fitBounds( - [[bboxArray[0], bboxArray[1]], [bboxArray[2], bboxArray[3]]], - { - animate: true, - padding: { top: 30, bottom: 30, left: 30, right: 30, } - } - ); - setInitalized(true); - } - }, [featureCollection, map, initialized, setInitalized]); - - return ( - <> - - - - - ) + const [initialized, setInitalized] = useState(false); + const { current: map } = useMap(); + const { state } = useContext(LayerContext); + const featureCollection = LayerToFeatureCollection(first(state.layers.filter((l) => l.id === state.activeLayer))); + + useEffect(() => { + let fc = FilterActiveFeatures(featureCollection); + if (initialized || !map?.loaded) { + return; + } + // only run this for the initialization as we don't want to continously + // change the map viewport on new features + if (map !== undefined && fc.features.length > 0) { + let bboxArray = bbox(fc); + map.fitBounds( + [ + [bboxArray[0], bboxArray[1]], + [bboxArray[2], bboxArray[3]], + ], + { + animate: true, + padding: { top: 30, bottom: 30, left: 30, right: 30 }, + }, + ); + setInitalized(true); + } + }, [featureCollection, map, initialized, setInitalized]); + + return ( + <> + + + + + ); } -const MemoDraw = memo(Draw) +const MemoDraw = memo(Draw); function Draw(props: { activeLayer: string | undefined }) { - const { state, dispatch } = useContext(LayerContext); - const { incidentId } = useParams(); - const { current: map } = useMap(); - - const [addFeature] = useMutation(AddFeatureToLayer, { - refetchQueries: [{ query: GetLayers, variables: { incidentId: incidentId } }] - }); - const [modifyFeature] = useMutation(ModifyFeature, { - refetchQueries: [{ query: GetLayers, variables: { incidentId: incidentId } }] - }); - - const [deleteFeature] = useMutation(DeleteFeature, { - refetchQueries: [{ query: GetLayers, variables: { incidentId: incidentId } }] - }); - - const onSelectionChange = useCallback((e: any) => { - const features: Feature[] = e.features; - if (features?.length > 0) { - const feature = first(features); - dispatch({ type: "SELECT_FEATURE", payload: { id: feature?.id } }) - } - else { - dispatch({ type: "DESELECT_FEATURE", payload: {} }); - } - }, [dispatch]); - - const onCreate = useCallback((e: FeatureEvent) => { - if (props.activeLayer === undefined) { - return - } - - const createdFeatures: Feature[] = e.features; - createdFeatures.forEach(f => { - let feature = CleanFeature(f) - dispatch({ - type: "ADD_FEATURE", payload: { - layerId: props.activeLayer, - feature: { - geometry: feature.geometry, - id: feature.id, - properties: feature.properties, - createdAt: f.properties?.createdAt, - updatedAt: f.properties?.updatedAt, - deletedAt: f.properties?.deletedAt, - } - } - }) - dispatch({ type: "SELECT_FEATURE", payload: { id: feature.id } }) - addFeature({ variables: { layerId: props.activeLayer || "", geometry: feature.geometry, id: feature.id, properties: feature.properties } }) - }) - }, [props.activeLayer, dispatch, addFeature]); - - const onUpdate = useCallback((e: FeatureEvent) => { - const updatedFeatures: Feature[] = e.features; - updatedFeatures.forEach(f => { - let feature = CleanFeature(f); - dispatch({ - type: "MODIFY_FEATURE", payload: { - layerId: props.activeLayer, - feature: { - geometry: feature.geometry, - id: feature.id, - properties: feature.properties, - createdAt: f.properties?.createdAt, - updatedAt: f.properties?.updatedAt, - deletedAt: f.properties?.deletedAt, - } - } - }); - modifyFeature({ variables: { id: feature.id, geometry: feature.geometry, properties: feature.properties } }); - }); + const { state, dispatch } = useContext(LayerContext); + const { incidentId } = useParams(); + const { current: map } = useMap(); + + const [addFeature] = useMutation(AddFeatureToLayer, { + refetchQueries: [{ query: GetLayers, variables: { incidentId: incidentId } }], + onCompleted: (data: AddFeatureResponse) => { + if (data.insertFeaturesOne?.id) { + dispatch({ type: "SELECT_FEATURE", payload: { id: data.insertFeaturesOne.id.toString() } }); + } + }, + onError: (error) => { + console.error("Error adding feature:", error); + }, + optimisticResponse: (vars) => { + return { + __typename: "Mutation", + insertFeaturesOne: { + __typename: "Feature", + id: vars.id, + geometry: { ...vars.geometry, __typename: "Geometry" }, + properties: { ...vars.properties, __typename: "Properties" }, + createdAt: new Date(), + updatedAt: null, + deletedAt: null, + }, + }; + }, + }); + const [modifyFeature] = useMutation(ModifyFeature, { + refetchQueries: [{ query: GetLayers, variables: { incidentId: incidentId } }], + onError: (error) => { + console.error("Error adding feature:", error); + }, + optimisticResponse: (vars, { IGNORE }) => { + if (vars.properties?.deletedAt) { + return IGNORE; + } + return { + __typename: "Mutation", + updateFeaturesByPk: { + __typename: "Feature", + id: vars.id, + geometry: { ...vars.geometry, __typename: "Geometry" }, + properties: { ...vars.properties, __typename: "Properties" }, + createdAt: vars.properties?.createdAt || new Date(), + updatedAt: vars.properties?.updatedAt || new Date(), + deletedAt: null, + }, + }; + }, + }); + + const [deleteFeature] = useMutation(DeleteFeature, { + refetchQueries: [{ query: GetLayers, variables: { incidentId: incidentId } }], + }); + + const onSelectionChange = useCallback( + (e: any) => { + const features: Feature[] = e.features; + if (features?.length > 0) { + const feature = first(features); + dispatch({ type: "SELECT_FEATURE", payload: { id: feature?.id?.toString() } }); + } else { dispatch({ type: "DESELECT_FEATURE", payload: {} }); - }, [dispatch, props.activeLayer, modifyFeature]); - - const onDelete = useCallback((e: FeatureEvent) => { - const deletedFeatures: Feature[] = e.features; - deletedFeatures.forEach(f => { - let feature = CleanFeature(f); - deleteFeature({ variables: { id: feature.id, deletedAt: new Date() } }) - dispatch({ type: "DELETE_FEATURE", payload: { featureId: f.id?.toString(), layerId: props.activeLayer } }); + } + }, + [dispatch], + ); + + const onCreate = useCallback( + (e: FeatureEvent) => { + if (props.activeLayer === undefined) { + return; + } + + const createdFeatures: Feature[] = e.features; + createdFeatures.forEach((f) => { + let feature = CleanFeature(f); + addFeature({ + variables: { + layerId: props.activeLayer || "", + geometry: feature.geometry, + id: feature.id, + properties: feature.properties, + }, }); - dispatch({ type: "DESELECT_FEATURE", payload: {} }); - }, [dispatch, props.activeLayer, deleteFeature]); - - const onCombine = useCallback((e: CombineFeatureEvent) => { - onCreate({ features: e.createdFeatures }) - onDelete({ features: e.deletedFeatures }) - dispatch({ type: "DESELECT_FEATURE", payload: {} }); - }, [dispatch, onCreate, onDelete]); - - - // this is the effect which syncs the drawings - useEffect(() => { - if (state.draw && map?.loaded) { - const featureCollection: FeatureCollection = FilterActiveFeatures(LayerToFeatureCollection(state.layers.find(l => l.id === props.activeLayer))) - state.draw.deleteAll() - state.draw.set(featureCollection) - } - }, [state.draw, map?.loaded, state.layers, props.activeLayer]) - - // this is the effect which syncs the drawings - useEffect(() => { - if (state.draw && map?.loaded) { - if (state.selectedFeature === undefined) { - state.draw?.changeMode("simple_select") - } - } - }, [state.draw, map?.loaded, state.selectedFeature]) - - - if (props.activeLayer === undefined) { - return (<>) + }); + }, + [props.activeLayer, dispatch, addFeature], + ); + + const onUpdate = useCallback( + (e: FeatureEvent) => { + const updatedFeatures: Feature[] = e.features; + updatedFeatures.forEach((f) => { + let feature = CleanFeature(f); + modifyFeature({ variables: { id: feature.id, geometry: feature.geometry, properties: feature.properties } }); + }); + dispatch({ type: "DESELECT_FEATURE", payload: {} }); + }, + [dispatch, props.activeLayer, modifyFeature], + ); + + const onDelete = useCallback( + (e: FeatureEvent) => { + const deletedFeatures: Feature[] = e.features; + deletedFeatures.forEach((f) => { + let feature = CleanFeature(f); + deleteFeature({ variables: { id: feature.id, deletedAt: new Date() } }); + }); + dispatch({ type: "DESELECT_FEATURE", payload: {} }); + }, + [dispatch, props.activeLayer, deleteFeature], + ); + + const onCombine = useCallback( + (e: CombineFeatureEvent) => { + onCreate({ features: e.createdFeatures }); + onDelete({ features: e.deletedFeatures }); + dispatch({ type: "DESELECT_FEATURE", payload: {} }); + }, + [dispatch, onCreate, onDelete], + ); + + // this is the effect which syncs the drawings + useEffect(() => { + if (state.draw && map?.loaded) { + const featureCollection: FeatureCollection = FilterActiveFeatures( + LayerToFeatureCollection(state.layers.find((l) => l.id === props.activeLayer)), + ); + state.draw.deleteAll(); + state.draw.set(featureCollection); } - - return ( - <> - - - ) - + }, [state.draw, map?.loaded, state.layers, props.activeLayer]); + + // this is the effect which syncs the drawings + useEffect(() => { + if (state.draw && map?.loaded) { + if (state.selectedFeature === undefined) { + // No feature selected, sync to draw control + state.draw.changeMode("simple_select"); + return; + } + + // Check if the selected feature exists in the draw control + const selectedFeature = state.draw.get(state.selectedFeature); + if (!selectedFeature) { + // Selected feature does not (yet) exist in draw control + return; + } + + // select the feature in the draw control + state.draw.changeMode("simple_select", { featureIds: [state.selectedFeature] }); + return; + } + }, [state.draw, map?.loaded, state.selectedFeature, state.layers, props.activeLayer]); + + if (props.activeLayer === undefined) { + return <>; + } + + return ( + <> + + + ); } function InactiveLayers(props: { layers: Layer[] }) { - const { layers } = props; - - return ( - <> - { - layers.map(l => - - ) - } - - ) + const { layers } = props; + + return ( + <> + {layers.map((l) => ( + + ))} + + ); } -function InactiveLayer(props: { featureCollection: FeatureCollection, id: string }) { - const { featureCollection, id } = props; - - return ( - <> - - - { - displayStyle.map(s => ) - } - - - ) +function InactiveLayer(props: { featureCollection: FeatureCollection; id: string }) { + const { featureCollection, id } = props; + + return ( + <> + + + {displayStyle.map((s) => ( + + ))} + + + ); } - - function MapWithProvder() { - return ( - - - - ) + return ( + + + + ); } export { MapWithProvder as Map }; export type FeatureEvent = { - features: Feature[] -} + features: Feature[]; +}; export type CombineFeatureEvent = { - deletedFeatures: Feature[] - createdFeatures: Feature[] -} \ No newline at end of file + deletedFeatures: Feature[]; + createdFeatures: Feature[]; +}; diff --git a/ui/src/views/map/reducer.tsx b/ui/src/views/map/reducer.tsx index 8f8e3b7c..baaea146 100644 --- a/ui/src/views/map/reducer.tsx +++ b/ui/src/views/map/reducer.tsx @@ -1,192 +1,184 @@ -import { Feature, Layer } from "types/layer"; +import { Layer } from "types/layer"; import { ActiveLayerState, DrawState, LayersState, SelectedFeatureState } from "./LayerContext"; import { first } from "lodash"; import MapboxDraw from "@mapbox/mapbox-gl-draw"; // All valid actions export type LayersAction = - SetLayerAction | - AddLayerAction | - RemoveLayerAction | - SelectFeatureAction | - DeselectFeature | - ModifyFeature | - AddFeature | - DeleteFeature | - SetActiveLayer | - SetDrawLayer; + | SetLayerAction + | AddLayerAction + | RemoveLayerAction + | SelectFeatureAction + | DeselectFeature + // | ModifyFeature + // | AddFeature + // | DeleteFeature + | SetActiveLayer + | SetDrawLayer; export type SetLayerAction = { - type: 'SET_LAYERS'; - payload: { - layers: Layer[]; - } -} + type: "SET_LAYERS"; + payload: { + layers: Layer[]; + }; +}; export type AddLayerAction = { - type: 'ADD_LAYER'; - payload: { - layer: Layer; - } -} + type: "ADD_LAYER"; + payload: { + layer: Layer; + }; +}; export type RemoveLayerAction = { - type: 'REMOVE_LAYER'; - payload: { - id: string - } -} - -export type AddFeature = { - type: 'ADD_FEATURE'; - payload: { - layerId: string | undefined; - feature: Feature; - } -} - -export type ModifyFeature = { - type: 'MODIFY_FEATURE'; - payload: { - layerId: string | undefined; - feature: Feature; - } -} - -export type DeleteFeature = { - type: 'DELETE_FEATURE'; - payload: { - layerId: string | undefined; - featureId: string | undefined; - } -} - + type: "REMOVE_LAYER"; + payload: { + id: string; + }; +}; + +// export type AddFeature = { +// type: "ADD_FEATURE"; +// payload: { +// layerId: string | undefined; +// feature: Feature; +// }; +// }; + +// export type ModifyFeature = { +// type: "MODIFY_FEATURE"; +// payload: { +// layerId: string | undefined; +// feature: Feature; +// }; +// }; + +// export type DeleteFeature = { +// type: "DELETE_FEATURE"; +// payload: { +// layerId: string | undefined; +// featureId: string | undefined; +// }; +// }; export type SetActiveLayer = { - type: 'SET_ACTIVE_LAYER'; - payload: { - layerId: string; - } -} + type: "SET_ACTIVE_LAYER"; + payload: { + layerId: string; + }; +}; export type SelectFeatureAction = { - type: "SELECT_FEATURE"; - payload: { - id: string | number | undefined - } -} + type: "SELECT_FEATURE"; + payload: { + id: string | undefined; + }; +}; export type DeselectFeature = { - type: "DESELECT_FEATURE"; - payload: {} -} + type: "DESELECT_FEATURE"; + payload: {}; +}; export type SetDrawLayer = { - type: "SET_DRAW"; - payload: { - draw: MapboxDraw | undefined; - } -} + type: "SET_DRAW"; + payload: { + draw: MapboxDraw | undefined; + }; +}; export const layersReducer = (state: LayersState, action: LayersAction) => { - switch (action.type) { - case 'SET_LAYERS': - return action.payload.layers - case 'ADD_LAYER': - return [ - ...state, - action.payload.layer - ] - case 'REMOVE_LAYER': - return [ - ...state.filter(layer => layer.id !== action.payload.id), - ] - case 'ADD_FEATURE': - return state.map((layer: Layer) => { - if (layer.id !== action.payload.layerId) { - // This isn't the item we care about - keep it as-is - return layer - } - - // Otherwise, this is the one we want - append the feature - return Object.assign({}, layer, { features: [...layer.features, action.payload.feature] }) - - }) - case 'MODIFY_FEATURE': - return state.map((layer: Layer) => { - if (layer.id !== action.payload.layerId) { - // This isn't the item we care about - keep it as-is - return layer - } - - // Otherwise, this is the one we want - append the feature - layer.features.map((feature: Feature) => { - if (feature.id !== action.payload.feature.id) { - return feature - } - // replace the feature - return action.payload.feature - }) - return layer - }) - case 'DELETE_FEATURE': - return state.map((layer: Layer) => { - if (layer.id !== action.payload.layerId) { - // This isn't the item we care about - keep it as-is - return layer - } - - if (action.payload.featureId === undefined) { - return layer - } - - // Otherwise, this is the one we want - append the feature - layer.features.map((feature: Feature) => { - if (feature.id !== action.payload.featureId) { - return feature - } - // set the deletedAt - return { ...feature, deleteAt: new Date() } - }) - return layer - }) - default: - return state; - } -} + switch (action.type) { + case "SET_LAYERS": + return action.payload.layers; + case "ADD_LAYER": + return [...state, action.payload.layer]; + case "REMOVE_LAYER": + return [...state.filter((layer) => layer.id !== action.payload.id)]; + // case "ADD_FEATURE": + // return state.map((layer: Layer) => { + // if (layer.id !== action.payload.layerId) { + // // This isn't the item we care about - keep it as-is + // return layer; + // } + + // // Otherwise, this is the one we want - append the feature + // return Object.assign({}, layer, { features: [...layer.features, action.payload.feature] }); + // }); + // case "MODIFY_FEATURE": + // return state.map((layer: Layer) => { + // if (layer.id !== action.payload.layerId) { + // // This isn't the item we care about - keep it as-is + // return layer; + // } + + // // Otherwise, this is the one we want - append the feature + // layer.features.map((feature: Feature) => { + // if (feature.id !== action.payload.feature.id) { + // return feature; + // } + // // replace the feature + // return action.payload.feature; + // }); + // return layer; + // }); + // case "DELETE_FEATURE": + // return state.map((layer: Layer) => { + // if (layer.id !== action.payload.layerId) { + // // This isn't the item we care about - keep it as-is + // return layer; + // } + + // if (action.payload.featureId === undefined) { + // return layer; + // } + + // // Otherwise, this is the one we want - append the feature + // layer.features.map((feature: Feature) => { + // if (feature.id !== action.payload.featureId) { + // return feature; + // } + // // set the deletedAt + // return { ...feature, deleteAt: new Date() }; + // }); + // return layer; + // }); + default: + return state; + } +}; export const selectedFeatureReducer = (state: SelectedFeatureState, action: LayersAction) => { - switch (action.type) { - case 'SELECT_FEATURE': - return action.payload.id; - case 'DESELECT_FEATURE': - return undefined - default: - return state; - } -} + switch (action.type) { + case "SELECT_FEATURE": + return action.payload.id; + case "DESELECT_FEATURE": + return undefined; + default: + return state; + } +}; export const activeLayerReducer = (state: ActiveLayerState, action: LayersAction) => { - switch (action.type) { - case 'SET_ACTIVE_LAYER': - return action.payload.layerId; - case 'SET_LAYERS': - if (state === undefined) { - // set the first layer as active if we have not an active layer yet - return first(action.payload.layers)?.id - } - return state - default: - return state; - } -} - + switch (action.type) { + case "SET_ACTIVE_LAYER": + return action.payload.layerId; + case "SET_LAYERS": + if (state === undefined) { + // set the first layer as active if we have not an active layer yet + return first(action.payload.layers)?.id; + } + return state; + default: + return state; + } +}; export const drawReducer = (state: DrawState, action: LayersAction) => { - switch (action.type) { - case 'SET_DRAW': - return action.payload.draw; - default: - return state; - } -} \ No newline at end of file + switch (action.type) { + case "SET_DRAW": + return action.payload.draw; + default: + return state; + } +};