@umituz/react-native-location 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -7
- package/src/domain/entities/Location.ts +16 -168
- package/src/index.ts +3 -107
- package/src/infrastructure/services/LocationService.ts +76 -286
- package/src/presentation/hooks/useLocation.ts +38 -221
- package/LICENSE +0 -22
- package/README.md +0 -127
- package/lib/domain/entities/Location.d.ts +0 -144
- package/lib/domain/entities/Location.d.ts.map +0 -1
- package/lib/domain/entities/Location.js +0 -52
- package/lib/domain/entities/Location.js.map +0 -1
- package/lib/index.d.ts +0 -95
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -95
- package/lib/index.js.map +0 -1
- package/lib/infrastructure/services/LocationService.d.ts +0 -93
- package/lib/infrastructure/services/LocationService.d.ts.map +0 -1
- package/lib/infrastructure/services/LocationService.js +0 -250
- package/lib/infrastructure/services/LocationService.js.map +0 -1
- package/lib/presentation/hooks/useLocation.d.ts +0 -95
- package/lib/presentation/hooks/useLocation.d.ts.map +0 -1
- package/lib/presentation/hooks/useLocation.js +0 -156
- package/lib/presentation/hooks/useLocation.js.map +0 -1
|
@@ -1,298 +1,88 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
* USAGE:
|
|
20
|
-
* ```typescript
|
|
21
|
-
* import { locationService } from '@umituz/react-native-location';
|
|
22
|
-
*
|
|
23
|
-
* // Check permission
|
|
24
|
-
* const hasPermission = await locationService.hasPermissions();
|
|
25
|
-
*
|
|
26
|
-
* // Request permission if needed
|
|
27
|
-
* if (!hasPermission) {
|
|
28
|
-
* const granted = await locationService.requestPermissions();
|
|
29
|
-
* }
|
|
30
|
-
*
|
|
31
|
-
* // Get current location
|
|
32
|
-
* const location = await locationService.getCurrentLocation();
|
|
33
|
-
* if (location) {
|
|
34
|
-
* console.log(location.latitude, location.longitude);
|
|
35
|
-
* console.log(locationService.formatLocation(location));
|
|
36
|
-
* }
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
import { Platform } from 'react-native';
|
|
41
|
-
import * as Location from 'expo-location';
|
|
42
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
43
|
-
|
|
44
|
-
import type {
|
|
45
|
-
LocationData,
|
|
46
|
-
LocationPermissionStatus,
|
|
47
|
-
CachedLocation,
|
|
48
|
-
ILocationService,
|
|
49
|
-
} from '../../domain/entities/Location';
|
|
50
|
-
import { LOCATION_CONSTANTS } from '../../domain/entities/Location';
|
|
51
|
-
|
|
52
|
-
class LocationService implements ILocationService {
|
|
53
|
-
private cachedLocation: LocationData | null = null;
|
|
54
|
-
private cachedAt: number = 0;
|
|
55
|
-
private permissionStatus: LocationPermissionStatus = 'unknown';
|
|
56
|
-
|
|
57
|
-
constructor() {
|
|
58
|
-
this.loadCachedLocation();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Check if location services are available on this platform
|
|
63
|
-
*/
|
|
64
|
-
isLocationAvailable(): boolean {
|
|
65
|
-
// Location services not available on web
|
|
66
|
-
if (Platform.OS === 'web') {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Load cached location from AsyncStorage
|
|
74
|
-
*/
|
|
75
|
-
private async loadCachedLocation(): Promise<void> {
|
|
76
|
-
try {
|
|
77
|
-
const cached = await AsyncStorage.getItem(LOCATION_CONSTANTS.CACHE_KEY);
|
|
78
|
-
if (cached) {
|
|
79
|
-
const parsed: CachedLocation = JSON.parse(cached);
|
|
80
|
-
if (Date.now() - parsed.cachedAt < LOCATION_CONSTANTS.CACHE_DURATION) {
|
|
81
|
-
this.cachedLocation = parsed.data;
|
|
82
|
-
this.cachedAt = parsed.cachedAt;
|
|
83
|
-
} else {
|
|
84
|
-
// Expired cache, remove it
|
|
85
|
-
await AsyncStorage.removeItem(LOCATION_CONSTANTS.CACHE_KEY);
|
|
1
|
+
import * as Location from "expo-location";
|
|
2
|
+
import { LocationData, LocationError } from "../../domain/entities/Location";
|
|
3
|
+
|
|
4
|
+
export class LocationService {
|
|
5
|
+
/**
|
|
6
|
+
* Requests foreground location permissions.
|
|
7
|
+
* @returns boolean indicating if permission was granted.
|
|
8
|
+
*/
|
|
9
|
+
async requestPermissions(): Promise<boolean> {
|
|
10
|
+
try {
|
|
11
|
+
console.log("[LocationService] Requesting permissions...");
|
|
12
|
+
const { status } = await Location.requestForegroundPermissionsAsync();
|
|
13
|
+
console.log("[LocationService] Permission status:", status);
|
|
14
|
+
return status === "granted";
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error("[LocationService] Error requesting permissions:", error);
|
|
17
|
+
return false;
|
|
86
18
|
}
|
|
87
|
-
}
|
|
88
|
-
} catch (error) {
|
|
89
|
-
// Silent failure on cache load
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Save location to cache (AsyncStorage + memory)
|
|
95
|
-
*/
|
|
96
|
-
private async saveCachedLocation(location: LocationData): Promise<void> {
|
|
97
|
-
try {
|
|
98
|
-
const cached: CachedLocation = {
|
|
99
|
-
data: location,
|
|
100
|
-
cachedAt: Date.now(),
|
|
101
|
-
};
|
|
102
|
-
this.cachedLocation = location;
|
|
103
|
-
this.cachedAt = cached.cachedAt;
|
|
104
|
-
await AsyncStorage.setItem(
|
|
105
|
-
LOCATION_CONSTANTS.CACHE_KEY,
|
|
106
|
-
JSON.stringify(cached)
|
|
107
|
-
);
|
|
108
|
-
} catch (error) {
|
|
109
|
-
// Silent failure on cache save
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get cached location if available and not expired
|
|
115
|
-
*/
|
|
116
|
-
getCachedLocation(): LocationData | null {
|
|
117
|
-
if (!this.cachedLocation) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
const age = Date.now() - this.cachedAt;
|
|
121
|
-
if (age < LOCATION_CONSTANTS.CACHE_DURATION) {
|
|
122
|
-
return { ...this.cachedLocation, isCached: true };
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Request location permissions from user
|
|
129
|
-
*/
|
|
130
|
-
async requestPermissions(): Promise<boolean> {
|
|
131
|
-
if (!this.isLocationAvailable()) {
|
|
132
|
-
this.permissionStatus = 'denied';
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
const { status } = await Location.requestForegroundPermissionsAsync();
|
|
138
|
-
this.permissionStatus = status === 'granted' ? 'granted' : 'denied';
|
|
139
|
-
return status === 'granted';
|
|
140
|
-
} catch (error) {
|
|
141
|
-
this.permissionStatus = 'denied';
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Check if location permissions are granted
|
|
148
|
-
*/
|
|
149
|
-
async hasPermissions(): Promise<boolean> {
|
|
150
|
-
if (!this.isLocationAvailable()) {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Use cached permission status if available
|
|
155
|
-
if (this.permissionStatus !== 'unknown') {
|
|
156
|
-
return this.permissionStatus === 'granted';
|
|
157
19
|
}
|
|
158
20
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Get permission status without requesting
|
|
171
|
-
*/
|
|
172
|
-
getPermissionStatus(): LocationPermissionStatus {
|
|
173
|
-
return this.permissionStatus;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Get current location with fallback strategies
|
|
178
|
-
* Priority: High accuracy → Balanced accuracy → Cached location → null
|
|
179
|
-
*/
|
|
180
|
-
async getCurrentLocation(): Promise<LocationData | null> {
|
|
181
|
-
if (!this.isLocationAvailable()) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
const hasPermission = await this.hasPermissions();
|
|
187
|
-
|
|
188
|
-
if (!hasPermission) {
|
|
189
|
-
const granted = await this.requestPermissions();
|
|
190
|
-
if (!granted) {
|
|
191
|
-
// Return cached location if available
|
|
192
|
-
return this.getCachedLocation();
|
|
21
|
+
/**
|
|
22
|
+
* Gets the current position of the device.
|
|
23
|
+
* @param withAddress If true, also performs reverse geocoding.
|
|
24
|
+
*/
|
|
25
|
+
async getCurrentPosition(withAddress: boolean = false): Promise<LocationData> {
|
|
26
|
+
console.log("[LocationService] getCurrentPosition called");
|
|
27
|
+
const hasPermission = await this.requestPermissions();
|
|
28
|
+
if (!hasPermission) {
|
|
29
|
+
console.warn("[LocationService] Permission denied");
|
|
30
|
+
throw { code: "PERMISSION_DENIED", message: "Location permission not granted" };
|
|
193
31
|
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Try high accuracy first
|
|
197
|
-
let locationData = await this.getLocationWithAccuracy(
|
|
198
|
-
Location.Accuracy.High,
|
|
199
|
-
LOCATION_CONSTANTS.HIGH_ACCURACY_TIMEOUT
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
// Fallback to balanced accuracy if high accuracy fails
|
|
203
|
-
if (!locationData) {
|
|
204
|
-
locationData = await this.getLocationWithAccuracy(
|
|
205
|
-
Location.Accuracy.Balanced,
|
|
206
|
-
LOCATION_CONSTANTS.BALANCED_ACCURACY_TIMEOUT
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Fallback to cached location
|
|
211
|
-
if (!locationData) {
|
|
212
|
-
return this.getCachedLocation();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Cache the new location
|
|
216
|
-
await this.saveCachedLocation(locationData);
|
|
217
32
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
accuracy: location.coords.accuracy ?? undefined,
|
|
249
|
-
isCached: false,
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
// Try to get address (reverse geocoding)
|
|
253
|
-
try {
|
|
254
|
-
const addresses = await Location.reverseGeocodeAsync({
|
|
255
|
-
latitude: location.coords.latitude,
|
|
256
|
-
longitude: location.coords.longitude,
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
if (addresses && addresses.length > 0) {
|
|
260
|
-
const address = addresses[0];
|
|
261
|
-
const addressParts = [address.street, address.city, address.region].filter(
|
|
262
|
-
Boolean
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
locationData.address =
|
|
266
|
-
addressParts.join(', ') || 'Unknown location';
|
|
33
|
+
try {
|
|
34
|
+
console.log("[LocationService] getting position async...");
|
|
35
|
+
const location = await Location.getCurrentPositionAsync({
|
|
36
|
+
accuracy: Location.Accuracy.Balanced,
|
|
37
|
+
});
|
|
38
|
+
console.log("[LocationService] position obtained", location);
|
|
39
|
+
|
|
40
|
+
let addressData;
|
|
41
|
+
if (withAddress) {
|
|
42
|
+
console.log("[LocationService] reverse geocoding...");
|
|
43
|
+
addressData = await this.reverseGeocode(
|
|
44
|
+
location.coords.latitude,
|
|
45
|
+
location.coords.longitude
|
|
46
|
+
);
|
|
47
|
+
console.log("[LocationService] address obtained", addressData);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
coords: {
|
|
52
|
+
latitude: location.coords.latitude,
|
|
53
|
+
longitude: location.coords.longitude,
|
|
54
|
+
},
|
|
55
|
+
timestamp: location.timestamp,
|
|
56
|
+
address: addressData,
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("[LocationService] Error getting location:", error);
|
|
60
|
+
// Type guard for error object
|
|
61
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error getting location";
|
|
62
|
+
throw { code: "LOCATION_ERROR", message: errorMessage };
|
|
267
63
|
}
|
|
268
|
-
} catch (error) {
|
|
269
|
-
// Fallback to coordinates if geocoding fails
|
|
270
|
-
locationData.address = `${location.coords.latitude.toFixed(
|
|
271
|
-
LOCATION_CONSTANTS.COORDINATE_PRECISION
|
|
272
|
-
)}, ${location.coords.longitude.toFixed(LOCATION_CONSTANTS.COORDINATE_PRECISION)}`;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return locationData;
|
|
276
|
-
} catch (error) {
|
|
277
|
-
return null;
|
|
278
64
|
}
|
|
279
|
-
}
|
|
280
65
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Reverse geocodes coordinates to an address.
|
|
68
|
+
*/
|
|
69
|
+
async reverseGeocode(latitude: number, longitude: number) {
|
|
70
|
+
try {
|
|
71
|
+
const [address] = await Location.reverseGeocodeAsync({ latitude, longitude });
|
|
72
|
+
if (!address) return undefined;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
city: address.city,
|
|
76
|
+
region: address.region,
|
|
77
|
+
country: address.country,
|
|
78
|
+
street: address.street,
|
|
79
|
+
formattedAddress: [address.city, address.country].filter(Boolean).join(", "),
|
|
80
|
+
};
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn("[LocationService] Reverse geocode failed:", error);
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
287
85
|
}
|
|
288
|
-
return `${location.latitude.toFixed(
|
|
289
|
-
LOCATION_CONSTANTS.COORDINATE_PRECISION
|
|
290
|
-
)}, ${location.longitude.toFixed(LOCATION_CONSTANTS.COORDINATE_PRECISION)}`;
|
|
291
|
-
}
|
|
292
86
|
}
|
|
293
87
|
|
|
294
|
-
/**
|
|
295
|
-
* Singleton instance
|
|
296
|
-
* Use this for all location operations
|
|
297
|
-
*/
|
|
298
88
|
export const locationService = new LocationService();
|
|
@@ -1,225 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* - Cached location access
|
|
11
|
-
* - Error handling
|
|
12
|
-
* - Format locations for display
|
|
13
|
-
*
|
|
14
|
-
* USAGE:
|
|
15
|
-
* ```typescript
|
|
16
|
-
* import { useLocation } from '@umituz/react-native-location';
|
|
17
|
-
*
|
|
18
|
-
* const MyComponent = () => {
|
|
19
|
-
* const {
|
|
20
|
-
* location,
|
|
21
|
-
* loading,
|
|
22
|
-
* error,
|
|
23
|
-
* hasPermission,
|
|
24
|
-
* requestPermission,
|
|
25
|
-
* getCurrentLocation,
|
|
26
|
-
* getCached,
|
|
27
|
-
* formatLocation,
|
|
28
|
-
* } = useLocation();
|
|
29
|
-
*
|
|
30
|
-
* useEffect(() => {
|
|
31
|
-
* // Auto-fetch location on mount
|
|
32
|
-
* getCurrentLocation();
|
|
33
|
-
* }, []);
|
|
34
|
-
*
|
|
35
|
-
* if (loading) return <LoadingIndicator />;
|
|
36
|
-
* if (error) return <ErrorMessage message={error} />;
|
|
37
|
-
*
|
|
38
|
-
* return (
|
|
39
|
-
* <View>
|
|
40
|
-
* {location && (
|
|
41
|
-
* <Text>Location: {formatLocation(location)}</Text>
|
|
42
|
-
* )}
|
|
43
|
-
* <AtomicButton onPress={getCurrentLocation}>
|
|
44
|
-
* Refresh Location
|
|
45
|
-
* </AtomicButton>
|
|
46
|
-
* </View>
|
|
47
|
-
* );
|
|
48
|
-
* };
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
import { useState, useCallback, useEffect } from 'react';
|
|
53
|
-
import { locationService } from '../../infrastructure/services/LocationService';
|
|
54
|
-
import type {
|
|
55
|
-
LocationData,
|
|
56
|
-
LocationPermissionStatus,
|
|
57
|
-
} from '../../domain/entities/Location';
|
|
58
|
-
|
|
59
|
-
export interface UseLocationReturn {
|
|
60
|
-
/** Current location data (null if not retrieved) */
|
|
61
|
-
location: LocationData | null;
|
|
62
|
-
|
|
63
|
-
/** Loading state during location retrieval */
|
|
64
|
-
loading: boolean;
|
|
65
|
-
|
|
66
|
-
/** Error message if location retrieval failed */
|
|
67
|
-
error: string | null;
|
|
68
|
-
|
|
69
|
-
/** Current permission status */
|
|
70
|
-
permissionStatus: LocationPermissionStatus;
|
|
71
|
-
|
|
72
|
-
/** Whether location permissions are granted */
|
|
73
|
-
hasPermission: boolean;
|
|
74
|
-
|
|
75
|
-
/** Whether location services are available on this platform */
|
|
76
|
-
isAvailable: boolean;
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Request location permissions from user
|
|
80
|
-
* @returns Promise resolving to true if granted
|
|
81
|
-
*/
|
|
82
|
-
requestPermission: () => Promise<boolean>;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Get current location with fallback strategies
|
|
86
|
-
* Updates location state and handles errors
|
|
87
|
-
*/
|
|
88
|
-
getCurrentLocation: () => Promise<void>;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Get cached location if available
|
|
92
|
-
* @returns Cached location or null
|
|
93
|
-
*/
|
|
94
|
-
getCached: () => LocationData | null;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Format location for display
|
|
98
|
-
* @param loc - LocationData to format
|
|
99
|
-
* @returns Formatted string (address or coordinates)
|
|
100
|
-
*/
|
|
101
|
-
formatLocation: (loc: LocationData) => string;
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Clear current location state
|
|
105
|
-
*/
|
|
106
|
-
clearLocation: () => void;
|
|
1
|
+
import { useState, useCallback } from "react";
|
|
2
|
+
import { locationService } from "../../infrastructure/services/LocationService";
|
|
3
|
+
import { LocationData, LocationError } from "../../domain/entities/Location";
|
|
4
|
+
|
|
5
|
+
export interface UseLocationResult {
|
|
6
|
+
location: LocationData | null;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
error: LocationError | null;
|
|
9
|
+
getCurrentLocation: () => Promise<LocationData | null>;
|
|
107
10
|
}
|
|
108
11
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const [location, setLocation] = useState<LocationData | null>(null);
|
|
114
|
-
const [loading, setLoading] = useState<boolean>(false);
|
|
115
|
-
const [error, setError] = useState<string | null>(null);
|
|
116
|
-
const [permissionStatus, setPermissionStatus] =
|
|
117
|
-
useState<LocationPermissionStatus>('unknown');
|
|
118
|
-
|
|
119
|
-
// Check if location services are available
|
|
120
|
-
const isAvailable = locationService.isLocationAvailable();
|
|
121
|
-
|
|
122
|
-
// Check initial permission status
|
|
123
|
-
useEffect(() => {
|
|
124
|
-
const checkPermission = async () => {
|
|
125
|
-
const hasPermission = await locationService.hasPermissions();
|
|
126
|
-
setPermissionStatus(
|
|
127
|
-
hasPermission ? 'granted' : locationService.getPermissionStatus()
|
|
128
|
-
);
|
|
129
|
-
};
|
|
12
|
+
export function useLocation(): UseLocationResult {
|
|
13
|
+
const [location, setLocation] = useState<LocationData | null>(null);
|
|
14
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
15
|
+
const [error, setError] = useState<LocationError | null>(null);
|
|
130
16
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
}, [isAvailable]);
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Request location permissions
|
|
138
|
-
*/
|
|
139
|
-
const requestPermission = useCallback(async (): Promise<boolean> => {
|
|
140
|
-
if (!isAvailable) {
|
|
141
|
-
setError('Location services not available on this platform');
|
|
142
|
-
setPermissionStatus('denied');
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
const granted = await locationService.requestPermissions();
|
|
148
|
-
setPermissionStatus(granted ? 'granted' : 'denied');
|
|
149
|
-
|
|
150
|
-
if (!granted) {
|
|
151
|
-
setError('Location permission denied');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return granted;
|
|
155
|
-
} catch (err) {
|
|
156
|
-
setError('Failed to request location permission');
|
|
157
|
-
setPermissionStatus('denied');
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
}, [isAvailable]);
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Get current location with fallback strategies
|
|
164
|
-
*/
|
|
165
|
-
const getCurrentLocation = useCallback(async (): Promise<void> => {
|
|
166
|
-
if (!isAvailable) {
|
|
167
|
-
setError('Location services not available on this platform');
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
setLoading(true);
|
|
172
|
-
setError(null);
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
const currentLocation = await locationService.getCurrentLocation();
|
|
176
|
-
|
|
177
|
-
if (currentLocation) {
|
|
178
|
-
setLocation(currentLocation);
|
|
17
|
+
const getCurrentLocation = useCallback(async () => {
|
|
18
|
+
setIsLoading(true);
|
|
179
19
|
setError(null);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Clear location state
|
|
206
|
-
*/
|
|
207
|
-
const clearLocation = useCallback((): void => {
|
|
208
|
-
setLocation(null);
|
|
209
|
-
setError(null);
|
|
210
|
-
}, []);
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
location,
|
|
214
|
-
loading,
|
|
215
|
-
error,
|
|
216
|
-
permissionStatus,
|
|
217
|
-
hasPermission: permissionStatus === 'granted',
|
|
218
|
-
isAvailable,
|
|
219
|
-
requestPermission,
|
|
220
|
-
getCurrentLocation,
|
|
221
|
-
getCached,
|
|
222
|
-
formatLocation,
|
|
223
|
-
clearLocation,
|
|
224
|
-
};
|
|
225
|
-
};
|
|
20
|
+
try {
|
|
21
|
+
const data = await locationService.getCurrentPosition(true);
|
|
22
|
+
setLocation(data);
|
|
23
|
+
return data;
|
|
24
|
+
} catch (err: any) {
|
|
25
|
+
const errorObj = {
|
|
26
|
+
code: err.code || "UNKNOWN_ERROR",
|
|
27
|
+
message: err.message || "An unknown error occurred",
|
|
28
|
+
};
|
|
29
|
+
setError(errorObj);
|
|
30
|
+
return null;
|
|
31
|
+
} finally {
|
|
32
|
+
setIsLoading(false);
|
|
33
|
+
}
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
location,
|
|
38
|
+
isLoading,
|
|
39
|
+
error,
|
|
40
|
+
getCurrentLocation,
|
|
41
|
+
};
|
|
42
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 Ümit UZ
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
22
|
-
|