From 59062946be7bdcd6439ab613a911c30caf4480a5 Mon Sep 17 00:00:00 2001 From: you Date: Sun, 22 Mar 2026 00:34:38 +0000 Subject: [PATCH] =?UTF-8?q?Fix=20multi-select=20filters=20(AND=E2=86=92OR)?= =?UTF-8?q?=20and=20add=20localStorage=20persistence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Server: support comma-separated type filter values (OR logic) - Server: add observer_id filtering to /api/packets endpoint - Client: fix type and observer filters to use OR logic for multi-select - Client: persist observer and type filter selections to localStorage - Keys: meshcore-observer-filter, meshcore-type-filter --- public/index.html | 2 +- public/packets.js | 13 +++++++++++-- server.js | 3 ++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/public/index.html b/public/index.html index bd293a15..5581eb29 100644 --- a/public/index.html +++ b/public/index.html @@ -84,7 +84,7 @@ - + diff --git a/public/packets.js b/public/packets.js index cffb9240..c5f9c862 100644 --- a/public/packets.js +++ b/public/packets.js @@ -13,6 +13,8 @@ let selectedId = null; let groupByHash = true; let filters = {}; + { const o = localStorage.getItem('meshcore-observer-filter'); if (o) filters.observer = o; + const t = localStorage.getItem('meshcore-type-filter'); if (t) filters.type = t; } let wsHandler = null; let observers = []; let regionMap = {}; @@ -21,6 +23,7 @@ let totalCount = 0; let expandedHashes = new Set(); let hopNameCache = {}; + let showHexHashes = localStorage.getItem('meshcore-hex-hashes') === 'true'; let filtersBuilt = false; const PANEL_WIDTH_KEY = 'meshcore-panel-width'; @@ -116,6 +119,9 @@ } function renderHop(h) { + if (showHexHashes) { + return `${escapeHtml(h)}`; + } const entry = hopNameCache[h]; const name = entry ? (typeof entry === 'string' ? entry : entry.name) : null; const pubkey = entry?.pubkey || h; @@ -242,8 +248,8 @@ // Check if new packets pass current filters const filtered = newPkts.filter(p => { - if (filters.type !== undefined && filters.type !== '' && p.payload_type !== Number(filters.type)) return false; - if (filters.observer && p.observer_id !== filters.observer) return false; + if (filters.type) { const types = filters.type.split(',').map(Number); if (!types.includes(p.payload_type)) return false; } + if (filters.observer) { const obsSet = new Set(filters.observer.split(',')); if (!obsSet.has(p.observer_id)) return false; } if (filters.hash && p.hash !== filters.hash) return false; if (RegionFilter.getRegionParam()) { const selectedRegions = RegionFilter.getRegionParam().split(','); @@ -462,6 +468,7 @@
+ @@ -512,6 +519,7 @@ if (e.target.checked) selectedObservers.add(id); else selectedObservers.delete(id); } filters.observer = selectedObservers.size > 0 ? [...selectedObservers].join(',') : undefined; + if (filters.observer) localStorage.setItem('meshcore-observer-filter', filters.observer); else localStorage.removeItem('meshcore-observer-filter'); buildObserverMenu(); updateObsTrigger(); loadPackets(); @@ -553,6 +561,7 @@ if (e.target.checked) selectedTypes.add(id); else selectedTypes.delete(id); } filters.type = selectedTypes.size > 0 ? [...selectedTypes].join(',') : undefined; + if (filters.type) localStorage.setItem('meshcore-type-filter', filters.type); else localStorage.removeItem('meshcore-type-filter'); buildTypeMenu(); updateTypeTrigger(); loadPackets(); diff --git a/server.js b/server.js index 6f5d2311..1357640b 100644 --- a/server.js +++ b/server.js @@ -921,7 +921,8 @@ app.get('/api/packets', (req, res) => { } let results = [...allPackets.values()].sort((a, b) => order === 'DESC' ? b.timestamp.localeCompare(a.timestamp) : a.timestamp.localeCompare(b.timestamp)); // Apply additional filters - if (type !== undefined) results = results.filter(p => String(p.payload_type) === String(type)); + if (type !== undefined) { const types = String(type).split(','); results = results.filter(p => types.includes(String(p.payload_type))); } + if (observer) { const obsIds = observer.split(','); results = results.filter(p => obsIds.includes(p.observer_id)); } if (region) results = results.filter(p => (p.observer_id || '').includes(region) || (p.decoded_json || '').includes(region)); if (since) results = results.filter(p => p.timestamp >= since); if (until) results = results.filter(p => p.timestamp <= until);