feat(map-analysis): rich multi-source node popups matching the Unified map#3692
Conversation
…d map Map Analysis node markers now render the same DashboardNodePopup used by the Unified/Dashboard map (rich .node-popup card) instead of the bare inline popup, including the 'Seen by N sources' list for nodes reported by multiple sources. Source rows navigate to that source's view (mirrors DashboardPage). The top-of-page source selection now keeps a node visible when ANY of its reporting sources is enabled — the filter checks node.sources rather than just the primary sourceId, so unified-merged multi-source nodes no longer vanish when only one of their sources is selected. Test: MapAnalysisCanvas asserts the popup lists every reporting source; wrapped in MemoryRouter and mocks useDisplaySettings for the new popup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBLhGGNh35oMwTL53va1Y5
|
Claude finished @Yeraze's task —— View job PR Review: feat(map-analysis): rich multi-source node popups matching the Unified mapTasks:
OverviewThis is a clean, well-scoped PR that achieves its stated goals. The approach — reusing Bug / Correctness Issues1. Source filter uses a shadowed variable ( // Line 138 — outer scope:
const sourceIds = sourceList.map((s) => s.id);
// Line 169 — inside .filter(), SHADOWS the outer `sourceIds`:
const sourceIds = node.sources && node.sources.length > 0
? node.sources.map((s) => s.sourceId)
: (node.sourceId ? [node.sourceId] : []);
return sourceIds.some((id) => config.sources.includes(id));The inner 2. Hop-count lookup uses only the node's primary const sourceId = n.sourceId ?? '';
const hopVal = hopByKey.get(`${sourceId}:${Number(n.nodeNum)}`);For a unified-merged node with multiple sources, Code Quality3. Within 4. Source-filter fallback for MeshCore nodes MeshCore nodes have Test CoverageThe new test in
Minor Nits
Summary✅ Correct approach — reuses existing unified data hooks and popup component. Overall the PR is in good shape and ready to merge after considering the above. |
useDashboardUnifiedData only stamps the per-node `sources` array when given full DashboardSource objects; bare source-id strings get none. NodeMarkersLayer was passing string ids, so the popup's 'Seen by N sources' list and the multi-source visibility filter had nothing to work with. Pass the full source objects (which also carry protocol/type for correct MeshCore/Meshtastic badges). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBLhGGNh35oMwTL53va1Y5
|
Claude finished @Yeraze's task —— View job PR Review: feat(map-analysis): rich multi-source node popups matching the Unified mapTasks:
OverviewThis is a clean, well-scoped PR that achieves its stated goals. The approach — reusing Bug / Correctness Issues1. Shadowed // Line 138 — outer scope:
const sourceIds = sourceList.map((s) => s.id);
// Line 172 — inside .filter(), SHADOWS the outer `sourceIds`:
const sourceIds = node.sources && node.sources.length > 0
? node.sources.map((s) => s.sourceId)
: (node.sourceId ? [node.sourceId] : []);
return sourceIds.some((id) => config.sources.includes(id));
2. Hop-count lookup uses only the node's primary const sourceId = n.sourceId ?? '';
const hopVal = hopByKey.get(`${sourceId}:${Number(n.nodeNum)}`);For unified-merged nodes, 3. MeshCore node filter fallback The filter at line 172–175 falls back to Code QualityThe comment block at lines 248–253 (inside JSX) is a bit verbose — a single-line summary referencing the issue numbers would keep the JSX scannable. Minor nit. Test CoverageThe new test at
Summary✅ Correct approach — reuses unified data hooks and popup component. The PR is in good shape. The shadowed variable rename is trivial and could be done before merge; the hop-count gap and missing filter test are reasonable to track as follow-ups. |
* chore(release): bump version to 4.12.0 Finalize 4.12.0 (from 4.12.0-rc2): package.json, package-lock.json, desktop/package.json, desktop/src-tauri/tauri.conf.json, helm/Chart.yaml. Promote CHANGELOG [Unreleased] → [4.12.0]. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4 * docs(4.12.0): document Automation Engine, MeshCore regions/scopes & UI additions Release-audit of every PR since v4.11.5 surfaced documentation gaps. Brings the docs site current with the 4.12.0 feature set: - New page docs/features/automation-engine.md (+ sidebar nav) for the node/ builder-based Automation Engine (#3653 et al). v-pre-wrapped so the {{ }} token syntax renders literally without breaking the VitePress build. - meshcore.md: new Regions/Scopes section (#3667 et al), channel unread indicators, heard-repeaters badge, per-message route line, byte counter, on-demand telemetry polling, Define Path editor, repeater DM-hidden behavior. - automation.md: corrected stale auto-favorite eligibility table (#3786/#3774). - maps.md: traceroute default-keyed channel routing + channel dropdowns (#3723). - map-analysis.md: rich multi-source node popups (#3692). - device.md: unmessagable-node DM-hidden behavior (#3760). Verified: vitepress build passes; tokens render literally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4 * fix(migration): make migration 104 PostgreSQL channel_database rebuild atomic Release-audit (PR #3753) flagged the tombstone-exhaustion rebuild path as a data-availability risk: it DROPs the live channel_database and RENAMEs a fresh copy into place, but the migration runner wraps each migration's statements in no transaction, so each DDL auto-commits. A crash/connection drop in the DROP→RENAME window would leave the database with no channel_database — and the migration's own idempotency check (`'channel_database'::regclass`) would then throw "relation does not exist" on the next startup, blocking recovery. Wrap the rebuild in BEGIN/COMMIT with ROLLBACK on error. PostgreSQL DDL is transactional, so any failure now rolls back to the original table intact. (Channel keys/PSKs were already copied before the drop, so this hardens the narrow availability window rather than fixing data loss. The path only fires at ≥1500 attnum tombstones — 1500+ restarts on the old migration 021 bug — so it's rare, but the blast radius warranted the fix.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Brings the Map Analysis node-marker popups up to parity with the Unified/Dashboard map, and makes the page properly multi-source-aware.
1. Popups look like the Unified map
Map Analysis markers now render the same rich
DashboardNodePopup(the.node-popup-*card — name/short name header, role, hops, hardware, battery, SNR, altitude, position, last-heard) instead of the bare inline<strong>name</strong> / Source / Hopspopup.2. Multi-source nodes list all their sources
DashboardNodePopuprenders the "Seen by N sources" list for unified-merged nodes. Map Analysis uses the sameuseDashboardUnifiedDatamerge (which attachesnode.sources), so a node reported by several sources now shows each one, with rows that link to that source's view (mirrorsDashboardPage.handleNodeSourceSelect).3. Source selection handles multi-source nodes
The top-of-page source filter now keeps a node visible when any of its reporting sources is enabled — it checks
node.sourcesrather than only the node's primarysourceId. Previously a multi-source node whose primary source wasn't selected would vanish even if one of its other sources was enabled.Testing
MapAnalysisCanvas.test: new assertion that the popup lists every reporting source ("Seen by 2 sources" + both names); wrapped inMemoryRouterand mocksuseDisplaySettingsfor the new popup.Builds on #3687 (now merged).
🤖 Generated with Claude Code