From bf34cc341ae82b683bb5e5cb708e8d382838e255 Mon Sep 17 00:00:00 2001 From: Nico Date: Thu, 4 Jun 2026 02:33:43 -0700 Subject: [PATCH] fix: precompute map zone item counts --- .../src/components/maps/LocationMapCanvas.jsx | 31 +++++++++++---- frontend/src/lib/locationMapUtils.js | 38 ++++++++----------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/frontend/src/components/maps/LocationMapCanvas.jsx b/frontend/src/components/maps/LocationMapCanvas.jsx index 68c0f09..c4df4e9 100644 --- a/frontend/src/components/maps/LocationMapCanvas.jsx +++ b/frontend/src/components/maps/LocationMapCanvas.jsx @@ -1,9 +1,9 @@ -import { useRef, useState } from "react"; +import { useMemo, useRef, useState } from "react"; import { clientPointToMap, getMapObjectDisplayLabel, getObjectKey, - itemsForZone, + itemMatchesMapFilters, } from "../../lib/locationMapUtils"; const DRAG_CHANGE_THRESHOLD = 2; @@ -55,6 +55,24 @@ export default function LocationMapCanvas({ const objectDragHistoryCapturedRef = useRef(false); const suppressPanClickRef = useRef(false); const [isPanning, setIsPanning] = useState(false); + const zoneItemSummary = useMemo(() => { + const assignedCounts = new Map(); + const visibleItemsByZone = new Map(); + + (mapItems || []).forEach((item) => { + const zoneKey = String(item.zone_id || ""); + if (!zoneKey) return; + + assignedCounts.set(zoneKey, (assignedCounts.get(zoneKey) || 0) + 1); + if (!itemMatchesMapFilters(item, filters, username)) return; + + const existingItems = visibleItemsByZone.get(zoneKey) || []; + existingItems.push(item); + visibleItemsByZone.set(zoneKey, existingItems); + }); + + return { assignedCounts, visibleItemsByZone }; + }, [filters, mapItems, username]); const startDrag = (event, object, type) => { if (!canEditObjects || object.locked || !svgRef.current) return; @@ -261,12 +279,9 @@ export default function LocationMapCanvas({ if (!object.visible || !filters.showZones) return null; const key = getObjectKey(object); const isSelected = key === selectedObjectKey; - const zoneItems = itemsForZone(mapItems, object.zone_id, filters, username); - const assignedZoneItemCount = object.zone_id - ? mapItems.filter( - (item) => String(item.zone_id || "") === String(object.zone_id || "") - ).length - : 0; + const zoneKey = String(object.zone_id || ""); + const zoneItems = zoneKey ? zoneItemSummary.visibleItemsByZone.get(zoneKey) || [] : []; + const assignedZoneItemCount = zoneKey ? zoneItemSummary.assignedCounts.get(zoneKey) || 0 : 0; const count = zoneItems.length; const hasHiddenZoneItems = assignedZoneItemCount > count; const countLabel = hasHiddenZoneItems diff --git a/frontend/src/lib/locationMapUtils.js b/frontend/src/lib/locationMapUtils.js index 9280dcf..5309905 100644 --- a/frontend/src/lib/locationMapUtils.js +++ b/frontend/src/lib/locationMapUtils.js @@ -156,32 +156,26 @@ export function prepareObjectsForSave(objects) { })); } -export function itemsForZone(items, zoneId, filters, username) { - return items.filter((item) => { - if (String(item.zone_id || "") !== String(zoneId || "")) return false; - if (!filters.showCompleted && item.bought) return false; +export function itemMatchesMapFilters(item, filters, username) { + if (!filters.showCompleted && item.bought) return false; - const addedBy = Array.isArray(item.added_by_users) ? item.added_by_users : []; - const hasOwnershipData = addedBy.length > 0 && username; - const isMine = hasOwnershipData ? addedBy.includes(username) : true; - if (isMine && !filters.showMyItems) return false; - if (!isMine && !filters.showOtherItems) return false; - return true; - }); + const addedBy = Array.isArray(item.added_by_users) ? item.added_by_users : []; + const hasOwnershipData = addedBy.length > 0 && username; + const isMine = hasOwnershipData ? addedBy.includes(username) : true; + if (isMine && !filters.showMyItems) return false; + if (!isMine && !filters.showOtherItems) return false; + return true; +} + +export function itemsForZone(items, zoneId, filters, username) { + return items.filter((item) => ( + String(item.zone_id || "") === String(zoneId || "") && + itemMatchesMapFilters(item, filters, username) + )); } export function unmappedItems(items, filters, username) { - return items.filter((item) => { - if (item.zone_id) return false; - if (!filters.showCompleted && item.bought) return false; - - const addedBy = Array.isArray(item.added_by_users) ? item.added_by_users : []; - const hasOwnershipData = addedBy.length > 0 && username; - const isMine = hasOwnershipData ? addedBy.includes(username) : true; - if (isMine && !filters.showMyItems) return false; - if (!isMine && !filters.showOtherItems) return false; - return true; - }); + return items.filter((item) => !item.zone_id && itemMatchesMapFilters(item, filters, username)); } export function clientPointToMap(event, svgElement, mapSize) {