Saving Marker Location After Edit#207
Conversation
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
left a comment
There was a problem hiding this comment.
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.
Summary
routesstate before clearingpendingPinMove.Motivation
setPendingPinMove(null), throwing away the drag.Changes
Frontend (
app/ui)page.tsxsavePendingPinMoveis defined beforehandleEditModeChange.value === false), callsavePendingPinMove()then clearpendingPinMove.cancelPendingPinMove, mobile Cancel with pending drag) unchanged — still discard without committing.Backend / mobile app / infra
Validation
Frontend
npm --prefix app/ui run lintnpm --prefix app/ui run format:checknpm --prefix app/ui run typechecknpm --prefix app/ui run testnpm --prefix app/ui run buildnpm --prefix app/ui run test:e2enpm --prefix app/mobile run lintnpm --prefix app/mobile run typecheckBackend
cmake --preset dev.github/scripts/check-backend-static.sh build/devcmake --build --preset dev --parallelctest --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 configManual checks
/results→ Edit → drag a marker → Save edits → marker stays at new location; route polyline updates.Risk
page.tsx; no API or persistence layer changes.handleEditModeChange(false)path (Save edits, export flow) now commits a pending drag — intentional and consistent with “save” semantics.Rollout and Recovery
app/ui; no migrations or feature flags.fixing-road-path(this branch adds one commit on top).High-Signal PR Checklist
Closes #196