feat(web): waypoint create/edit + geofences (design#114)#1231
feat(web): waypoint create/edit + geofences (design#114)#1231danditomaso wants to merge 10 commits into
Conversation
|
@danditomaso is attempting to deploy a commit to the Meshtastic Team on Vercel. A member of the Team first needs to authorize it. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds waypoint geofence support across protobufs, geofence utilities, alerting, map rendering, waypoint editing, and map-page integration. It also updates protobuf build and workspace configuration. ChangesWaypoint Geofences Feature
Protobuf Build Tooling
Estimated code review effort: 4 (Complex) | ~75 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
Pull request overview
Implements end-to-end waypoint geofences for the Meshtastic web client (per design#114), including waypoint create/edit UI, geofence rendering overlays, and an alert engine that emits enter/exit toasts with a deep-link “View on map” action.
Changes:
- Adds waypoint create/edit flow (including placement mode) and geofence controls (radius presets + bounding-box draw).
- Renders geofence overlays (circle + bounding box) on the map and adds a layer visibility toggle.
- Introduces a baseline-first geofence crossing tracker and hook-driven toast notifications with map-focus deep linking.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Allows buf binary builds and pins @meshtastic/protobufs to the workspace for local stubs. |
| pnpm-lock.yaml | Locks dependency changes needed for buf + updated protoc-gen-es and workspace protobuf override. |
| packages/protobufs/package.json | Adds buf tooling and postinstall regeneration for protobuf TS stubs. |
| packages/protobufs/meshtastic/mesh.proto | Extends Waypoint with geofence fields and adds the BoundingBox message (WSEN order). |
| apps/web/src/pages/Map/index.tsx | Wires geofence layer, waypoint editor/creator, placement mode, bounding-box drawing overlay, and focus-by-waypoint-id deep link handling. |
| apps/web/src/core/utils/geofenceCrossings.ts | Adds baseline-first (waypointId,nodeNum) geofence crossing state machine. |
| apps/web/src/core/utils/geofenceCrossings.test.ts | Tests baseline-first semantics and per-node/per-waypoint independence. |
| apps/web/src/core/utils/geofence.ts | Adds locale unit helpers and point-in-circle/box geofence evaluation helpers. |
| apps/web/src/core/utils/geofence.test.ts | Tests units helpers and point-in-region logic (including anti-meridian boxes). |
| apps/web/src/core/stores/deviceStore/deviceStore.test.ts | Ensures geofence waypoint fields persist through the device store. |
| apps/web/src/core/stores/appStore/index.ts | Adds focusWaypointId slice + setter for deep-link focusing. |
| apps/web/src/core/stores/appStore/appStore.test.ts | Tests focusWaypointId action/state behavior. |
| apps/web/src/core/hooks/useGeofenceAlerts.ts | Subscribes to position packets, evaluates crossings, applies favorites gate, and shows enter/exit toasts with “View on map”. |
| apps/web/src/core/hooks/useBoundingBoxDraw.ts | Adds a promise-based drag-to-define bounding-box draw hook for MapLibre. |
| apps/web/src/core/hooks/useBoundingBoxDraw.test.tsx | Tests draw normalization, cancellation, and zero-area behavior. |
| apps/web/src/components/PageComponents/Map/Tools/MapLayerTool.tsx | Adds “geofences” visibility toggle state and UI entry. |
| apps/web/src/components/PageComponents/Map/Popups/WaypointDetail.tsx | Displays geofence metadata and adds an “Add/Edit geofence” action. |
| apps/web/src/components/PageComponents/Map/Layers/WaypointLayer.tsx | Plumbs an edit callback from waypoint popup into the waypoint editor flow. |
| apps/web/src/components/PageComponents/Map/Layers/GeofenceLayer.tsx | Adds a layer that generates and renders circle + bounding-box polygon features for waypoint geofences. |
| apps/web/src/components/PageComponents/Map/Layers/GeofenceLayer.test.ts | Tests GeoJSON feature generation for circle/box/both. |
| apps/web/src/components/PageComponents/Map/BoundingBoxOverlay.tsx | Adds a screen-space rectangle overlay used during bounding-box drawing. |
| apps/web/src/components/Dialog/WaypointEditDialog.tsx | Adds waypoint create/edit dialog including geofence presets, draw/edit/remove bounding box, and notify toggles. |
| apps/web/src/components/Dialog/WaypointEditDialog.test.tsx | Tests progressive reveal rules, draw callback wiring, and outbound geofence field persistence. |
| apps/web/src/App.tsx | Installs a bridge component to run geofence alert evaluation at app scope. |
| apps/web/public/i18n/locales/en/map.json | Adds strings for geofence layer toggle, waypoint editor UI, toast text, and unit strings. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (5)
pnpm-workspace.yaml (1)
2-2: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winAllowing build scripts for
@bufbuild/bufweakens pnpm's default script-blocking protection.This is necessary to let
buf's postinstall fetch its native binary, but it's worth flagging as a conscious trust decision on that dependency, since pnpm blocks arbitrary install scripts by default specifically to mitigate supply-chain risk.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pnpm-workspace.yaml` at line 2, The pnpm workspace is explicitly allowing build scripts for `@bufbuild/buf`, which bypasses pnpm’s default install-script blocking. Keep this allowlist only if the dependency is fully trusted and the native binary fetch is required, and make the trust decision clearly scoped to `@bufbuild/buf` in pnpm-workspace.yaml rather than broadening script execution for other packages.apps/web/src/core/hooks/useGeofenceAlerts.ts (2)
42-47: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsolidate the duplicate
client.nodes.byNum(from)lookup.
byNum(from)is called twice — once for the favorites gate, once fornodeName. A single lookup avoids duplicated work and keeps both usages guaranteed-consistent.♻️ Suggested fix
- if (wp.notifyFavoritesOnly) { - const node = client.nodes.byNum(from); - if (!node?.isFavorite) continue; - } - const node = client.nodes.byNum(from); + const node = client.nodes.byNum(from); + if (wp.notifyFavoritesOnly && !node?.isFavorite) continue; const nodeName = node?.user?.longName ?? String(from);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/core/hooks/useGeofenceAlerts.ts` around lines 42 - 47, The `useGeofenceAlerts` logic is doing a duplicate `client.nodes.byNum(from)` lookup, once in the `wp.notifyFavoritesOnly` gate and again for `nodeName`. Refactor the surrounding block to fetch the node once, reuse that same `node` for the favorites check and the `node?.user?.longName ?? String(from)` fallback, and keep the existing behavior unchanged.
34-34: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winUse
coordToDeginstead of dividing by1e7inline.
geofence.tsalready exports the shared conversion helper, so this hook can reuse it and stay aligned with the geofence math.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/core/hooks/useGeofenceAlerts.ts` at line 34, The geofence coordinate conversion in useGeofenceAlerts is hardcoded inline instead of using the shared helper. Update the point creation logic in useGeofenceAlerts to call coordToDeg from geofence.ts for both longitudeI and latitudeI, so the hook reuses the same conversion path as the rest of the geofence math and stays consistent with shared behavior.apps/web/src/core/hooks/useBoundingBoxDraw.ts (1)
47-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate pixel→lngLat conversion in
onPointerDown/onPointerMove.Both handlers repeat the same
getBoundingClientRect()+map.unproject(...)logic. Extract a small helper to avoid divergence risk.♻️ Suggested refactor
+ const toMapLngLat = useCallback( + (event: React.PointerEvent): [number, number] | undefined => { + const map = mapRef?.getMap(); + if (!map) return undefined; + const rect = map.getContainer().getBoundingClientRect(); + const lngLat = map.unproject([event.clientX - rect.left, event.clientY - rect.top]); + return [lngLat.lng, lngLat.lat]; + }, + [mapRef], + ); + const onPointerDown = useCallback( (event: React.PointerEvent) => { - const map = mapRef?.getMap(); - if (!map) return; - const rect = map.getContainer().getBoundingClientRect(); - const lngLat = map.unproject([event.clientX - rect.left, event.clientY - rect.top]); - startRef.current = [lngLat.lng, lngLat.lat]; - currentRef.current = [lngLat.lng, lngLat.lat]; + const point = toMapLngLat(event); + if (!point) return; + startRef.current = point; + currentRef.current = point; setState({ active: true, start: startRef.current, current: currentRef.current }); event.currentTarget.setPointerCapture(event.pointerId); }, - [mapRef], + [toMapLngLat], );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/core/hooks/useBoundingBoxDraw.ts` around lines 47 - 72, The pointer handlers in useBoundingBoxDraw repeat the same pixel-to-lngLat conversion logic in onPointerDown and onPointerMove. Extract the shared getBoundingClientRect() plus map.unproject(...) code into a small helper within useBoundingBoxDraw so both handlers call the same conversion path, keeping startRef/currentRef updates unchanged and reducing the risk of divergence.apps/web/src/components/Dialog/WaypointEditDialog.tsx (1)
34-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated large-unit threshold/conversion constants; inconsistent mile-preset label.
The
unitSystem === "imperial" ? value >= 1609.344 : value >= 1000threshold check and the1609.344magic number are repeated in three places (initial form derivation, preset application, and label rendering), independent of thedisplayToMeters/metersToDisplayhelpers already imported from@core/utils/geofence.ts. This is fragile if the threshold logic in those utilities ever diverges from the copies here.Separately, line 390's special case
toFixed(meters === 1609 ? 1 : 0)produces"1.0 mi"for the 1-mile preset while the 5/10-mile presets render as"5 mi"/"10 mi"— an inconsistent, likely unintended display quirk.♻️ Suggested cleanup
- ? `${(meters / 1609.344).toFixed(meters === 1609 ? 1 : 0)} ${t("unit.mile.plural")}` + ? `${Math.round(meters / 1609.344)} ${t("unit.mile.plural")}`Consider extracting a shared
isLargeUnit(meters, system)helper (alongsidedisplayToMeters/metersToDisplayingeofence.ts) to eliminate the duplicated threshold logic.Also applies to: 90-91, 181-190, 384-394
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/Dialog/WaypointEditDialog.tsx` around lines 34 - 42, The waypoint radius logic in WaypointEditDialog duplicates the imperial/metric threshold and 1609.344 conversion in multiple spots, and the 1-mile preset label is rendered inconsistently. Replace the repeated `unitSystem === "imperial" ? value >= 1609.344 : value >= 1000` checks with the existing `displayToMeters`/`metersToDisplay` helpers or a shared helper near `geofence.ts`, and update the preset label rendering so the mile presets format consistently (including the 1-mile case) within `WaypointEditDialog`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/components/Dialog/WaypointEditDialog.tsx`:
- Around line 267-269: The save path in WaypointEditDialog is still copying
notifyOnEnter, notifyOnExit, and notifyFavoritesOnly from form into base even
after hasAnyGeofence becomes false. Update the form/state handling in
WaypointEditDialog so these flags are cleared or ignored whenever the radius/box
geofence is removed, and make sure the save logic that builds base only persists
notification flags when a geofence actually exists.
- Around line 271-273: Replace the 32-bit random waypoint ID generation in
WaypointEditDialog’s new-waypoint creation path with the existing
collision-resistant randId() helper (or an equivalent stronger generator) so
device.addWaypoint() does not accidentally dedupe and overwrite another
waypoint. Update the isCreating && base.id === 0 branch in WaypointEditDialog to
use the shared ID generator consistently with the rest of the waypoint flow.
- Around line 275-280: The waypoint flow in WaypointEditDialog currently updates
local state via device.addWaypoint before the network send, which can leave the
web store out of sync if sendWaypoint fails. Update the add/update path to
follow the same send-first pattern used by removeWaypoint: call
device.connection.sendWaypoint from the relevant handler before committing with
device.addWaypoint, and only persist locally after the send succeeds. Keep the
fix within WaypointEditDialog and preserve the existing targetChannel/fromNode
handling.
In `@apps/web/src/core/utils/geofence.ts`:
- Around line 10-17: The locale check in unitSystemFromLocale only normalizes
casing for the en-US fallback, so lowercase en-LR and en-MM are still missed and
return the wrong unit system. Update the matching logic in unitSystemFromLocale
and the IMPERIAL_LOCALES lookup to compare normalized locale strings for all
imperial entries, so casing-insensitive matches work uniformly for every
imperial locale.
In `@apps/web/src/pages/Map/index.tsx`:
- Around line 313-327: The WaypointEditDialog is being unmounted/closed during
box drawing, which causes its form state to reset when it reopens. Update the
Map page’s WaypointEditDialog usage so its open prop is driven only by
editorOpen, and handle boxDraw.active with a separate visual-hide state instead
of toggling the dialog itself. Keep editorWaypoint and editorInitialLngLat
intact across the draw flow so the dialog’s open-triggered reset effect in
WaypointEditDialog does not wipe unsaved edits mid-session.
- Around line 341-346: The box-draw pointer-capture overlay in Map/index.tsx is
missing touch gesture suppression, so touch drags can be converted into browser
scrolling/panning and cancel the draw flow. Update the fixed overlay div that
wires boxDraw.onPointerDown/onPointerMove/onPointerUp to disable touch panning
(for example via touch-action none on that element or equivalent styling) so
touch input stays with the box-draw handlers.
In `@packages/protobufs/package.json`:
- Around line 19-21: The postinstall script in package.json is triggering
protobuf generation on every install, which should be removed. Update the
scripts so buf/pnpm run gen is only invoked from an explicit build or other
opt-in command, and keep the build flow centered around the existing gen script
rather than postinstall.
---
Nitpick comments:
In `@apps/web/src/components/Dialog/WaypointEditDialog.tsx`:
- Around line 34-42: The waypoint radius logic in WaypointEditDialog duplicates
the imperial/metric threshold and 1609.344 conversion in multiple spots, and the
1-mile preset label is rendered inconsistently. Replace the repeated `unitSystem
=== "imperial" ? value >= 1609.344 : value >= 1000` checks with the existing
`displayToMeters`/`metersToDisplay` helpers or a shared helper near
`geofence.ts`, and update the preset label rendering so the mile presets format
consistently (including the 1-mile case) within `WaypointEditDialog`.
In `@apps/web/src/core/hooks/useBoundingBoxDraw.ts`:
- Around line 47-72: The pointer handlers in useBoundingBoxDraw repeat the same
pixel-to-lngLat conversion logic in onPointerDown and onPointerMove. Extract the
shared getBoundingClientRect() plus map.unproject(...) code into a small helper
within useBoundingBoxDraw so both handlers call the same conversion path,
keeping startRef/currentRef updates unchanged and reducing the risk of
divergence.
In `@apps/web/src/core/hooks/useGeofenceAlerts.ts`:
- Around line 42-47: The `useGeofenceAlerts` logic is doing a duplicate
`client.nodes.byNum(from)` lookup, once in the `wp.notifyFavoritesOnly` gate and
again for `nodeName`. Refactor the surrounding block to fetch the node once,
reuse that same `node` for the favorites check and the `node?.user?.longName ??
String(from)` fallback, and keep the existing behavior unchanged.
- Line 34: The geofence coordinate conversion in useGeofenceAlerts is hardcoded
inline instead of using the shared helper. Update the point creation logic in
useGeofenceAlerts to call coordToDeg from geofence.ts for both longitudeI and
latitudeI, so the hook reuses the same conversion path as the rest of the
geofence math and stays consistent with shared behavior.
In `@pnpm-workspace.yaml`:
- Line 2: The pnpm workspace is explicitly allowing build scripts for
`@bufbuild/buf`, which bypasses pnpm’s default install-script blocking. Keep this
allowlist only if the dependency is fully trusted and the native binary fetch is
required, and make the trust decision clearly scoped to `@bufbuild/buf` in
pnpm-workspace.yaml rather than broadening script execution for other packages.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: f0e514c7-7817-49bf-8f42-4b711f341b2f
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (24)
apps/web/public/i18n/locales/en/map.jsonapps/web/src/App.tsxapps/web/src/components/Dialog/WaypointEditDialog.test.tsxapps/web/src/components/Dialog/WaypointEditDialog.tsxapps/web/src/components/PageComponents/Map/BoundingBoxOverlay.tsxapps/web/src/components/PageComponents/Map/Layers/GeofenceLayer.test.tsapps/web/src/components/PageComponents/Map/Layers/GeofenceLayer.tsxapps/web/src/components/PageComponents/Map/Layers/WaypointLayer.tsxapps/web/src/components/PageComponents/Map/Popups/WaypointDetail.tsxapps/web/src/components/PageComponents/Map/Tools/MapLayerTool.tsxapps/web/src/core/hooks/useBoundingBoxDraw.test.tsxapps/web/src/core/hooks/useBoundingBoxDraw.tsapps/web/src/core/hooks/useGeofenceAlerts.tsapps/web/src/core/stores/appStore/appStore.test.tsapps/web/src/core/stores/appStore/index.tsapps/web/src/core/stores/deviceStore/deviceStore.test.tsapps/web/src/core/utils/geofence.test.tsapps/web/src/core/utils/geofence.tsapps/web/src/core/utils/geofenceCrossings.test.tsapps/web/src/core/utils/geofenceCrossings.tsapps/web/src/pages/Map/index.tsxpackages/protobufs/meshtastic/mesh.protopackages/protobufs/package.jsonpnpm-workspace.yaml
Implements the Waypoint geofences client spec (meshtastic/design#114) end to end in the web client: * Proto extended with `geofence_radius`, `bounding_box`, `notify_on_enter`, `notify_on_exit`, `notify_favorites_only` (fields 9-13) plus a `BoundingBox` message (WSEN). Regenerated at install time — `@bufbuild/buf` added as a workspace devDep and a `postinstall` hook regenerates `packages/ts/dist`, keeping the generated stubs out of git per repo convention. * SDK protobufs override in `pnpm-workspace.yaml` so all packages consume the workspace `@meshtastic/protobufs` instead of the JSR-pinned copy. * Waypoint create UI: the web client previously had no create/edit flow; a new toolbar toggle enters placement mode (cursor swapped to a MapPinPlus SVG matching the toolbar affordance) and the map click seeds the editor dialog with name/description/icon/coords/expiry fields. * Geofence controls in the editor: Off + locale-aware radius presets (metric: 100 m / 500 m / 1 km / 5 km; imperial: 528 ft / 0.5 mi / 1 mi / 5 mi / 10 mi), a drag-to-define bounding box (`useBoundingBoxDraw` + `BoundingBoxOverlay`), and enter/exit + favorites-only toggles that reveal progressively per the spec's UX affordances. * Map overlays: `GeofenceLayer` renders turf circles + WSEN polygons for every waypoint that carries a geofence, toggled via the layer tool. * Alert engine: `GeofenceCrossings` implements baseline-first per-pair `(waypointId, nodeNum)` state, `useGeofenceAlerts` evaluates each incoming node position, applies the favorites-only gate against the SDK `NodesClient`, and emits an enter/exit toast with a ToastAction that deep-links to the waypoint on the map via router.navigate + a new `focusWaypointId` slice on the app store. * Persistence: existing `addWaypoint` spread covers the new fields; a store test asserts the geofence payload round-trips through the device store. * i18n: added `waypointEdit.*` block plus radius/unit/preset strings. * Tests: 8 new suites covering point-in-region math, baseline-first crossings, overlay feature generation, drag-to-define hook, editor dialog interaction (preset selection, progressive reveal, save wiring), focus-waypoint app-store slice, and geofence persistence in the device store. 275 tests pass in apps/web, 419 across the workspace. Refs: meshtastic/design#114, meshtastic/protobufs#962
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
0025d03 to
755c09b
Compare
…string Review finding: the `unitSystemFromLocale` helper only lowercased for the `en-US` fallback path, so `en-lr` / `en-mm` etc. would silently fall through to metric. Rather than harden the locale-string parsing, switch to the convention already used elsewhere in the app (see the position-precision selector in `Channels/Channel.tsx`): read `config.display.units` from the connected device. Waypoint create/edit now: * Reads `Protobuf.Config.Config_DisplayConfig_DisplayUnits` from `useDevice().config.display.units`. * Exposes `unitSystemFromDisplayUnits(units | undefined)` on the geofence helper. Defaults to metric when the config slice hasn't loaded. * Drops the ad-hoc `IMPERIAL_LOCALES` set and the locale-casing branch. Tests updated to cover the enum mapping. Dialog test now mocks `config.display.units` on the fake device instead of `i18n.language`.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/core/utils/geofence.ts`:
- Around line 24-30: The imperial conversion in metersToDisplay is inconsistent
with WaypointEditDialog.initialForm because it switches to feet below 1 mile
while the editor uses useLargeUnit at 0.5 mile. Update metersToDisplay to use
the same 0.5-mile threshold for imperial display so values between 0.5 and 1
mile stay in miles, and verify the logic stays aligned with the geofenceRadius
flow in WaypointEditDialog.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d38eedea-f427-4c4b-a0e5-76f0a5fd5e03
📒 Files selected for processing (4)
apps/web/src/components/Dialog/WaypointEditDialog.test.tsxapps/web/src/components/Dialog/WaypointEditDialog.tsxapps/web/src/core/utils/geofence.test.tsapps/web/src/core/utils/geofence.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/web/src/core/utils/geofence.test.ts
- apps/web/src/components/Dialog/WaypointEditDialog.test.tsx
- apps/web/src/components/Dialog/WaypointEditDialog.tsx
Review finding: `Math.floor(Math.random() * 0xffffffff)` is neither collision-resistant nor consistent with the rest of the waypoint flow. `sendWaypoint` in `packages/sdk` already stamps outbound waypoints with `generatePacketId()` (crypto.getRandomValues-backed), so the local id we stored via `addWaypoint` was drifting from the id the mesh actually sees and could collide with an existing waypoint on the device store's per-id dedup. Reuse the shared generator so the local and broadcast ids match.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Drop postinstall hook so pnpm install stays offline (buf uses remote plugin needing BSR access; CodeRabbit flagged on PR). - Regenerate dist against protoc-gen-es 2.12.1 (stale after meshtastic#1204). - Export DeviceUi, DeviceOnly, DeviceOnlyLegacy, InterDevice, SerialHal from mod.ts to match generated files.
… layer - Align imperial unit threshold at 0.5 mi across metersToDisplay, useLargeUnit calc, applyRadiusPreset, and preset labels so a 0.5 mi waypoint no longer round-trips as feet. - Gate notifyOnEnter/notifyOnExit/notifyFavoritesOnly on hasAnyGeofence in save path; removing the geofence no longer persists stale notify flags. - Send waypoint before addWaypoint so a transport error doesn't desync the local store from the mesh (mirrors removeWaypoint ordering). - Drop `open` from the form-reset useEffect so unmounting during box draw no longer wipes unsaved edits when the dialog reopens. - Split anti-meridian bounding boxes into two polygons in GeofenceLayer so the overlay matches pointInBoundingBox containment instead of drawing a globe-wrapping polygon.
Resolve pnpm-lock.yaml conflict by taking upstream lock and running pnpm install to sync with merged package.json ranges.
| if (!Number.isFinite(lat) || !Number.isFinite(lng)) { | ||
| toast({ title: t("waypointEdit.errorBadCoords") }); | ||
| setSaving(false); | ||
| return; | ||
| } |
| action: createElement( | ||
| ToastAction, | ||
| { | ||
| altText: t("waypointEdit.viewOnMap"), | ||
| onClick: () => { | ||
| setFocusWaypointId(wp.id); | ||
| void router.navigate({ to: "/map" }); | ||
| }, | ||
| }, | ||
| t("waypointEdit.viewOnMap"), | ||
| ) as unknown as ToastActionElement, |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary
Implements the Waypoint geofences client
spec end-to-end for the
web client. Also introduces the missing waypoint create/edit flow (the web
app previously had no authoring UI at all) so the geofence controls have a
surface to live on.
Proto
Waypointgainsgeofence_radius,bounding_box,notify_on_enter,notify_on_exit,notify_favorites_only(fields 9–13); newBoundingBoxmessage uses GeoJSON-style WSEN order.@meshtastic/protobufsworkspace override pinned viapnpm-workspace.yamlso the SDK consumes local stubs instead of the JSR-pinned copy.@bufbuild/bufadded as a devDep with apostinstallregen hook.Feature scope (per design#114)
useBoundingBoxDraw+BoundingBoxOverlay); Edit / Remove buttons after set.GeofenceCrossingsimplements baseline-first per-(waypointId, nodeNum)state (in-memory, not persisted, matches spec).useGeofenceAlertsruns on every incoming position, applies the favorites-only gate viaMeshClient.nodes.byNum(from).isFavorite, and emits an enter/exit toast with a View on map action that deep-links viarouter.navigate+ a newfocusWaypointIdslice on the app store.MapPinPlusSVG and captures the next click as the new waypoint location.Notes
2.7.26/06d729a; older receivers drop the unknown fields silently.apps/web/src/core/connections/sdkClient.ts:39-65fails silently on OPFS timeout / unavailability and falls back toInMemoryMessageRepository. Not touched in this PR.Closes #1227.
Test plan
pnpm --filter meshtastic-web test— 275 pass (8 new suites:point-in-region math, baseline-first crossings, overlay features,
drag-to-define hook, editor dialog interaction, focus-waypoint app-store
slice, deviceStore geofence persistence, plus existing suites)
pnpm --filter meshtastic-web build— cleanpnpm run test— 419 tests pass across the workspacepreset, draw a bounding box, save, confirm circle + rectangle overlays
render
until radius/box exists and Favorites Only hidden until Enter or Exit is on
behavior — first position from that node emits no toast, subsequent
boundary crossings emit enter/exit toasts, "View on map" action recentres
and opens the popup
en-USand confirm imperial radiuspresets, other locales show metric
trip through the device
Summary by CodeRabbit