- fix driver_locations schema mismatch: use `relation` instead of `driver` per live PB schema - remove invalid `ready_for_collection` filter that caused silent 400 errors - add order_item_attachments support for driver pickup photos with proper error handling: * replace legacy collection_photo field with new collection for multiple per-item files * upload photos after updating item status to avoid stranding assigned items * show warning toast if upload fails instead of success confirmation * update toast messages to reflect photo upload status - add pre-build schema check script to catch invalid PB field references pre-deploy - add corresponding npm scripts and prebuild hook for the schema check - add e2e test scripts for collection and upload flows - update type definitions and pocketbase SDK helpers for new schema and functionality
101 lines
3.5 KiB
TypeScript
101 lines
3.5 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
import { useAuth } from '@/components/AuthProvider';
|
|
import { getSettings, saveDriverLocation, isAuthenticated } from '@/lib/pocketbase';
|
|
|
|
export default function LocationTracker() {
|
|
const { isLoggedIn } = useAuth();
|
|
const watchRef = useRef<number | null>(null);
|
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
const wakeLockRef = useRef<WakeLockSentinel | null>(null);
|
|
|
|
useEffect(() => {
|
|
// Skip entirely when not signed in — previously the watcher
|
|
// was set up regardless of auth, which meant we requested a
|
|
// GPS lock on the login screen (and ate battery doing nothing).
|
|
if (!isLoggedIn) return;
|
|
if (!('geolocation' in navigator)) return;
|
|
|
|
const settings = getSettings();
|
|
const intervalMs = Math.max(30000, settings.locationInterval * 1000);
|
|
|
|
const sendLocation = (position: GeolocationPosition) => {
|
|
if (!isAuthenticated()) return;
|
|
if (position.coords.accuracy > settings.minimumAccuracy) return;
|
|
|
|
const auth = JSON.parse(localStorage.getItem('pocketbase_auth') || '{}');
|
|
const user = auth?.model;
|
|
if (!user) return;
|
|
|
|
saveDriverLocation({
|
|
// PB schema note: driver_locations stores the user relation under
|
|
// `relation`, not `driver`. Using the wrong key here silently
|
|
// 400'd inside a try/catch — see lib/types.ts DriverLocation.
|
|
relation: user.id,
|
|
latitude: position.coords.latitude,
|
|
longitude: position.coords.longitude,
|
|
accuracy: position.coords.accuracy || undefined,
|
|
altitude: position.coords.altitude || undefined,
|
|
speed: position.coords.speed || undefined,
|
|
heading: position.coords.heading || undefined,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
};
|
|
|
|
// 1. watchPosition keeps GPS active (helps Android stay alive longer)
|
|
watchRef.current = navigator.geolocation.watchPosition(
|
|
sendLocation,
|
|
() => {},
|
|
{
|
|
enableHighAccuracy: settings.enableHighAccuracy,
|
|
maximumAge: 5000, // fresh GPS every 5s
|
|
timeout: 30000,
|
|
}
|
|
);
|
|
|
|
// 2. Interval fallback (for when watchPosition is throttled)
|
|
intervalRef.current = setInterval(() => {
|
|
navigator.geolocation.getCurrentPosition(
|
|
sendLocation,
|
|
() => {},
|
|
{ enableHighAccuracy: settings.enableHighAccuracy, maximumAge: intervalMs, timeout: 30000 }
|
|
);
|
|
}, intervalMs);
|
|
|
|
// 3. Screen Wake Lock — keeps phone awake while app is open
|
|
if ('wakeLock' in navigator) {
|
|
navigator.wakeLock.request('screen')
|
|
.then((lock) => { wakeLockRef.current = lock; })
|
|
.catch(() => {});
|
|
// Re-acquire if page regains focus
|
|
const onFocus = () => {
|
|
if (!wakeLockRef.current) {
|
|
navigator.wakeLock.request('screen')
|
|
.then((lock) => { wakeLockRef.current = lock; })
|
|
.catch(() => {});
|
|
}
|
|
};
|
|
document.addEventListener('visibilitychange', () => {
|
|
if (document.visibilityState === 'visible') onFocus();
|
|
});
|
|
}
|
|
|
|
return () => {
|
|
if (watchRef.current !== null) {
|
|
navigator.geolocation.clearWatch(watchRef.current);
|
|
watchRef.current = null;
|
|
}
|
|
if (intervalRef.current !== null) {
|
|
clearInterval(intervalRef.current);
|
|
intervalRef.current = null;
|
|
}
|
|
if (wakeLockRef.current) {
|
|
wakeLockRef.current.release();
|
|
wakeLockRef.current = null;
|
|
}
|
|
};
|
|
}, [isLoggedIn]);
|
|
|
|
return null;
|
|
}
|