Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 90 additions & 3 deletions nexa/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion nexa/src/app/report/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ export default function ReportPage() {
onDescriptionChange={setDescription}
onAddressChange={handleAddressChange}
onDetectLocation={geo.detect}
onLocationChange={geo.setCoordinates}
onLocationChange={geo.movePin}
onClassify={handleClassify}
/>
)}
Expand Down
42 changes: 29 additions & 13 deletions nexa/src/hooks/use-geolocation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { useState, useCallback } from "react";
import { useState, useCallback, useRef } from "react";
import { reverseGeocode } from "@/lib/reverse-geocode";

export function useGeolocation() {
const [address, setAddress] = useState("");
Expand All @@ -10,6 +11,10 @@ export function useGeolocation() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

// Monotonic counter so a slow response from an earlier drag never overwrites
// the result of a more recent one.
const geocodeSeq = useRef(0);

const setCoordinates = useCallback(
(lat: number | null, lng: number | null) => {
setLatitude(lat);
Expand All @@ -18,6 +23,16 @@ export function useGeolocation() {
[],
);

/** Shared reverse-geocode: updates address to match lat/lng. */
const refreshAddress = useCallback(async (lat: number, lng: number) => {
const seq = ++geocodeSeq.current;
const name = await reverseGeocode(lat, lng);
if (seq === geocodeSeq.current) {
setAddress(name);
}
}, []);

/** Browser GPS → set coords + reverse-geocode address. */
const detect = useCallback(() => {
if (!navigator.geolocation) {
setError("Geolocation is not supported by this browser.");
Expand All @@ -32,17 +47,7 @@ export function useGeolocation() {
const lng = position.coords.longitude;
setCoordinates(lat, lng);
setAccuracy(position.coords.accuracy);

try {
const res = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`,
);
const data = await res.json();
if (data.display_name) setAddress(data.display_name);
} catch {
setAddress(`${lat.toFixed(6)}, ${lng.toFixed(6)}`);
}

await refreshAddress(lat, lng);
setLoading(false);
},
(err) => {
Expand All @@ -65,9 +70,19 @@ export function useGeolocation() {
maximumAge: 60000,
},
);
}, [setCoordinates]);
}, [setCoordinates, refreshAddress]);

/** Pin drag → update coords + reverse-geocode to keep address in sync. */
const movePin = useCallback(
(lat: number, lng: number) => {
setCoordinates(lat, lng);
void refreshAddress(lat, lng);
},
[setCoordinates, refreshAddress],
);

const reset = useCallback(() => {
geocodeSeq.current += 1;
setAddress("");
setCoordinates(null, null);
setAccuracy(null);
Expand All @@ -83,6 +98,7 @@ export function useGeolocation() {
loading,
error,
setCoordinates,
movePin,
detect,
reset,
};
Expand Down
20 changes: 20 additions & 0 deletions nexa/src/lib/reverse-geocode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Reverse-geocode coordinates via Nominatim (same source as Detect Location).
* Returns a display-name string, or a coordinate fallback on failure.
*/
export async function reverseGeocode(
lat: number,
lng: number,
): Promise<string> {
const fallback = `${lat.toFixed(6)}, ${lng.toFixed(6)}`;
try {
const res = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`,
);
if (!res.ok) return fallback;
const data = (await res.json()) as { display_name?: string };
return data.display_name?.trim() || fallback;
} catch {
return fallback;
}
}
Loading