fix: Add fallback to low-accuracy geolocation when GPS times out

- After 2 consecutive timeouts with high accuracy mode, automatically
  switch to low accuracy mode (WiFi/cell-based location)
- Increased maxAge from 5s to 30s to accept older cached positions
- Reduced timeout from 30s to 15s for faster fallback
- Low accuracy mode uses 60s maxAge and 60s timeout for reliability

This helps indoor users who can't get GPS signal.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-01-02 10:23:06 +01:00
parent 2284f94ee3
commit 007a7e877f
1 changed files with 50 additions and 3 deletions

View File

@ -38,8 +38,8 @@ export function useLocationSharing(
onLocationUpdate,
updateInterval = 5000,
highAccuracy = true,
maxAge = 5000, // Allow cached position up to 5 seconds old
timeout = 30000,
maxAge = 30000, // Allow cached position up to 30 seconds old
timeout = 15000, // 15 second timeout before fallback
} = options;
const [isSharing, setIsSharing] = useState(false);
@ -49,6 +49,8 @@ export function useLocationSharing(
const watchIdRef = useRef<number | null>(null);
const onLocationUpdateRef = useRef(onLocationUpdate);
const timeoutCountRef = useRef(0);
const usingLowAccuracyRef = useRef(false);
// Keep callback ref updated
useEffect(() => {
@ -73,6 +75,9 @@ export function useLocationSharing(
}, []);
const handlePosition = useCallback((position: GeolocationPosition) => {
// Reset timeout counter on successful position
timeoutCountRef.current = 0;
const location: ParticipantLocation = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
@ -90,12 +95,50 @@ export function useLocationSharing(
onLocationUpdateRef.current?.(location);
}, []);
// Restart watcher with low accuracy mode
const restartWithLowAccuracy = useCallback(() => {
if (watchIdRef.current !== null) {
navigator.geolocation.clearWatch(watchIdRef.current);
}
console.log('Switching to low accuracy mode for faster location');
usingLowAccuracyRef.current = true;
const geoOptions: PositionOptions = {
enableHighAccuracy: false,
maximumAge: 60000, // Accept positions up to 1 minute old
timeout: 60000, // Longer timeout for low accuracy
};
watchIdRef.current = navigator.geolocation.watchPosition(
handlePosition,
(err) => {
setError(err);
console.warn('Geolocation warning (low accuracy):', err.message, '(code:', err.code, ')');
},
geoOptions
);
}, [handlePosition]);
const handleError = useCallback((err: GeolocationPositionError) => {
setError(err);
// Count consecutive timeouts
if (err.code === 3) { // TIMEOUT
timeoutCountRef.current++;
console.warn('Geolocation timeout', timeoutCountRef.current, '/', 3);
// After 2 timeouts, switch to low accuracy mode
if (timeoutCountRef.current >= 2 && !usingLowAccuracyRef.current && highAccuracy) {
restartWithLowAccuracy();
return;
}
}
// Only log as warning, not error - timeouts are common indoors
console.warn('Geolocation warning:', err.message, '(code:', err.code, ')');
// Don't stop sharing on errors - keep trying
}, []);
}, [highAccuracy, restartWithLowAccuracy]);
const startSharing = useCallback(() => {
if (!('geolocation' in navigator)) {
@ -107,6 +150,10 @@ export function useLocationSharing(
return; // Already watching
}
// Reset fallback flags
timeoutCountRef.current = 0;
usingLowAccuracyRef.current = false;
const geoOptions: PositionOptions = {
enableHighAccuracy: highAccuracy,
maximumAge: maxAge,