Geolocation: handle 'no provider' case (GrapheneOS / no Play Services)

Root cause of the persistent Near Me failure: the test device runs
GrapheneOS with no Google Play Services, so there's no network/fused
location provider — only raw GPS, which can't fix indoors. This was
never a code bug.

- Add 'no_provider' GeoErrorKind; detect it from native error text
  (disabled / provider / settings / unavailable).
- Show accurate, actionable messaging: tell the user to enable Network
  Location (Settings → Location) — the GrapheneOS-friendly fix — or use
  GPS outdoors, instead of a vague timeout.
- Refine the timeout copy along the same lines.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-07-25 00:32:03 +07:00
parent 8870a3e07f
commit a3dad2cbec
2 changed files with 31 additions and 5 deletions

View file

@ -46,7 +46,7 @@ export interface GeoCoords {
lng: number; lng: number;
} }
export type GeoErrorKind = 'denied' | 'unavailable' | 'timeout'; export type GeoErrorKind = 'denied' | 'unavailable' | 'timeout' | 'no_provider';
export class GeolocationError extends Error { export class GeolocationError extends Error {
kind: GeoErrorKind; kind: GeoErrorKind;
@ -319,12 +319,26 @@ async function getNativePosition(native: CapGeolocationPlugin): Promise<GeoCoord
if (msg.includes('denied') || msg.includes('permission')) { if (msg.includes('denied') || msg.includes('permission')) {
throw new GeolocationError('denied', 'Location permission denied.'); throw new GeolocationError('denied', 'Location permission denied.');
} }
// "location disabled" / "provider" → the OS has NO usable location provider.
// On GrapheneOS (no Google Play Services) there's often no network/fused
// provider, so only GPS satellites work — which fail indoors. Surface this
// as its own kind so the UI can give accurate advice.
if (
msg.includes('disabled') ||
msg.includes('provider') ||
msg.includes('settings') ||
msg.includes('unavailable')
) {
throw new GeolocationError(
'no_provider',
err instanceof Error ? err.message : String(err) || 'No location provider available.',
);
}
if (msg.includes('timeout') || msg.includes('timed out')) { if (msg.includes('timeout') || msg.includes('timed out')) {
throw new GeolocationError('timeout', 'Location request timed out.'); throw new GeolocationError('timeout', 'Location request timed out.');
} }
// "location disabled", "unavailable", plugin-not-implemented, etc. — pass // Anything else — pass the real native message through so it shows in the
// the real native message through so it shows in the on-screen toast and // on-screen toast and we can see what actually failed without needing adb.
// we can see what actually failed without needing adb.
throw new GeolocationError( throw new GeolocationError(
'unavailable', 'unavailable',
err instanceof Error ? err.message : String(err) || 'Location unavailable.', err instanceof Error ? err.message : String(err) || 'Location unavailable.',

View file

@ -667,11 +667,23 @@ export default function MapPage() {
title: 'Location timed out', title: 'Location timed out',
description: description:
(isNativeGeolocation() (isNativeGeolocation()
? "Couldn't get a location fix. Make sure Location/GPS is turned on in your phone's quick settings, then tap Near Me again." ? "Couldn't get a location fix in time. If you're indoors, GPS can't see satellites — enable Network Location (Settings → Location) or step outside, then try again."
: 'Could not get your location in time. Check your signal and try again.') + diag, : 'Could not get your location in time. Check your signal and try again.') + diag,
variant: 'destructive', variant: 'destructive',
duration: 12000, duration: 12000,
}); });
} else if (kind === 'no_provider') {
setNearMeError('unavailable');
toast({
title: 'No location provider',
description:
(isNativeGeolocation()
? "Your phone isn't providing a location. On privacy-focused ROMs (e.g. GrapheneOS) enable Network Location under Settings → Location, or make sure GPS is on and try outdoors."
: 'No location provider is available. Enable location services and try again.') +
diag,
variant: 'destructive',
duration: 14000,
});
} else { } else {
setNearMeError('unavailable'); setNearMeError('unavailable');
toast({ toast({