diff --git a/web/src/components/LeafletMap.tsx b/web/src/components/LeafletMap.tsx index e1facc54c..f71122ce4 100644 --- a/web/src/components/LeafletMap.tsx +++ b/web/src/components/LeafletMap.tsx @@ -37,6 +37,16 @@ const LocationMarker = (props: MarkerProps) => { map.locate(); }, []); + // Keep marker and map in sync with external position updates + useEffect(() => { + if (props.position) { + setPosition(props.position); + map.setView(props.position); + } else { + setPosition(undefined); + } + }, [props.position, map]); + return position === undefined ? null : ; }; diff --git a/web/src/components/MemoEditor/ActionButton/LocationSelector.tsx b/web/src/components/MemoEditor/ActionButton/LocationSelector.tsx index d101bb064..1d1ebf873 100644 --- a/web/src/components/MemoEditor/ActionButton/LocationSelector.tsx +++ b/web/src/components/MemoEditor/ActionButton/LocationSelector.tsx @@ -16,17 +16,21 @@ interface Props { } interface State { - initilized: boolean; + initialized: boolean; placeholder: string; position?: LatLng; + latInput: string; + lngInput: string; } const LocationSelector = (props: Props) => { const t = useTranslate(); const [state, setState] = useState({ - initilized: false, + initialized: false, placeholder: props.location?.placeholder || "", position: props.location ? new LatLng(props.location.latitude, props.location.longitude) : undefined, + latInput: props.location ? String(props.location.latitude) : "", + lngInput: props.location ? String(props.location.longitude) : "", }); const [popoverOpen, setPopoverOpen] = useState(false); @@ -35,13 +39,15 @@ const LocationSelector = (props: Props) => { ...state, placeholder: props.location?.placeholder || "", position: new LatLng(props.location?.latitude || 0, props.location?.longitude || 0), + latInput: String(props.location?.latitude) || "", + lngInput: String(props.location?.longitude) || "", })); }, [props.location]); useEffect(() => { if (popoverOpen && !props.location) { const handleError = (error: any, errorMessage: string) => { - setState({ ...state, initilized: true }); + setState({ ...state, initialized: true }); toast.error(errorMessage); console.error(error); }; @@ -51,7 +57,7 @@ const LocationSelector = (props: Props) => { (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; - setState({ ...state, position: new LatLng(lat, lng), initilized: true }); + setState({ ...state, position: new LatLng(lat, lng), initialized: true }); }, (error) => { handleError(error, "Failed to get current position"); @@ -65,16 +71,23 @@ const LocationSelector = (props: Props) => { useEffect(() => { if (!state.position) { - setState({ ...state, placeholder: "" }); + setState((prev) => ({ ...prev, placeholder: "" })); return; } + // Sync lat/lng input values from position + const newLat = String(state.position.lat); + const newLng = String(state.position.lng); + if (state.latInput !== newLat || state.lngInput !== newLng) { + setState((prev) => ({ ...prev, latInput: newLat, lngInput: newLng })); + } + // Fetch reverse geocoding data. fetch(`https://nominatim.openstreetmap.org/reverse?lat=${state.position.lat}&lon=${state.position.lng}&format=json`) .then((response) => response.json()) .then((data) => { if (data && data.display_name) { - setState({ ...state, placeholder: data.display_name }); + setState((prev) => ({ ...prev, placeholder: data.display_name })); } }) .catch((error) => { @@ -83,8 +96,19 @@ const LocationSelector = (props: Props) => { }); }, [state.position]); + // Update position when lat/lng inputs change (if valid numbers) + useEffect(() => { + const lat = parseFloat(state.latInput); + const lng = parseFloat(state.lngInput); + if (Number.isFinite(lat) && Number.isFinite(lng)) { + if (!state.position || state.position.lat !== lat || state.position.lng !== lng) { + setState((prev) => ({ ...prev, position: new LatLng(lat, lng) })); + } + } + }, [state.latInput, state.lngInput]); + const onPositionChanged = (position: LatLng) => { - setState({ ...state, position }); + setState((prev) => ({ ...prev, position })); }; const removeLocation = (e: React.MouseEvent) => { @@ -123,22 +147,34 @@ const LocationSelector = (props: Props) => { - + - - {state.position && ( - - [{state.position.lat.toFixed(2)}, {state.position.lng.toFixed(2)}] - - )} + + setState((prev) => ({ ...prev, latInput: e.target.value }))} + className="w-28" + /> setState((state) => ({ ...state, placeholder: e.target.value }))} + placeholder="Lng" + type="number" + step="any" + value={state.lngInput} + onChange={(e) => setState((prev) => ({ ...prev, lngInput: e.target.value }))} + className="w-28" /> + + setState((prev) => ({ ...prev, placeholder: e.target.value }))} + /> +