diff --git a/web/src/components/LeafletMap.tsx b/web/src/components/LeafletMap.tsx index e1facc54c..8d5f66a04 100644 --- a/web/src/components/LeafletMap.tsx +++ b/web/src/components/LeafletMap.tsx @@ -34,6 +34,7 @@ const LocationMarker = (props: MarkerProps) => { useEffect(() => { map.attributionControl.setPrefix(""); + map.attributionControl.addAttribution("OpenStreetMap"); map.locate(); }, []); diff --git a/web/src/components/MemoEditor/ActionButton/LocationSelector.tsx b/web/src/components/MemoEditor/ActionButton/LocationSelector.tsx index d101bb064..e7d015a3b 100644 --- a/web/src/components/MemoEditor/ActionButton/LocationSelector.tsx +++ b/web/src/components/MemoEditor/ActionButton/LocationSelector.tsx @@ -1,6 +1,6 @@ import { LatLng } from "leaflet"; import { MapPinIcon, XIcon } from "lucide-react"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import toast from "react-hot-toast"; import LeafletMap from "@/components/LeafletMap"; import { Button } from "@/components/ui/button"; @@ -21,6 +21,12 @@ interface State { position?: LatLng; } +interface NomatimRateLimit { + lastNominatimFetch: Date; + nominatimTimeoutId: number | undefined; + timeBetweenFetch: number; +} + const LocationSelector = (props: Props) => { const t = useTranslate(); const [state, setState] = useState({ @@ -28,6 +34,12 @@ const LocationSelector = (props: Props) => { placeholder: props.location?.placeholder || "", position: props.location ? new LatLng(props.location.latitude, props.location.longitude) : undefined, }); + const rateLimit = useRef({ + lastNominatimFetch: new Date(0), + nominatimTimeoutId: undefined, + timeBetweenFetch: 1300, + }); + const [popoverOpen, setPopoverOpen] = useState(false); useEffect(() => { @@ -63,14 +75,16 @@ const LocationSelector = (props: Props) => { } }, [popoverOpen]); - useEffect(() => { + const updateReverseGeocoding = () => { if (!state.position) { setState({ ...state, placeholder: "" }); return; } - // Fetch reverse geocoding data. - fetch(`https://nominatim.openstreetmap.org/reverse?lat=${state.position.lat}&lon=${state.position.lng}&format=json`) + fetch(`https://nominatim.openstreetmap.org/reverse?lat=${state.position.lat}&lon=${state.position.lng}&format=json`, { + cache: "default", + headers: new Headers({ "Cache-Control": "max-age=86400" }), + }) .then((response) => response.json()) .then((data) => { if (data && data.display_name) { @@ -81,6 +95,16 @@ const LocationSelector = (props: Props) => { toast.error("Failed to fetch reverse geocoding data"); console.error("Failed to fetch reverse geocoding data:", error); }); + } + + useEffect(() => { + // Fetch reverse geocoding with rate limits + clearTimeout(rateLimit.current.nominatimTimeoutId); + const timeLeft = rateLimit.current.timeBetweenFetch - (new Date().getTime() - rateLimit.current.lastNominatimFetch.getTime()); + rateLimit.current.nominatimTimeoutId = setTimeout(() => { + updateReverseGeocoding(); + rateLimit.current.lastNominatimFetch = new Date(); + }, timeLeft); }, [state.position]); const onPositionChanged = (position: LatLng) => {