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..3f5c0a47b 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 NominatimRateLimit {
+ 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();
+ }, Math.max(0, timeLeft));
}, [state.position]);
const onPositionChanged = (position: LatLng) => {