From ef628e066a2b1691792b9ba3d2198a323284cb3e Mon Sep 17 00:00:00 2001 From: Gregory Salaun Date: Thu, 13 Aug 2026 08:26:00 +0200 Subject: [PATCH] fix(bandmap): follow the cluster's filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The band map received the raw spot stream filtered by band alone, so none of the cluster's filters reached it: switching on "LoTW users only" changed the list and left the panadapter showing everyone. The same was true of hide worked, the spotter continent, and the status and mode chips. The predicate is extracted and shared rather than copied — two views of one spot stream disagreeing about the same spot is the fault lib/spotDisplay already exists to prevent, and a second copy would have drifted the first time a filter was added. The BAND filter is deliberately excluded from the shared part: a band map's band is its filter, and applying the cluster's would empty every map but one. --- changelog.json | 6 ++++-- frontend/src/App.tsx | 30 ++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/changelog.json b/changelog.json index 84159ff..1458abc 100644 --- a/changelog.json +++ b/changelog.json @@ -3,10 +3,12 @@ "version": "0.24.9", "date": "", "en": [ - "Appearance: row colouring now defaults to a left stripe, with a filled row and its strength offered as choices." + "Appearance: row colouring now defaults to a left stripe, with a filled row and its strength offered as choices.", + "The band map now follows the cluster filters — LoTW only, spotter continent, hide worked, status and mode chips." ], "fr": [ - "Apparence : la coloration des lignes se fait par défaut sur une barre à gauche, la ligne remplie et son intensité restant proposées." + "Apparence : la coloration des lignes se fait par défaut sur une barre à gauche, la ligne remplie et son intensité restant proposées.", + "La band map suit désormais les filtres du cluster — LoTW seulement, continent du spotter, masquer les contactés, statuts et modes." ] }, { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index aa53e78..eb0b23c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4717,12 +4717,21 @@ export default function App() { // Cluster spots after every active filter (band / mode / status / search / // hide-worked / group). Shared by the Cluster tab and the Main-view cluster // pane so both show exactly the same list. - const clusterRenderedRows = useMemo(() => { - const bandsActive = clusterLockBand ? new Set([band]) : clusterBands; + // Everything the operator has said about WHICH STATIONS they care about — + // LoTW only, the spotter's continent, hide worked, the status and mode chips, + // the search box, the source node. + // + // Shared with the band map, which used to receive the raw stream filtered by + // band alone: switching on "LoTW users only" changed the cluster list and left + // the panadapter showing everyone. Two views of one spot stream disagreeing + // about the same spot is the fault lib/spotDisplay already exists to prevent. + // + // The BAND filter is deliberately not in here: a band map's band IS its + // filter, and applying the cluster's would empty every map but one. + const spotPassesStationFilters = useCallback((s: ClusterSpot): boolean => { const search = clusterSearch.trim().toUpperCase(); - const list = spots.filter((s) => { + { if (clusterFilterSource && s.source_id !== clusterFilterSource) return false; - if (bandsActive.size > 0 && !bandsActive.has(s.band ?? '')) return false; if (search && !s.dx_call.includes(search)) return false; if (clusterLockMode) { const spotMode = inferSpotMode(s.comment ?? '', s.freq_hz); @@ -4774,6 +4783,15 @@ export default function App() { if (e.worked_call || e.status === 'worked') return false; } return true; + } + }, [clusterFilterSource, clusterSearch, clusterLockMode, mode, clusterModeFilter, + clusterStatusFilter, spotStatus, clusterLotwOnly, clusterSpotterConts, clusterHideWorked]); + + const clusterRenderedRows = useMemo(() => { + const bandsActive = clusterLockBand ? new Set([band]) : clusterBands; + const list = spots.filter((s) => { + if (bandsActive.size > 0 && !bandsActive.has(s.band ?? '')) return false; + return spotPassesStationFilters(s); }); let rendered = list as (ClusterSpot & { repeats?: number })[]; if (clusterGroup) { @@ -6775,7 +6793,7 @@ export default function App() { s.band === b)} + spots={spots.filter((s) => s.band === b && spotPassesStationFilters(s))} spotStatus={spotStatus} currentFreqHz={band === b && freqMhz ? Math.round(parseFloat(freqMhz) * 1_000_000) : 0} onSpotClick={handleSpotClick} @@ -6811,7 +6829,7 @@ export default function App() { side={bandMapSide} onToggleSide={toggleBandMapSide} band={band} - spots={spots.filter((s) => s.band === band)} + spots={spots.filter((s) => s.band === band && spotPassesStationFilters(s))} spotStatus={spotStatus} currentFreqHz={band && freqMhz ? Math.round(parseFloat(freqMhz) * 1_000_000) : 0} onSpotClick={handleSpotClick}