Skip to content

Saving Marker Location After Edit#207

Open
KesarSidhu wants to merge 6 commits into
benevolentbandwidth:mainfrom
KesarSidhu:saving-marker-location
Open

Saving Marker Location After Edit#207
KesarSidhu wants to merge 6 commits into
benevolentbandwidth:mainfrom
KesarSidhu:saving-marker-location

Conversation

@KesarSidhu

@KesarSidhu KesarSidhu commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes a bug where dragging a route marker and clicking Save edits discarded the move and snapped the marker back to its original position.
  • Exiting edit mode now commits any pending pin drag to in-session routes state before clearing pendingPinMove.
  • Cancel still discards a pending drag without saving (unchanged).

Motivation

  • Save edits is the primary exit-edit affordance in the sidebar/bottom sheet, but it only toggled edit mode off and called setPendingPinMove(null), throwing away the drag.
  • The only path that persisted a move was the contextual header Save button shown while a drag was pending — easy to miss.
  • Users expect Save edits to keep their marker change; this aligns behavior with that expectation without redesigning the edit/save UX.

Changes

Frontend (app/ui)

  • page.tsx
    • Reorder handlers so savePendingPinMove is defined before handleEditModeChange.
    • On exit edit mode (value === false), call savePendingPinMove() then clear pendingPinMove.
    • Cancel paths (cancelPendingPinMove, mobile Cancel with pending drag) unchanged — still discard without committing.

Backend / mobile app / infra

  • No backend, mobile app, or infrastructure changes in this PR.

Validation

Frontend

  • npm --prefix app/ui run lint
  • npm --prefix app/ui run format:check
  • npm --prefix app/ui run typecheck
  • npm --prefix app/ui run test
  • npm --prefix app/ui run build
  • npm --prefix app/ui run test:e2e
  • npm --prefix app/mobile run lint
  • npm --prefix app/mobile run typecheck

Backend

  • cmake --preset dev
  • .github/scripts/check-backend-static.sh build/dev
  • cmake --build --preset dev --parallel
  • ctest --preset dev --output-on-failure --no-tests=error -LE 'e2e|docker'
  • docker compose -f deploy/compose/docker-compose.arm64.yml --env-file deploy/env/http-server.arm64.env config

Manual checks

  1. Optimize → open /resultsEdit → drag a marker → Save edits → marker stays at new location; route polyline updates.
  2. Edit → drag a marker → Cancel (header or mobile) → marker snaps back; coordinates unchanged.
  3. Edit → drag a marker → header Save (contextual) → still works as before.
  4. Edit → drag a marker → Done editing from export warning → pending move is committed (same exit-edit path).

Risk

  • Low: Single handler change in page.tsx; no API or persistence layer changes.
  • Exiting edit mode via any handleEditModeChange(false) path (Save edits, export flow) now commits a pending drag — intentional and consistent with “save” semantics.
  • In-session only; coordinates are not persisted to backend (out of scope).

Rollout and Recovery

  • Frontend-only deploy of app/ui; no migrations or feature flags.
  • Suggested base branch: fixing-road-path (this branch adds one commit on top).
  • Roll back by reverting this PR; Save edits will again discard pending drags.

High-Signal PR Checklist

  • The summary states the user-visible or operational outcome, not just file names.
  • The motivation explains why this change is needed now.
  • The change list separates frontend, backend, infrastructure, and documentation work where applicable.
  • Validation includes exact commands run, relevant output, and any checks intentionally skipped.
  • Risks, migrations, feature flags, and rollback steps are called out when relevant.
  • Screenshots or request/response examples are included for UI and API behavior changes.

Closes #196

Kesar Sidhu and others added 6 commits June 22, 2026 11:17
Use callback-based directions requests with status checks, extract road paths
from overview or leg steps, fetch routes sequentially to avoid rate limits,
stop overwriting cached road paths with straight lines, and document Directions
API requirement in .env.example.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…smatch

Restore straight-line polyline fallback on directions cache miss after pin
commit, recreate polylines when the directions effect clears refs first,
dedupe step-path vertices in extractRoadPath, and defer missing-data errors
to a client-only useEffect to prevent SSR hydration mismatch.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the client-only error useEffect with useSyncExternalStore so
validation runs after hydration without triggering react-hooks/set-state-in-effect.

Co-authored-by: Cursor <cursoragent@cursor.com>
Save edits now persists a dragged marker before leaving edit mode instead
of discarding pendingPinMove. Cancel still drops the pending move unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

@markboenigk markboenigk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Overall: The core fix is correct — wiring savePendingPinMove() into handleEditModeChange(false) and reordering the definitions is the right approach. The Map.tsx refactor (stable routesPathKey dep, refs for callbacks, serial Directions fetching) is solid. Two things worth flagging:

1. Serial fetch has no per-request timeout

The for-await loop is the right call for rate-limit avoidance, but if the Directions API stalls on one route (UNKNOWN_ERROR, dropped connection), the await hangs indefinitely and every route after it never gets a polyline — silently. The old Promise.allSettled at least drew the other routes while one was pending.

Suggest adding a timeout per iteration:

```ts
const withTimeout = (p: Promise, ms: number): Promise =>
Promise.race([p, new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), ms))]);

const result = await withTimeout(requestDrivingDirections(directionsService, { ... }), 10_000);
```

Then the catch block's drawFallback handles it like any other failure.

2. requestDrivingDirections wraps a promise the SDK already returns

directionsService.route(request) — called without a callback — returns a native Promise<DirectionsResult> in @types/google.maps ≥ 3.54. The wrapper recreates this with a flipped signature and a custom error message. It works, but it's extra surface to maintain. Consider just await directionsService.route({ origin, destination, ... }) directly in drawRoutePolyline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: New Marker Location is Not Saved After Edit

2 participants