Root cause: /api/traffic is on Caddy's @authed_api, so when logged out
MapLibre's raster tile fetches receive a 302 to the Authentik login (HTML),
which it can't decode as an image and retries on every map move — console spam
and a stuck-feeling Traffic toggle.
Fix (frontend-only; /api/traffic stays auth-gated in Caddy):
- LayerControl: the Traffic toggle is always rendered but disabled (greyed,
"Sign in to enable traffic" tooltip) until auth has loaded AND the user is
authenticated — mirroring Panel.jsx's contacts gating. The add-traffic apply
effect now also requires auth.authenticated (and lists it in deps), and the
mount init only restores saved traffic=true when authenticated.
- Teardown on session -> anonymous: an effect flips traffic:false once auth has
loaded and the user is not authenticated, which drives the apply effect to
removeTrafficLayer (no further tile requests).
- MapView: the style-reload re-apply (which re-adds layers from localStorage on
theme/style changes) now also checks auth.authenticated for traffic, so it
can't re-add the source for an anonymous session — the second add path that
would otherwise reintroduce the 302 retry loop.
- localStorage hydration: LayerControl now subscribes via useConfig() and its
init effect depends on [config] instead of [], so saved layer prefs hydrate
correctly once /api/config resolves (previously, mounting before config
loaded left toggles stuck off and never re-initialized).
Shown-but-disabled (not hidden) so logged-in users see no flicker on reload
during the brief pre-whoami window.
Tests: the navi repo has no test infrastructure (no vitest/jest); bootstrapping
is out of scope. Follow-up: seed a vitest + RTL test asserting the Traffic
toggle is disabled when !auth.authenticated.
Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add viewMode state to store with localStorage persistence
- Add satellite layer functions to MapView (ESRI World Imagery via nginx proxy)
- Add view mode segmented control in LayerControl popover
- Add view-mode-control CSS styles
- Hide/show vector fills and lines based on view mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Increase touch targets from 36px to 44px (meets accessibility guidelines)
- Wrap Locate and Layers buttons in unified .map-controls-br container
- Layer popover now opens LEFT of buttons (avoids collision with Locate)
- Add hover and active states with theme-aware styling
- Proper spacing for scale control below the cluster
- Increased icon sizes from 18px to 20px
- Mobile-responsive with proper max-height on layer popover
Layout:
[Locate] 44x44
[Layers] 44x44
──────────────
Scale: 0.5 mi
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add USFS trails/roads as toggleable map layer via PMTiles
- Trails: dashed brown lines, roads: solid khaki lines
- Labels at zoom 12+ for trail and road names
- Click handler shows popup with trail/road info
- Feature-flag gated with has_usfs_trails (default false)
- Add Trails toggle to Layer Control panel
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Adds contour PMTiles vector source (contours-na.pmtiles)
- Minor/intermediate/index tier rendering at z11+/z8+/z4+
- Elevation labels on index contours at z12+
- Dark theme opacity adjustment
- has_contours feature flag gated
Completes T pipeline integration (Phase 1).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Toggleable vector tile overlay rendering 651k PAD-US protected areas
as colored polygons on the map. Data-driven styling by agency/designation
(USFS green, NPS darker green, BLM tan, wilderness amber, state teal).
Unit name labels at z10+. Feature-flagged via has_public_lands_layer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GPS permission:
- Remove silent mount-time getCurrentPosition calls that cause iOS Safari
to cache a "denied" state without ever prompting the user
- LocateButton always retries getCurrentPosition on tap (user gesture)
- Only show "denied" toast on PERMISSION_DENIED (code 1), not timeout
- MapView watchPosition now starts only after confirmed grant, not unconditionally
Traffic toggle:
- Fix isStyleLoaded() race in LayerControl — if style not loaded when
toggle fires, defer to map.once("style.load") instead of silently bailing
- Change outside-click handler from mousedown to pointerdown for mobile
Mobile UX (from prior session):
- Add LocateButton component (crosshair GPS locate/re-center)
- Reposition layer control + locate button to top-right on mobile
(below MapLibre nav controls, above bottom sheet)
- ModeSelector: add min-w-0 to prevent flex overflow at 390px
- StopItem: remove button visible on touch (60% opacity vs hover-only)
- Panel: overflow-x-hidden + safe-area-inset-bottom on mobile sheet
- Body overflow-x guard on mobile viewports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New LayerControl component with popover toggles for hillshade/traffic
- MapView: add/remove hillshade raster-dem and traffic raster layers
- Overlay layers persist in localStorage, survive theme swaps
- Hillshade defaults ON, traffic defaults OFF when available
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>