Commit graph

10 commits

Author SHA1 Message Date
SkateSpots
4474cc26e9 feat(directions): bigger map + app-like layout and richer route details
- Map is now the hero: h-[38vh] mobile / 400px desktop (was 256), with a metric
  Leaflet scale control.
- New layout: flex column with header/modes/From/map pinned, the details as the
  ONLY scroll region, and Start/Stop pinned at the bottom so it's always
  reachable. Previously the dialog was a single overflow-hidden box, so once the
  map grew everything below the fold (Start, steps) was unreachable.
- Summary rebuilt as stat tiles (distance / time / arrive) plus a detail line:
  step count, honest 'uses driving roads'/'driving estimate' notes, and the
  average speed the engine assumed ('21 km/h avg'; foot keeps the skate cruise
  estimate). Steps header shows the count; step list scrolls taller (max-h-72).
- Wider dialog on desktop (sm:max-w-xl).

Verified in-app: tiles + 9/8/5/5 steps per mode, follow banner still tracks
movement, offline chip still appears, map 371px -> 321px with the new layout.
2026-09-27 19:42:06 +07:00
SkateSpots
5a819d5a31 feat(directions): follow mode, choose-your-start, offline route cache
Follow mode (explicit Start; off by default — a watcher drains battery):
watchPosition in geolocation.ts, live dot, 'NEXT · step N' banner with
distance-to-turn + distance-to-go, current step highlighted, map follows.
A transient watch error (timeout/unavailable) no longer kills it — only a
permission denial does; the watcher recovers on its own.

Choose your start: 'From → My location / Choose on map' — tapping the map sets
the origin, and the route recomputes from there.

Offline: src/lib/routeCache.ts keeps the last route per destination+mode with
the line downsampled to <=250 points. If a fetch fails (no signal / routing
server down) the dialog falls back to it with an 'Offline · saved route {age}'
badge instead of a dead error.

Also tidied the map overlays (offline badge bottom-left, Whole route
bottom-right) so nothing covers Leaflet's zoom control.

Verified with CDP: controls present; follow banner tracks movement (step 1 ->
step 7, distances update); blocking routing shows the cached route + badge;
picking a start on the map changed the route (968 m -> 922 m). Still no voice
guidance or re-routing if you leave the route (documented).
2026-09-27 19:11:14 +07:00
SkateSpots
59e5829b2a feat(directions): make the route dialog usable as guidance, not just a preview
Answering 'are we giving the best navigation experience?': no, it was a preview
with dead ends. Fixes the concrete gaps:

- ETA: 'arrive ~08:10 PM' alongside the duration.
- Mode memory: the last travel mode is remembered (skatespots:route-mode), so
  cyclists/drivers aren't reset to Skate/Walk every trip.
- Tappable steps: each step pans the map and highlights; added a 'Whole route'
  re-fit button to zoom back out.
- Taller route map on desktop (h-64 sm:h-80).
- min-h-0 on step rows: the app-wide 44px mobile touch-target rule was making
  every step a 44px slab (rows now 29px).

Still out of scope (documented, not faked): live turn-by-turn following, voice
guidance and offline routing.
2026-09-27 18:47:29 +07:00
SkateSpots
e69bace784 fix(amber,routing): scope Amber prompts to real actions; real per-mode routes + steps
AMBER — 'pops on every screen':
staleTime: Infinity only stops REFETCHING, never the FIRST decrypt. So the
encrypted useSyncedAppData blobs (read-state, preferences) each ran a queryFn on
mount (racing consumers multiplied it) and relay responses landing during
navigation made it look per-page. useSyncedAppData now has interactiveNetwork
(default false): for an interactive signer the blob is LOCAL-ONLY — query not
even enabled, patch() a no-op — no decrypt on mount, no publish on a nav-tab
change. Verified with a mocked x-amber login: network-on => Amber op at startup,
with the fix => 0. (useCrewDismissals/useStash were already gated.)

ROUTING — 'same time for every transport option':
router.project-osrm.org hosts ONE car dataset and ignores the profile in the URL,
so foot/bike/car returned identical numbers. Switched to the FOSSGIS per-profile
servers (routing.openstreetmap.de/routed-{foot,bike,car}); the demo server is now
only a last-resort fallback and its result is flagged approximate ('driving
estimate'). Request steps=true and surface turn-by-turn steps for orientation,
plus a '~X min on a board' skate estimate next to the walking time. Verified:
same trip => walk 13 min / bike 5 / moto+car 2, with 5-9 steps each.
2026-09-27 18:35:17 +07:00
SkateSpots
0b5230d4ab fix(directions): route map never rendered + dialog stacked under the spot sheet
RouteDialog was effectively broken — users got an empty dialog / 'no route'.

- Map init: the Leaflet map lives in Radix Dialog content, which mounts AFTER
  the first effect pass, so the mapDivRef guard was null, the init effect bailed,
  and the map was never created (no route line, ever). Hold the node in state
  with a callback ref and add it to the effect deps.
- Height: the global .leaflet-container{height:100%} override beats h-64; against
  a height-less parent it resolved to 0 (invisible map). Height now lives on the
  wrapper (relative h-64) and the map is h-full.
- Stacking: from the map, Directions rendered the dialog inside the spot Sheet
  (two nested Radix modals; the sheet covered the route). The map now owns the
  RouteDialog at page level and closes the sheet first.
- Routing transport: call OSRM directly (it sends CORS '*') with the cors proxy
  only as fallback; distinguish busy / no-route / unreachable errors and guard
  against NaN coordinates.
2026-09-27 17:07:44 +07:00
SkateSpots
e5e2f7d7a1 fix(nav,amber,reputation,routing): header crowding, no read-state popups, anti-collusion rep, instant directions
- Header: shrink-0 logo + whitespace-nowrap + gap, nav shrinks/scrolls so it
  can never collide with (or glow over) the SkateSpots wordmark; tighter item
  padding. Fixes the top-left logo/nav overlap.
- Amber: useReadState keeps read markers LOCAL-ONLY for interactive signers, so
  merely opening the bell/DM no longer publishes (encrypt + sign = 2 popups).
- Reputation: lead with verifiedCounterparties (distinct people), cap each
  partner at 3 in the WoT-weighted score, warn when every confirmation is with
  one account. NIP.md documents the collusion limit honestly.
- Directions: skip the live GPS call when the cached fix is <2 min old; the
  blocking 'Finding your location' overlay only shows with no seed (silent
  refine chip otherwise). Near Me also flips the sort to proximity.
- .gitignore: cvm-server/package.json (local dev copy of package.cvm.json).
2026-09-27 16:42:39 +07:00
SkateSpots
f95e2233c8 fix(mobile-ux): Mints in the nav, instant directions, no stray signer prompt
- Nav: MIDDLE_SLOTS 3->4 and add 'mints' to the default middle + desktop tabs,
  so the Cashu mints directory is reachable on the phone out of the box (also
  still linked from Wallet; any of the 4 slots can be swapped via long-press).
- Directions: RouteDialog now seeds the origin from the last known location and
  draws immediately, then refines with a live fix — no more hanging on 'Finding
  your location…'. Shortened the web geolocation fallback 15s -> 8s.
- Amber: /api/ai/status is now public (it only reports a small balance), so
  showing the AI balance no longer signs NIP-98 / opens the signer. The signer is
  reserved for actions that actually need it.
2026-09-27 15:54:29 +07:00
SkateSpots
b8757eccd0 feat(directions): every Directions button now uses the in-app router
- MapPage: replace the external 'Open with' menu (Google/OSM/Waze) with the
  shared in-app RouteDialog, so the map spot sheet routes natively (incl. the
  Transitous transit mode).
- FoodPage: replace the Google Maps handoff with RouteDialog.
- RouteDialog: remove the opt-in external 'Open in' handoff and delete
  src/lib/directions.ts — no external map apps anywhere (product decision).
2026-09-27 11:25:56 +07:00
SkateSpots
fdf33d2c45 feat(directions): public-transport mode (Transitous) + open-in-maps handoff
- routing.ts: add a 'transit' travel mode via Transitous (MOTIS 2 API, keyless,
  open CORS), with a local precision-7 polyline decoder and per-leg parsing.
  Road modes stay on keyless OSRM; moto remains ≈car and honestly labelled.
- RouteDialog: transit draws each leg (walk dashed grey, transit coloured by
  mode) and summarises depart→arrive, duration, transfers and line chips, with
  Transitous/OpenStreetMap attribution.
- directions.ts: opt-in 'Open in' handoff to Google / Apple / OpenStreetMap
  (+ Organic Maps via geo: on Android), mode-aware. In-app routing stays the
  default; the description notes transit shares the trip with Transitous.
- No scooter/moped profile (deliberate).
2026-09-27 10:52:26 +07:00
shakespeare.diy
35915557bb Add in-app routing (foot/bike/moto/car) via OSRM — no external maps handoff
- New src/lib/routing.ts: keyless OSRM client, travel modes, distance/skate-time formatters
- New RouteDialog: self-contained Leaflet map drawing the route from the user's
  location to a spot/shop/event, with a mode toggle and distance/time summary
- Wire the "Directions" buttons on Spot/Shop/Event detail pages to route in-app
  instead of opening Google/Apple Maps

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-03 10:17:06 +07:00