Skip to content
Merged
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
16 changes: 13 additions & 3 deletions src/components/map/near-me-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ export function NearMeButton({ onLocated, className }: NearMeButtonProps) {
setBusy(false);
onLocated({ lat: pos.coords.latitude, lng: pos.coords.longitude });
},
() => {
(err) => {
setBusy(false);
setError("Could not read your location.");
setError(
err.code === err.PERMISSION_DENIED
? "Allow location access in your browser settings."
: err.code === err.TIMEOUT
? "Location request timed out. Try again."
: "Your location could not be determined. Try again.",
);
},
{ enableHighAccuracy: true, timeout: 10000 },
);
Expand All @@ -47,7 +53,11 @@ export function NearMeButton({ onLocated, className }: NearMeButtonProps) {
<LocateFixed className="size-4" />
{busy ? "Locating..." : "Near me"}
</Button>
{error && <p className="mt-1 text-xs text-destructive">{error}</p>}
{error && (
<p className="mt-1 text-xs text-destructive" role="alert">
{error}
</p>
)}
</div>
);
}
54 changes: 36 additions & 18 deletions src/components/map/near-me-fab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,60 @@ interface NearMeFabProps {
/** Round floating "near me" button for the mobile map, sized for thumb reach. */
export function NearMeFab({ onLocated, className }: NearMeFabProps) {
const [busy, setBusy] = React.useState(false);
const [error, setError] = React.useState<string | null>(null);

function locate() {
if (!navigator.geolocation) {
toast.error("Geolocation is not available in this browser.");
const message = "Geolocation is not available in this browser.";
setError(message);
toast.error(message);
return;
}
setBusy(true);
setError(null);
navigator.geolocation.getCurrentPosition(
(pos) => {
setBusy(false);
onLocated({ lat: pos.coords.latitude, lng: pos.coords.longitude });
},
() => {
(err) => {
setBusy(false);
toast.error("Could not read your location.");
const message =
err.code === err.PERMISSION_DENIED
? "Allow location access in your browser settings."
: err.code === err.TIMEOUT
? "Location request timed out. Try again."
: "Your location could not be determined. Try again.";
setError(message);
toast.error(message);
},
{ enableHighAccuracy: true, timeout: 10000 },
);
}

return (
<button
type="button"
onClick={locate}
disabled={busy}
aria-label="Find places near me"
className={cn(
"flex size-12 items-center justify-center rounded-full border border-border bg-card text-foreground shadow-lg transition-colors hover:bg-muted active:bg-muted/80 disabled:opacity-70",
className,
<>
<button
type="button"
onClick={locate}
disabled={busy}
aria-label="Find places near me"
className={cn(
"flex size-12 items-center justify-center rounded-full border border-border bg-card text-foreground shadow-lg transition-colors hover:bg-muted active:bg-muted/80 disabled:opacity-70",
className,
)}
>
{busy ? (
<Loader2 className="size-5 animate-spin" />
) : (
<LocateFixed className="size-5" />
)}
</button>
{error && (
<span className="sr-only" role="alert">
{error}
</span>
)}
>
{busy ? (
<Loader2 className="size-5 animate-spin" />
) : (
<LocateFixed className="size-5" />
)}
</button>
</>
);
}
Loading