From 0d169b0641e0635c5bb2571f8752a433673ca7ee Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Mon, 16 Feb 2026 20:28:22 +0200 Subject: [PATCH] fix(collections/map): crash when reloading markers --- .../widgets/collections/geomap/Markers.tsx | 62 ++++++++++++------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/apps/client/src/widgets/collections/geomap/Markers.tsx b/apps/client/src/widgets/collections/geomap/Markers.tsx index 4fd25ddd09..33192d0e7c 100644 --- a/apps/client/src/widgets/collections/geomap/Markers.tsx +++ b/apps/client/src/widgets/collections/geomap/Markers.tsx @@ -1,4 +1,5 @@ -import { useContext, useEffect } from "preact/hooks"; +import { GeoJSONSource } from "maplibre-gl"; +import { useCallback, useContext, useEffect } from "preact/hooks"; import FNote from "../../../entities/fnote"; import { useChildNotes } from "../../react/hooks"; @@ -6,6 +7,7 @@ import { ParentMap } from "./map"; export const LOCATION_ATTRIBUTE = "geolocation"; export const MARKER_LAYER = "points-layer"; +const MARKER_SOURCE = "points"; const DEFAULT_MARKER_COLOR = "#2A81CB"; // SVG marker pin shape (replaces the Leaflet marker PNG). @@ -18,7 +20,36 @@ export default function Markers({ note }: { note: FNote }) { const map = useContext(ParentMap); const childNotes = useChildNotes(note?.noteId); - async function refresh() { + // Add the source and layer. + useEffect(() => { + if (!map) return; + + map.addSource(MARKER_SOURCE, { + type: "geojson", + data: { + type: "FeatureCollection", + features: [] + } + }); + map.addLayer({ + id: MARKER_LAYER, + type: "symbol", + source: MARKER_SOURCE, + layout: { + "icon-image": [ "get", "icon" ], + "icon-size": 1, + "icon-anchor": "bottom", + "icon-allow-overlap": true + } + }); + + return () => { + map.removeLayer(MARKER_LAYER); + map.removeSource(MARKER_SOURCE); + }; + }, [ map ]); + + const refresh = useCallback(async () => { if (!map) return; async function ensureIcon(color: string, iconClass: string) { @@ -62,29 +93,18 @@ export default function Markers({ note }: { note: FNote }) { }); })); - map.addSource("points", { - type: "geojson", - data: { - type: "FeatureCollection", - features - } + // Update the source + const source = map.getSource(MARKER_SOURCE); + source?.setData({ + type: "FeatureCollection", + features }); - map.addLayer({ - id: MARKER_LAYER, - type: "symbol", - source: "points", - layout: { - "icon-image": [ "get", "icon" ], - "icon-size": 1, - "icon-anchor": "bottom", - "icon-allow-overlap": true - } - }); - } + }, [ childNotes, map ]); + // Refresh the data. useEffect(() => { refresh(); - }, [ map, childNotes ]); + }, [ refresh ]); return null; }