From 497a671504e5dc2b40ff6a60e061dfbd372ef99b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 29 Apr 2026 18:32:41 +0000 Subject: [PATCH] fix(place): add reverse geocode fallback for place details When a place is selected without osm_type/osm_id (e.g., clicking on a basemap label), trigger a reverse geocode to obtain the OSM identifiers. This enables fetching place details including boundary, wiki_summary, and other enriched data. The existing placeDetails useEffect will then fire once the osm_type/osm_id are available in the selectedPlace.raw object. Co-Authored-By: Claude --- src/components/PlaceCard.jsx | 28 +++++++++++++++++++++++++++- src/components/PlaceDetail.jsx | 27 ++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/components/PlaceCard.jsx b/src/components/PlaceCard.jsx index 4860a37..3447608 100644 --- a/src/components/PlaceCard.jsx +++ b/src/components/PlaceCard.jsx @@ -6,7 +6,7 @@ import { import OpeningHours from "opening_hours" import toast from "react-hot-toast" import { useStore } from "../store" -import { fetchElevation, fetchPlaceDetails, fetchPlaceByWikidata, fetchDriveTime, fetchNearbyContacts, fetchLandclass } from "../api" +import { fetchElevation, fetchPlaceDetails, fetchPlaceByWikidata, fetchDriveTime, fetchNearbyContacts, fetchLandclass, fetchReverse } from "../api" import { hasFeature } from "../config" import { buildAddress } from "../utils/place" @@ -328,6 +328,32 @@ export function PlaceCard({ place, variant = "preview", expanded = true, onToggl return () => { cancelled = true } }, [placeLat, placeLon]) + // Reverse geocode to get OSM type/id if not present (e.g., basemap label clicks) + useEffect(() => { + if (!hasFeature('has_nominatim_details')) return + if (osmType && osmId) return + if (placeLat == null || placeLon == null) return + // Skip for dropped pins - they get reverse geocoded by MapView + if (place?.source === 'map_click') return + + const controller = new AbortController() + fetchReverse(placeLat, placeLon).then((result) => { + if (controller.signal.aborted) return + if (result?.raw?.osm_type && result?.raw?.osm_id) { + const current = useStore.getState().selectedPlace + if (current && current.lat === placeLat && current.lon === placeLon) { + // Merge OSM data into raw, preserving existing data + useStore.getState().setSelectedPlace({ + ...current, + raw: { ...current.raw, osm_type: result.raw.osm_type, osm_id: result.raw.osm_id } + }) + } + } + }) + return () => controller.abort() + }, [placeLat, placeLon, osmType, osmId, place?.source]) + + useEffect(() => { if (!hasFeature("has_nominatim_details") || !osmType || !osmId) { setPlaceDetails(null); return } const controller = new AbortController() diff --git a/src/components/PlaceDetail.jsx b/src/components/PlaceDetail.jsx index 18ea48b..5ecc915 100644 --- a/src/components/PlaceDetail.jsx +++ b/src/components/PlaceDetail.jsx @@ -6,7 +6,7 @@ import { import OpeningHours from 'opening_hours' import toast from 'react-hot-toast' import { useStore } from '../store' -import { fetchElevation, fetchPlaceDetails, fetchPlaceByWikidata, fetchDriveTime, fetchNearbyContacts, fetchLandclass } from '../api' +import { fetchElevation, fetchPlaceDetails, fetchPlaceByWikidata, fetchDriveTime, fetchNearbyContacts, fetchLandclass, fetchReverse } from '../api' import { hasFeature } from '../config' import { buildAddress } from '../utils/place' @@ -514,6 +514,31 @@ export default function PlaceDetail() { return () => { cancelled = true } }, [placeLat, placeLon]) + // Reverse geocode to get OSM type/id if not present (e.g., basemap label clicks) + useEffect(() => { + if (!hasFeature('has_nominatim_details')) return + if (selectedPlace?.raw?.osm_type && selectedPlace?.raw?.osm_id) return + if (placeLat == null || placeLon == null) return + // Skip for dropped pins - they get reverse geocoded by MapView + if (selectedPlace?.source === 'map_click') return + + const controller = new AbortController() + fetchReverse(placeLat, placeLon).then((result) => { + if (controller.signal.aborted) return + if (result?.raw?.osm_type && result?.raw?.osm_id) { + const current = useStore.getState().selectedPlace + if (current && current.lat === placeLat && current.lon === placeLon) { + useStore.getState().setSelectedPlace({ + ...current, + raw: { ...current.raw, osm_type: result.raw.osm_type, osm_id: result.raw.osm_id } + }) + } + } + }) + return () => controller.abort() + }, [placeLat, placeLon, selectedPlace?.raw?.osm_type, selectedPlace?.raw?.osm_id, selectedPlace?.source]) + + // Fetch place details when place changes (if feature enabled) const osmType = selectedPlace?.raw?.osm_type const osmId = selectedPlace?.raw?.osm_id