Skip to content

feat(repeater): add map-based neighbors tab#182

Merged
kNoAPP merged 5 commits into
developfrom
kNoAPP/178-feat-neighbors-map
Jul 17, 2026
Merged

feat(repeater): add map-based neighbors tab#182
kNoAPP merged 5 commits into
developfrom
kNoAPP/178-feat-neighbors-map

Conversation

@kNoAPP

@kNoAPP kNoAPP commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Description

Turns the repeater Neighbors tab into a spatial, map-based view, and does
the map-layer refactor that #178 called out as a prerequisite.

Reusable map layer (refactor)

  • Extracts BaseLeafletMap from the monolithic Map view — a store-decoupled
    component that takes an arbitrary nodes set plus an optional edges set
    ({ from, to, label }, drawn as polylines with a permanent midpoint label).
    It owns only the shared machinery (tiles, single-world bounds, min-zoom
    clamp, theme swap, marker + edge layers).
  • Moves the shared nodeIcon/escapeHtml primitives into a client-only
    lib/map/leafletIcon module, and the category legend into a shared
    MapLegend component (with a children slot; the Map page supplies the
    favorites-only toggle there).
  • Rewrites MapView as a thin Map-page wrapper that composes BaseLeafletMap
    and keeps the page-only concerns (location-pick mode, viewport persistence,
    favorites filter, marker cap). No behavior change to the Map page.

Neighbors map (feature)

  • Adds NeighborsMap (loaded via dynamic/ssr: false), which anchors the
    administered repeater and plots each neighbor that resolves to a saved
    contact/advert with a GPS fix, drawing an SNR-labeled link from the repeater
    to each. Coordinates are resolved by matching the neighbor's pubkey prefix in
    new lib/map/nodes helpers (repeaterAnchorNode, locateNeighborNode).
  • Makes the Neighbors tab map-only: the map fills the full tab height, with the
    Refresh control pinned to its top-right corner. When nothing can be mapped
    (the repeater or its neighbors have no location, none were heard, or a read
    errored), a same-sized placeholder explains why. Clicking a neighbor marker
    opens its manage panel; the shared legend documents the marker categories.

Reuses the existing neighbors CLI command — no protocol-layer changes.

Issues

Type of Change

  • ! -- Breaking change (fix or feature that would cause existing
    functionality to change)
  • feat -- New feature (non-breaking change which adds functionality)
  • 🛠️ fix -- Bug fix (non-breaking change which fixes an issue)
  • 📝 docs -- Additional or updated documentation
  • 🎨 style -- Code style changes (formatting, missing semi colons, etc)
  • 🧹 refactor -- Code refactor
  • perf -- Performance improvement
  • 🧪 test -- Tests (adding or updating tests)
  • 🏗️ build -- Build system or external dependencies changes
  • ci -- Build configuration change
  • 🗑️ chore -- Grunt tasks
  • revert -- Revert previous changes

Extract a reusable BaseLeafletMap (nodes + optional SNR-labeled edges) from the monolithic Map view, moving nodeIcon/escapeHtml into lib/map/leafletIcon and the category legend into a shared MapLegend. Add NeighborsMap, which anchors the administered repeater and plots each locatable neighbor with an SNR link, and make the repeater Neighbors tab map-only with the refresh control on the map. The map fills the full tab height.
Copilot AI review requested due to automatic review settings July 17, 2026 21:51
@github-actions github-actions Bot added the ui label Jul 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a reusable Leaflet map layer and a spatial repeater-neighbors view with SNR links.

Changes:

  • Extracts shared map, marker, legend, and edge infrastructure.
  • Replaces the neighbors table with a map and localized fallback states.
  • Refactors the main Map page to use the shared layer.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
app/globals.css Styles neighbor links and SNR labels.
components/BaseLeafletMap.tsx Adds reusable Leaflet map infrastructure.
components/MapLegend.tsx Extracts the shared map legend.
components/MapView.tsx Composes the main map from shared components.
components/NeighborsMap.tsx Builds the repeater-neighbor spatial view.
components/RepeaterView.tsx Integrates the map-only neighbors tab.
lib/map/config.ts Defines edge styling and viewport types.
lib/map/leafletIcon.ts Extracts shared Leaflet icon helpers.
lib/map/nodes.ts Adds map edges and neighbor-node resolution.
locales/de.json Updates German neighbor states.
locales/en.json Defines authoritative neighbor states.
locales/es.json Updates Spanish neighbor states.
locales/fr.json Updates French neighbor states.

Comment thread components/RepeaterView.tsx
Comment thread components/RepeaterView.tsx
Comment thread lib/map/nodes.ts Outdated
Address PR review: the map-only Neighbors tab hid unlocatable neighbors and dropped the admin neighbor.remove action. Restore the map + list together (issue #178's recommended layout) — the map stays primary with every neighbor kept in the list below, and the per-row admin Remove (with its non-empty-prefix guard) returns. Also fix locateNeighborNode to fall back to a matched node's cached advert coordinates when its contact record has no fix, so a locatable neighbor is no longer dropped.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

components/RepeaterView.tsx:660

  • This count includes a row that resolves back to the repeater anchor, but buildNeighborMap intentionally drops that self-edge. If that is the only resolvable row, showMap becomes true and renders an anchor-only map with no neighbor. Apply the same anchor exclusion here.
  const mappableCount = useMemo(() => {
    if (!contact.advLat || !contact.advLon) return 0;
    return (neighbors ?? []).filter((n) =>
      locateNeighborNode(n.prefix, contacts, advertCache),
    ).length;

Comment thread components/NeighborsMap.tsx Outdated
Comment thread components/RepeaterView.tsx Outdated
Per maintainer decision, the Neighbors tab shows only the map (filling the tab), with the Refresh control on it. Unlocatable neighbors and the admin neighbor.remove action are intentionally out of scope for this tab; issue #178's design questions are resolved that way in the ticket. The locateNeighborNode advert-coordinate fallback is kept, so a contact lacking its own fix still plots from its cached advert.
The permanent SNR edge labels are produced by formatSnr, which reads the active locale, but the memo that builds the map data didn't depend on it — so a language switch left the labels in the previous decimal format until neighbor/contact data changed. Subscribe to the locale and add it to the memo dependencies.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Comment thread lib/map/nodes.ts
Comment thread components/RepeaterView.tsx Outdated
Comment thread components/RepeaterView.tsx Outdated
Address PR review: (1) locateNeighborNode now backs up an unlocated contact only with an advert for the exact same node (full-key match), so a 4-byte neighbor-prefix collision can't lend another node's coordinates; (2) the placeholder now shows a distinct message when the repeater itself has no advertised location (vs. neighbors being unlocatable); (3) update a stale comment that still described the removed table fallback.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

@kNoAPP
kNoAPP merged commit 3595a21 into develop Jul 17, 2026
5 checks passed
@kNoAPP
kNoAPP deleted the kNoAPP/178-feat-neighbors-map branch July 17, 2026 23:18
@github-actions github-actions Bot mentioned this pull request Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Map-based Neighbors tab (repeater + neighbors + SNR links)

2 participants