File size: 3,241 Bytes
5c876be | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | /**
* Device location state store.
*
* Tracks the user's current GPS location and its human-readable address.
* Updated periodically by a foreground location service.
*/
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
// βββ State Shape βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
interface LocationState {
/** Current GPS coordinates (null when location is unavailable) */
currentLocation: { lat: number; lng: number } | null;
/** Reverse-geocoded address string for the current location */
locationAddress: string | null;
/** Most recent location error, if any */
locationError: string | null;
/** Whether location services are currently tracking */
isTracking: boolean;
}
// βββ Actions βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
interface LocationActions {
/** Update the current GPS coordinates */
setLocation: (lat: number, lng: number) => void;
/** Update the reverse-geocoded address */
setAddress: (address: string | null) => void;
/** Set a location error */
setError: (error: string | null) => void;
/** Clear all location state (e.g., on logout) */
clearLocation: () => void;
/** Toggle the tracking flag */
setTracking: (value: boolean) => void;
}
// βββ Store βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
export const useLocationStore = create<LocationState & LocationActions>()(
immer((set) => ({
// Initial state
currentLocation: null,
locationAddress: null,
locationError: null,
isTracking: false,
// Actions
setLocation: (lat, lng) =>
set((state) => {
state.currentLocation = { lat, lng };
state.locationError = null;
}),
setAddress: (address) =>
set((state) => {
state.locationAddress = address;
}),
setError: (error) =>
set((state) => {
state.locationError = error;
}),
clearLocation: () =>
set((state) => {
state.currentLocation = null;
state.locationAddress = null;
state.locationError = null;
state.isTracking = false;
}),
setTracking: (value) =>
set((state) => {
state.isTracking = value;
}),
})),
);
// βββ Selectors βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
export const selectHasLocation = (state: LocationState) =>
state.currentLocation !== null;
export const selectFormattedLocation = (state: LocationState) => {
if (!state.currentLocation) return null;
const { lat, lng } = state.currentLocation;
return {
lat,
lng,
address: state.locationAddress ?? `${lat.toFixed(6)}, ${lng.toFixed(6)}`,
};
};
|