Improve geolocation reliability: 3-stage fix with forgiving timeouts
Now that the native crash is fixed (Kotlin 2.1 alignment on the Android
side), the remaining issue was GPS "Location timed out" on a cold first
fix / indoors. Rework the native position acquisition into three
increasingly-forgiving stages that return the first success:
1. INSTANT — accept a cached/coarse fix up to 5 min old (resolves
immediately on a warm chip).
2. PRECISE — high-accuracy GPS with a generous 30s window so a cold
first-fix has room to complete.
3. COARSE FALLBACK — network/wifi fix with the longest window to
rescue the "GPS can't lock indoors" case.
Also bump the web-path timeout to 20s, and make the "Location timed out"
toast actionable on native (suggests stepping near a window / retrying).
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
71e5c5df1f
commit
9c7cd5ec4e
2 changed files with 41 additions and 17 deletions
|
|
@ -188,31 +188,53 @@ async function getNativePosition(native: CapGeolocationPlugin): Promise<GeoCoord
|
||||||
throw new GeolocationError('denied', 'Location permission denied.');
|
throw new GeolocationError('denied', 'Location permission denied.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// First try a precise fix. High accuracy can fail/time out indoors or
|
// Getting a location fix — especially a "cold start" the first time after
|
||||||
// with a weak GPS signal, which shows up as "unavailable" or "timeout".
|
// granting permission, or indoors — can be slow. A GPS chip can take 30s+
|
||||||
|
// to acquire its first lock. So we try three increasingly-forgiving stages
|
||||||
|
// and return the first one that succeeds. Each is guarded so a failure just
|
||||||
|
// moves on to the next stage rather than surfacing as an error.
|
||||||
|
|
||||||
|
// Stage 1 — INSTANT: accept any recently cached fix (up to 5 min old).
|
||||||
|
// enableHighAccuracy:false + short timeout means "give me a cached/coarse
|
||||||
|
// fix immediately if you have one". This resolves instantly on a warm chip.
|
||||||
|
try {
|
||||||
|
const pos = await safeCall(() =>
|
||||||
|
native.getCurrentPosition({
|
||||||
|
enableHighAccuracy: false,
|
||||||
|
timeout: 8000,
|
||||||
|
maximumAge: 300000, // accept a fix up to 5 minutes old
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
||||||
|
} catch (cachedErr) {
|
||||||
|
console.warn('[geolocation] cached/coarse fix unavailable, trying GPS:', cachedErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stage 2 — PRECISE: full high-accuracy GPS with a generous window so a
|
||||||
|
// cold first-fix has room to complete.
|
||||||
try {
|
try {
|
||||||
const pos = await safeCall(() =>
|
const pos = await safeCall(() =>
|
||||||
native.getCurrentPosition({
|
native.getCurrentPosition({
|
||||||
enableHighAccuracy: true,
|
enableHighAccuracy: true,
|
||||||
timeout: 10000,
|
timeout: 30000, // give a cold GPS lock plenty of time
|
||||||
maximumAge: 30000,
|
maximumAge: 60000,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
return { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
||||||
} catch (highAccErr) {
|
} catch (highAccErr) {
|
||||||
// Fall back to a coarse (network/cell/wifi) fix with a longer window.
|
|
||||||
// This rescues the common "GPS can't lock" case that otherwise looks
|
|
||||||
// like a hard failure.
|
|
||||||
console.warn('[geolocation] high-accuracy failed, trying coarse:', highAccErr);
|
console.warn('[geolocation] high-accuracy failed, trying coarse:', highAccErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stage 3 — COARSE FALLBACK: network/cell/wifi fix with the longest window.
|
||||||
|
// This rescues the common "GPS can't lock indoors" case.
|
||||||
const pos = await safeCall(() =>
|
const pos = await safeCall(() =>
|
||||||
native.getCurrentPosition({
|
native.getCurrentPosition({
|
||||||
enableHighAccuracy: false,
|
enableHighAccuracy: false,
|
||||||
timeout: 20000,
|
timeout: 30000,
|
||||||
maximumAge: 120000,
|
maximumAge: 300000,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
return { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof GeolocationError) throw err;
|
if (err instanceof GeolocationError) throw err;
|
||||||
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
||||||
|
|
@ -255,7 +277,7 @@ function getWebPosition(): Promise<GeoCoords> {
|
||||||
reject(new GeolocationError('unavailable', 'Location unavailable.'));
|
reject(new GeolocationError('unavailable', 'Location unavailable.'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 30000 },
|
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 60000 },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -656,7 +656,9 @@ export default function MapPage() {
|
||||||
setNearMeError('timeout');
|
setNearMeError('timeout');
|
||||||
toast({
|
toast({
|
||||||
title: 'Location timed out',
|
title: 'Location timed out',
|
||||||
description: 'Could not get your location in time. Check your signal and try again.',
|
description: isNativeGeolocation()
|
||||||
|
? "Couldn't get a GPS fix in time. This is common on the first try or indoors — step near a window or outside and tap Near Me again."
|
||||||
|
: 'Could not get your location in time. Check your signal and try again.',
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue