fix: search geocode now includes viewport bias in API requests

Symptom: HAR capture showed /api/geocode requests with NO lat/lon/zoom
params despite map being centered on Twin Falls. Results returned
out-of-state addresses (Illinois, Iowa, Arkansas).

Root cause: SearchBar subscribed to mapCenter via React hook, but the
value was stale at search time due to render timing.

Fix: api.js searchGeocode now reads mapCenter directly from the store
via useStore.getState() at call time. This is the correct pattern for
non-component code. SearchBar no longer passes mapCenter as a param.
This commit is contained in:
Matt 2026-04-26 23:38:31 +00:00
commit 60effce679
2 changed files with 16 additions and 6 deletions

View file

@ -1,3 +1,5 @@
import { useStore } from './store'
const GEOCODE_URL = '/api/geocode' const GEOCODE_URL = '/api/geocode'
const VALHALLA_URL = '/valhalla/route' const VALHALLA_URL = '/valhalla/route'
const VALHALLA_OPTIMIZED_URL = '/valhalla/optimized_route' const VALHALLA_OPTIMIZED_URL = '/valhalla/optimized_route'
@ -10,11 +12,19 @@ const VALHALLA_HEIGHT_URL = '/valhalla/height'
* @param {AbortSignal} signal * @param {AbortSignal} signal
* @returns {Promise<{query, results, count}>} * @returns {Promise<{query, results, count}>}
*/ */
export async function searchGeocode(query, limit = 6, signal, viewport = null) { export async function searchGeocode(query, limit = 6, signal) {
const params = new URLSearchParams({ q: query, limit: String(limit) }) const params = new URLSearchParams({ q: query, limit: String(limit) })
if (viewport?.lat != null) params.set('lat', String(viewport.lat)) // Read current mapCenter directly from store (non-reactive, correct for non-component code)
if (viewport?.lon != null) params.set('lon', String(viewport.lon)) const mapCenter = useStore.getState().mapCenter
if (viewport?.zoom != null) params.set('zoom', String(Math.round(viewport.zoom))) if (mapCenter?.lat != null && Number.isFinite(mapCenter.lat)) {
params.set('lat', String(mapCenter.lat))
}
if (mapCenter?.lon != null && Number.isFinite(mapCenter.lon)) {
params.set('lon', String(mapCenter.lon))
}
if (mapCenter?.zoom != null && Number.isFinite(mapCenter.zoom)) {
params.set('zoom', String(Math.round(mapCenter.zoom)))
}
const resp = await fetch(`${GEOCODE_URL}?${params}`, { signal, timeout: 5000 }) const resp = await fetch(`${GEOCODE_URL}?${params}`, { signal, timeout: 5000 })
if (!resp.ok) throw new Error(`Geocode error: ${resp.status}`) if (!resp.ok) throw new Error(`Geocode error: ${resp.status}`)
return resp.json() return resp.json()

View file

@ -53,7 +53,7 @@ const SearchBar = forwardRef(function SearchBar(_, ref) {
const setClickMarker = useStore((s) => s.setClickMarker) const setClickMarker = useStore((s) => s.setClickMarker)
const setEditingContact = useStore((s) => s.setEditingContact) const setEditingContact = useStore((s) => s.setEditingContact)
const clearPendingDestination = useStore((s) => s.clearPendingDestination) const clearPendingDestination = useStore((s) => s.clearPendingDestination)
const mapCenter = useStore((s) => s.mapCenter) // mapCenter now read directly in api.js
useEffect(() => { useEffect(() => {
inputRef.current?.focus() inputRef.current?.focus()
@ -100,7 +100,7 @@ const SearchBar = forwardRef(function SearchBar(_, ref) {
setSearchLoading(true) setSearchLoading(true)
try { try {
const data = await searchGeocode(q.trim(), 6, ctrl.signal, mapCenter) const data = await searchGeocode(q.trim(), 6, ctrl.signal)
const combined = [...contactResults, ...(data.results || [])] const combined = [...contactResults, ...(data.results || [])]
setResults(combined) setResults(combined)
setAutocompleteOpen(combined.length > 0) setAutocompleteOpen(combined.length > 0)