fix(ux): Batch C part 1 — Near Me timing & reachability
- geolocation: cap the overall deadline at 15s (was 25s) and shorten the native plugin attempt to 8s, so a failed fix no longer hangs ~25-40s - MapPage: only warm the location cache when permission is ALREADY granted, so opening /map no longer fires the browser's 'Allow location?' prompt - MapPage: add a desktop Near Me control (it was mobile-only) - MapPage: on native, a denial is now retryable instead of permanently opening the browser-only help sheet
This commit is contained in:
parent
8e35859188
commit
dca0c0ede1
2 changed files with 57 additions and 10 deletions
|
|
@ -228,7 +228,7 @@ async function getNativePosition(native: CapGeolocationPlugin): Promise<GeoCoord
|
||||||
// it defensively and fall back to navigator.geolocation only if it fails.
|
// it defensively and fall back to navigator.geolocation only if it fails.
|
||||||
try {
|
try {
|
||||||
const pos = await safeCall(() =>
|
const pos = await safeCall(() =>
|
||||||
native.getCurrentPosition({ enableHighAccuracy: false, timeout: 15000, maximumAge: 60000 }),
|
native.getCurrentPosition({ enableHighAccuracy: false, timeout: 8000, maximumAge: 60000 }),
|
||||||
);
|
);
|
||||||
const lat = pos?.coords?.latitude;
|
const lat = pos?.coords?.latitude;
|
||||||
const lng = pos?.coords?.longitude;
|
const lng = pos?.coords?.longitude;
|
||||||
|
|
@ -340,7 +340,7 @@ function getWebPosition(): Promise<GeoCoords> {
|
||||||
settled = true;
|
settled = true;
|
||||||
cleanup();
|
cleanup();
|
||||||
reject(new GeolocationError('timeout', 'Location request timed out.'));
|
reject(new GeolocationError('timeout', 'Location request timed out.'));
|
||||||
}, 25000);
|
}, 15000);
|
||||||
|
|
||||||
// 1) watchPosition — first update wins (fast network fix).
|
// 1) watchPosition — first update wins (fast network fix).
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -804,13 +804,29 @@ export default function MapPage() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isNativeGeolocation()) return;
|
if (isNativeGeolocation()) return;
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
getCurrentPosition()
|
|
||||||
.then(({ lat, lng }) => {
|
const warm = () =>
|
||||||
if (!cancelled) setUserLocation({ lat, lon: lng });
|
getCurrentPosition()
|
||||||
})
|
.then(({ lat, lng }) => {
|
||||||
.catch(() => {
|
if (!cancelled) setUserLocation({ lat, lon: lng });
|
||||||
// Silent — user hasn't asked to be located yet.
|
})
|
||||||
});
|
.catch(() => {
|
||||||
|
// Silent — user hasn't asked to be located yet.
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only warm up when permission is ALREADY granted — otherwise this fires the
|
||||||
|
// browser's "Allow location?" prompt the moment someone opens /map.
|
||||||
|
if (typeof navigator !== 'undefined' && navigator.permissions?.query) {
|
||||||
|
navigator.permissions
|
||||||
|
.query({ name: 'geolocation' as PermissionName })
|
||||||
|
.then((status) => {
|
||||||
|
if (!cancelled && status.state === 'granted') warm();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
/* permissions API unavailable — don't prompt */
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
|
|
@ -1432,6 +1448,29 @@ export default function MapPage() {
|
||||||
compact
|
compact
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Near Me — desktop parity (mobile has the floating control) */}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
if (nearMeError === 'denied' && !isNativeGeolocation()) {
|
||||||
|
setShowLocationHelp(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleNearMe();
|
||||||
|
}}
|
||||||
|
disabled={nearMeLoading}
|
||||||
|
title={nearMeError === 'denied' ? 'Enable location access' : 'Find my location'}
|
||||||
|
className="hidden md:inline-flex h-8 gap-1.5 border-white/20 flex-shrink-0 touch-manipulation"
|
||||||
|
>
|
||||||
|
{nearMeError === 'denied' ? (
|
||||||
|
<LocateOff className="h-4 w-4 text-red-400" />
|
||||||
|
) : (
|
||||||
|
<LocateFixed className={cn('h-4 w-4', userLocation ? 'text-[#ccff00]' : 'text-gray-300')} />
|
||||||
|
)}
|
||||||
|
<span className="hidden lg:inline">Near Me</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
{/* Clear filters */}
|
{/* Clear filters */}
|
||||||
{hasActiveFilters && (
|
{hasActiveFilters && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -1723,7 +1762,15 @@ export default function MapPage() {
|
||||||
>
|
>
|
||||||
{/* Near Me */}
|
{/* Near Me */}
|
||||||
<button
|
<button
|
||||||
onClick={nearMeError === 'denied' ? () => setShowLocationHelp(true) : handleNearMe}
|
onClick={() => {
|
||||||
|
// On native a denial is an OS permission the user can grant
|
||||||
|
// and retry; only the web needs the browser re-enable sheet.
|
||||||
|
if (nearMeError === 'denied' && !isNativeGeolocation()) {
|
||||||
|
setShowLocationHelp(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleNearMe();
|
||||||
|
}}
|
||||||
disabled={nearMeLoading}
|
disabled={nearMeLoading}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-11 w-11 rounded-full flex items-center justify-center shadow-lg border backdrop-blur transition-all touch-manipulation active:scale-90',
|
'h-11 w-11 rounded-full flex items-center justify-center shadow-lg border backdrop-blur transition-all touch-manipulation active:scale-90',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue