feat(maps): persist map view across refresh
The maps page reset to the default US-wide view on every refresh because initialViewState was hardcoded. Save the position and zoom to localStorage on each move-end (key nomad:map-view, matching the existing scale-unit pattern) and restore it at mount: saved view → default. The restore is bounds-checked so a corrupt value falls through to the default. Replaces #815, whose branch had drifted far out of scope (56 files of stale-base/merged-commit noise plus unrelated map-feature WIP). This is just the persist-view improvement, ported cleanly onto current dev. The null-island and coordinate-search parts of #815 targeted URL-param code that never landed on dev, so they don't apply here.
This commit is contained in:
parent
9b84d3aa54
commit
da60c6ce9c
|
|
@ -28,6 +28,38 @@ type MapComponentProps = {
|
|||
showCoordinatesEnabled: boolean
|
||||
}
|
||||
|
||||
const SAVED_MAP_VIEW_KEY = 'nomad:map-view'
|
||||
const DEFAULT_MAP_VIEW = { longitude: -101, latitude: 40, zoom: 3.5 }
|
||||
|
||||
type SavedMapView = { longitude: number; latitude: number; zoom: number }
|
||||
|
||||
// Restore the last map position/zoom from localStorage so a refresh of /maps doesn't snap back
|
||||
// to the default US-wide view. Bounds-checked so a corrupt or out-of-range value falls through
|
||||
// to the default instead of throwing.
|
||||
const getSavedMapView = (): SavedMapView | null => {
|
||||
try {
|
||||
const raw = localStorage.getItem(SAVED_MAP_VIEW_KEY)
|
||||
if (!raw) return null
|
||||
const parsed = JSON.parse(raw)
|
||||
if (
|
||||
parsed &&
|
||||
typeof parsed === 'object' &&
|
||||
Number.isFinite(parsed.longitude) &&
|
||||
Number.isFinite(parsed.latitude) &&
|
||||
Number.isFinite(parsed.zoom) &&
|
||||
parsed.latitude >= -90 &&
|
||||
parsed.latitude <= 90 &&
|
||||
parsed.longitude >= -180 &&
|
||||
parsed.longitude <= 180
|
||||
) {
|
||||
return { longitude: parsed.longitude, latitude: parsed.latitude, zoom: parsed.zoom }
|
||||
}
|
||||
} catch {
|
||||
// ignore — fall through to default
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export default function MapComponent({
|
||||
isHoveringUI,
|
||||
showCoordinatesEnabled,
|
||||
|
|
@ -47,6 +79,10 @@ export default function MapComponent({
|
|||
() => (localStorage.getItem('nomad:map-scale-unit') as ScaleUnit) || 'metric'
|
||||
)
|
||||
|
||||
// Resolve the initial view once at mount: saved view → default. Lazy so it isn't recomputed
|
||||
// on every render.
|
||||
const [initialViewState] = useState(() => getSavedMapView() ?? DEFAULT_MAP_VIEW)
|
||||
|
||||
const [cursorLngLat, setCursorLngLat] = useState<{
|
||||
lng: number
|
||||
lat: number
|
||||
|
|
@ -171,10 +207,18 @@ export default function MapComponent({
|
|||
cursor={isDraggingMap ? 'grabbing' : 'crosshair'}
|
||||
mapStyle={`${window.location.protocol}//${window.location.hostname}:${window.location.port}/api/maps/styles`}
|
||||
mapLib={maplibregl}
|
||||
initialViewState={{
|
||||
longitude: -101,
|
||||
latitude: 40,
|
||||
zoom: 3.5,
|
||||
initialViewState={initialViewState}
|
||||
onMoveEnd={(e) => {
|
||||
// Persist the view so a refresh restores where the user was, not the default.
|
||||
const { longitude, latitude, zoom } = e.viewState
|
||||
try {
|
||||
localStorage.setItem(
|
||||
SAVED_MAP_VIEW_KEY,
|
||||
JSON.stringify({ longitude, latitude, zoom })
|
||||
)
|
||||
} catch {
|
||||
// ignore persistence failures (private mode, quota)
|
||||
}
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
setIsDraggingMap(true)
|
||||
|
|
|
|||
Loading…
Reference in New Issue