fix(neighbor-info): deduplicate bidirectional links and fix MQTT transport class (#3777, #3780)#3784
Conversation
…sport class (#3777, #3780) Three related fixes for neighbor-info display on the dashboard map: 1. **`nodes.ts`**: Add `transportMechanism` to `upsertNode` INSERT and UPDATE sets. The field existed in the schema (migration 066) and was passed in by `meshtasticManager.ts`, but the repository silently dropped it. This also adds an optional `sourceId` parameter to `getNodesByNums` for per-source scoped lookups. 2. **`sourceDashboardData.ts`**: Scope the `getNodesByNums` call in `buildSourceNeighborInfo` to the current source so that `transportClass` reflects how THIS source hears each node rather than pulling across-source rows (fixes #3780 — MQTT neighbor lines not affected by Show RF toggle). Also deduplicate bidirectional A→B / B→A pairs within a single source, keeping the canonical direction (smaller nodeNum first) and marking it `bidirectional=true`. 3. **`useDashboardData.ts`**: Deduplicate neighbor links across sources in `mergeUnifiedSourceData` using the same canonical-pair-key approach, so multi-source setups don't draw double polylines for shared nodes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jf83jmbVNEgu8Fh4Dvuqea
|
Claude finished @Yeraze's task —— View job PR Review
Overall AssessmentThis is a well-targeted, clean fix for two genuine bugs. The root-cause analysis in the PR description is accurate, and the implementation is consistent with the codebase's existing patterns. Bug Fix AnalysisBug #3780 (MQTT transport class): The fix is correct. Bug #3777 (duplicate polylines): The dedup logic is correct. Using Issues Found1. Missing The SQLite sync path ( Specifically:
2. When 3. Cross-source neighbor dedup discards transport metadata (useDashboardData.ts:382–389) In 4. bidirectional: linkKeys.has(`${ni.neighborNodeNum}-${ni.nodeNum}`),
Code Quality
Test CoverageTests are well-written:
Missing test: No test verifies that Minor SuggestionsThe existing SummaryThe core fixes are correct and the logic is sound. The main actionable gap is the missing |
The async upsertNode fix was correct but the legacy SQLite sync path (upsertNodeSqlite) had the same omission — transportMechanism was absent from both the UPDATE set and the INSERT values, so SQLite deployments using the sync facade would silently drop the field despite the async path storing it. Mirrors the same fix applied to upsertNode in the prior commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jf83jmbVNEgu8Fh4Dvuqea
|
Claude finished @Yeraze's task —— View job PR Review
Overall AssessmentThis is a clean, well-scoped fix for two genuine bugs. The previous review cycle identified a gap in What Changed Since Last Review
Bug Fix VerificationBug #3780 (MQTT transport class):
Bug #3777 (duplicate polylines):
Known Limitations (Pre-existing, Not Introduced Here)
Both limitations are out of scope for this PR and are noted only for awareness. Test CoverageTests are thorough and well-structured:
One minor gap: The Code Quality
SummaryThe fixes are correct, the primary concern from the previous review is resolved, and the test coverage is solid. The two known limitations are pre-existing and explicitly scoped out. This PR is ready to merge. |
) The bidirectional dedup keeps only the canonical direction and drops the reverse row, so the reverse direction's signal data would be inaccessible on the map. Backfill the reverse direction's SNR and report timestamp onto the kept record (reverseSnr / reverseTimestamp) and add a click popup (DashboardNeighborPopup) that shows both directions — addressing the "clicking the link should expose both directions of data" part of #3777 that the dedup alone didn't cover. - sourceDashboardData: directed-key lookup attaches reverseSnr/reverseTimestamp - DashboardMap: neighbor Polyline now has a Popup + stable pair-based key - DashboardNeighborPopup: new popup component (reuses .node-popup-* classes) - tests: DashboardNeighborPopup.test.tsx (both/one-way/missing-SNR) and a reverse-SNR assertion in sourceRoutes.neighbor-info.test.ts 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
Overall AssessmentThis is a solid, well-scoped PR that fixes two genuine bugs. The previous two review cycles caught and resolved the main issues (missing New in This Commit:
|
| Issue | Status |
|---|---|
upsertNodeSqlite missing transportMechanism |
✅ Fixed in d2aad5f |
getNodesByNums source scoping |
✅ Fixed |
| Within-source bidirectional dedup | ✅ Fixed |
Cross-source dedup in mergeUnifiedSourceData |
✅ Fixed |
Type gap: (node as any)?.transportMechanism in sourceDashboardData.ts |
Pre-existing, still present — follow-up candidate |
Missing test for upsertNodeSqlite sync path |
Still missing — noted but non-blocking |
Test Coverage
The three new DashboardNeighborPopup tests are well-chosen:
- Bidirectional link: verifies both direction rows render with correct SNR values ✅
- One-way link: verifies no reverse row, one-way indicator ✅
- Missing SNR (
null): verifies em-dash fallback ✅
One gap: the hasReverse guard covers bidirectional=false but reverseSnr != null — this state isn't reachable in production but could be worth a test if the condition is intentional.
Summary
The core fixes are correct, the new popup component is clean, and the UX change (clickable polylines showing both directions) directly addresses the dedup's information loss. The link: any type and the 'blue' color string are minor pre-existing style issues not introduced by this PR. Ready to merge.
|
Pushed Backend ( Frontend: the neighbor Tests: new Full suite: 7603 passed / 0 failed (the 4 failing suites are the known worktree-only With this, #3784 now resolves all of #3777 (items 1–3) and #3780 — the body can take |
Closes #3777
Closes #3780
Summary
Fixes two related bugs in how neighbor links are built and displayed on the dashboard map.
Bug #3780 — MQTT neighbor lines not affected by "Show RF" toggle
Root cause (two-part):
upsertNodeinnodes.tsincludedtransportMechanismin neither its UPDATE nor INSERT paths (including the legacyupsertNodeSqlitesync path), so the field added by migration 066 was never actually written to the DB even thoughmeshtasticManager.tscorrectly passed it in.buildSourceNeighborInfoinsourceDashboardData.tscalledgetNodesByNums(allNodeNums)without asourceId, so in multi-source deployments it could pick up a row from a different source — giving an MQTT node the RF transport class of a same-nodeNum row from another source.Fix:
transportMechanismtoupsertNodeandupsertNodeSqlite's INSERT and UPDATE sets.sourceIdparameter togetNodesByNumsand use it inbuildSourceNeighborInfoso node transport classification is always scoped to the source that heard the packet.Bug #3777 — Neighbor lines drawn twice + missing signal info in both directions
When both A→B and B→A records exist in the DB, both were being sent to the map (two overlapping polylines). Additionally, the reverse direction's SNR/timestamp was being discarded by the dedup.
Fix:
buildSourceNeighborInfo: deduplicate using a canonical pair key (min(A,B)-max(A,B)). Only the smaller-nodeNum-first direction is kept;bidirectionalis set totrueand the dropped reverse link's SNR/timestamp is backfilled asreverseSnr/reverseTimestampon the kept record.mergeUnifiedSourceData(useDashboardData.ts): same dedup across sources.DashboardMap.tsx: neighbor polylines now have aDashboardNeighborPopupshowing transport class, bidirectional state, and both directions' SNR + report time.Changes
src/db/repositories/nodes.tstransportMechanismto upsertNode and upsertNodeSqlite INSERT/UPDATE; add optionalsourceIdtogetNodesByNumssrc/server/services/sourceDashboardData.tsreverseSnr/reverseTimestampbackfillsrc/hooks/useDashboardData.tsmergeUnifiedSourceDatasrc/components/Dashboard/DashboardMap.tsxDashboardNeighborPopupon each linesrc/components/Dashboard/DashboardNeighborPopup.tsxsrc/db/repositories/nodes.test.tsgetNodesByNumssourceId scopingsrc/hooks/useDashboardData.test.tssrc/server/routes/sourceRoutes.neighbor-info.test.tsreverseSnrassertionsrc/components/Dashboard/DashboardNeighborPopup.test.tsxTest plan
npx vitest run src/db/repositories/nodes.test.ts— sourceId-scoping test passesnpx vitest run src/hooks/useDashboardData.test.ts— 3 new dedup tests passnpx vitest run src/server/routes/sourceRoutes.neighbor-info.test.ts— length 1, bidirectional=true, reverseSnr correctnpx vitest run src/components/Dashboard/DashboardNeighborPopup.test.tsx— all popup tests pass🤖 Generated with Claude Code
https://claude.ai/code/session_01Jf83jmbVNEgu8Fh4Dvuqea