use-geo-sync 1.0.0 β 1.0.2
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/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/dist/index.mjs +4 -1
- package/package.json +1 -1
- package/readme.md +66 -33
- package/src/backgroundTask.ts +3 -2
- package/src/index.ts +1 -1
- package/src/types.ts +7 -6
package/dist/index.d.mts
CHANGED
|
@@ -5,6 +5,7 @@ interface GeoPoint {
|
|
|
5
5
|
timestamp: number;
|
|
6
6
|
accuracy: number | null;
|
|
7
7
|
speed: number | null;
|
|
8
|
+
heading: number | null;
|
|
8
9
|
}
|
|
9
10
|
interface SyncConfig {
|
|
10
11
|
batchSize?: number;
|
|
@@ -31,5 +32,6 @@ declare function useGeoPermissions(): {
|
|
|
31
32
|
declare function useGeoSync(config?: SyncConfig): UseGeoSyncResult;
|
|
32
33
|
|
|
33
34
|
declare const BACKGROUND_LOCATION_TASK = "BACKGROUND_LOCATION_TASK";
|
|
35
|
+
declare let locationQueue: GeoPoint[];
|
|
34
36
|
|
|
35
|
-
export { BACKGROUND_LOCATION_TASK, type GeoPoint, type PermissionState, type SyncConfig, type UseGeoSyncResult, useGeoPermissions, useGeoSync };
|
|
37
|
+
export { BACKGROUND_LOCATION_TASK, type GeoPoint, type PermissionState, type SyncConfig, type UseGeoSyncResult, locationQueue, useGeoPermissions, useGeoSync };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ interface GeoPoint {
|
|
|
5
5
|
timestamp: number;
|
|
6
6
|
accuracy: number | null;
|
|
7
7
|
speed: number | null;
|
|
8
|
+
heading: number | null;
|
|
8
9
|
}
|
|
9
10
|
interface SyncConfig {
|
|
10
11
|
batchSize?: number;
|
|
@@ -31,5 +32,6 @@ declare function useGeoPermissions(): {
|
|
|
31
32
|
declare function useGeoSync(config?: SyncConfig): UseGeoSyncResult;
|
|
32
33
|
|
|
33
34
|
declare const BACKGROUND_LOCATION_TASK = "BACKGROUND_LOCATION_TASK";
|
|
35
|
+
declare let locationQueue: GeoPoint[];
|
|
34
36
|
|
|
35
|
-
export { BACKGROUND_LOCATION_TASK, type GeoPoint, type PermissionState, type SyncConfig, type UseGeoSyncResult, useGeoPermissions, useGeoSync };
|
|
37
|
+
export { BACKGROUND_LOCATION_TASK, type GeoPoint, type PermissionState, type SyncConfig, type UseGeoSyncResult, locationQueue, useGeoPermissions, useGeoSync };
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
BACKGROUND_LOCATION_TASK: () => BACKGROUND_LOCATION_TASK,
|
|
34
|
+
locationQueue: () => locationQueue,
|
|
34
35
|
useGeoPermissions: () => useGeoPermissions,
|
|
35
36
|
useGeoSync: () => useGeoSync
|
|
36
37
|
});
|
|
@@ -111,7 +112,9 @@ TaskManager.defineTask(BACKGROUND_LOCATION_TASK, async ({ data, error }) => {
|
|
|
111
112
|
longitude: loc.coords.longitude,
|
|
112
113
|
timestamp: loc.timestamp,
|
|
113
114
|
accuracy: loc.coords.accuracy,
|
|
114
|
-
speed: loc.coords.speed
|
|
115
|
+
speed: loc.coords.speed,
|
|
116
|
+
heading: loc.coords.heading
|
|
117
|
+
// <-- ADD THIS NEW LINE
|
|
115
118
|
}));
|
|
116
119
|
locationQueue.push(...newPoints);
|
|
117
120
|
console.log(`[use-geo-sync] \u{1F4CD} Caught ${newPoints.length} points. Queue size: ${locationQueue.length}`);
|
|
@@ -194,6 +197,7 @@ function useGeoSync(config) {
|
|
|
194
197
|
// Annotate the CommonJS export names for ESM import in node:
|
|
195
198
|
0 && (module.exports = {
|
|
196
199
|
BACKGROUND_LOCATION_TASK,
|
|
200
|
+
locationQueue,
|
|
197
201
|
useGeoPermissions,
|
|
198
202
|
useGeoSync
|
|
199
203
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -73,7 +73,9 @@ TaskManager.defineTask(BACKGROUND_LOCATION_TASK, async ({ data, error }) => {
|
|
|
73
73
|
longitude: loc.coords.longitude,
|
|
74
74
|
timestamp: loc.timestamp,
|
|
75
75
|
accuracy: loc.coords.accuracy,
|
|
76
|
-
speed: loc.coords.speed
|
|
76
|
+
speed: loc.coords.speed,
|
|
77
|
+
heading: loc.coords.heading
|
|
78
|
+
// <-- ADD THIS NEW LINE
|
|
77
79
|
}));
|
|
78
80
|
locationQueue.push(...newPoints);
|
|
79
81
|
console.log(`[use-geo-sync] \u{1F4CD} Caught ${newPoints.length} points. Queue size: ${locationQueue.length}`);
|
|
@@ -155,6 +157,7 @@ function useGeoSync(config) {
|
|
|
155
157
|
}
|
|
156
158
|
export {
|
|
157
159
|
BACKGROUND_LOCATION_TASK,
|
|
160
|
+
locationQueue,
|
|
158
161
|
useGeoPermissions,
|
|
159
162
|
useGeoSync
|
|
160
163
|
};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
# use-geo-sync π
|
|
2
2
|
|
|
3
3
|
A plug-and-play, offline-first background location tracking and syncing toolkit for React Native and Expo.
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ Handling iOS/Android background permissions, app state, and network drops is a m
|
|
|
8
8
|
- **π± Bulletproof Permissions:** Automatically handles the strict Foreground -> Background permission cascade on both iOS and Android.
|
|
9
9
|
- **π Battery Optimized:** Uses balanced accuracy and headless task management to save battery life.
|
|
10
10
|
- **π‘ Offline-First Queuing:** If the network drops, coordinates are safely queued locally. When the connection returns, the queue is automatically batched and synced to your backend.
|
|
11
|
-
-
|
|
11
|
+
- **π§ Live Navigation Ready:** Exposes compass headings and the raw local queue to build Uber-style live map arrows.
|
|
12
12
|
|
|
13
13
|
## π¦ Installation
|
|
14
14
|
|
|
@@ -23,62 +23,95 @@ Important: This package relies on Expo modules under the hood. You must install
|
|
|
23
23
|
npx expo install expo-location expo-task-manager
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
# π Quick Start
|
|
27
|
-
Here is
|
|
26
|
+
# π Standard API Sync (Quick Start)
|
|
27
|
+
Here is how to set up automatic background tracking that syncs to your database in batches:
|
|
28
28
|
```
|
|
29
|
-
import {
|
|
30
|
-
import { View, Text, Button, StyleSheet } from 'react-native';
|
|
29
|
+
import { View, Text, Button } from 'react-native';
|
|
31
30
|
import { useGeoSync } from 'use-geo-sync';
|
|
32
31
|
|
|
33
32
|
export default function TrackingScreen() {
|
|
34
|
-
const {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
startTracking,
|
|
38
|
-
stopTracking,
|
|
39
|
-
isTracking,
|
|
40
|
-
queueLength
|
|
41
|
-
} = useGeoSync({
|
|
42
|
-
syncEndpoint: '[https://your-api.com/api/location-sync](https://your-api.com/api/location-sync)', // Your backend URL
|
|
43
|
-
batchSize: 20, // Auto-syncs to your backend after collecting 20 points
|
|
44
|
-
syncIntervalMs: 10000 // Pings GPS every 10 seconds
|
|
33
|
+
const { permissionState, requestPermissions, startTracking, stopTracking, isTracking } = useGeoSync({
|
|
34
|
+
syncEndpoint: '[https://your-api.com/api/location-sync](https://your-api.com/api/location-sync)',
|
|
35
|
+
batchSize: 20,
|
|
45
36
|
});
|
|
46
37
|
|
|
47
38
|
return (
|
|
48
|
-
<View
|
|
49
|
-
<Text style={styles.title}>π Fleet Tracker</Text>
|
|
50
|
-
|
|
51
|
-
<Text>Status: {isTracking ? 'Active π’' : 'Stopped π΄'}</Text>
|
|
39
|
+
<View>
|
|
52
40
|
<Text>Permissions: {permissionState}</Text>
|
|
53
|
-
<Text>Unsynced Points in Queue: {queueLength}</Text>
|
|
54
|
-
|
|
55
41
|
{permissionState !== 'granted_background' && (
|
|
56
|
-
<Button title="Grant
|
|
42
|
+
<Button title="Grant Permissions" onPress={requestPermissions} />
|
|
57
43
|
)}
|
|
58
|
-
|
|
59
44
|
<Button
|
|
60
|
-
title={isTracking ? "Stop
|
|
45
|
+
title={isTracking ? "Stop" : "Start"}
|
|
61
46
|
onPress={isTracking ? stopTracking : startTracking}
|
|
62
|
-
color={isTracking ? "red" : "green"}
|
|
63
47
|
/>
|
|
64
48
|
</View>
|
|
65
49
|
);
|
|
66
50
|
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## πΊοΈ Advanced Use Cases (Offline & Maps)
|
|
54
|
+
|
|
55
|
+
If you don't provide a `syncEndpoint`, the package acts as a powerful local GPS engine. You can import the raw `locationQueue` to build complex map features.
|
|
56
|
+
|
|
57
|
+
### 1. The Uber-Style Live Navigation Arrow
|
|
58
|
+
Keep a map marker perfectly locked onto the user's position and rotation, even if the app goes into the background.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
import { useEffect, useState } from 'react';
|
|
62
|
+
import MapView, { Marker } from 'react-native-maps';
|
|
63
|
+
import { useGeoSync, locationQueue } from 'use-geo-sync';
|
|
64
|
+
|
|
65
|
+
export default function LiveMap() {
|
|
66
|
+
const { queueLength } = useGeoSync({ batchSize: 1000 }); // High batch size for local use
|
|
67
|
+
const [currentLoc, setCurrentLoc] = useState(null);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (queueLength > 0) {
|
|
71
|
+
setCurrentLoc(locationQueue[locationQueue.length - 1]);
|
|
72
|
+
}
|
|
73
|
+
}, [queueLength]);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<MapView style={{ flex: 1 }}>
|
|
77
|
+
{currentLoc && (
|
|
78
|
+
<Marker
|
|
79
|
+
coordinate={{ latitude: currentLoc.latitude, longitude: currentLoc.longitude }}
|
|
80
|
+
// Rotate the custom arrow image based on user's compass heading
|
|
81
|
+
style={{ transform: [{ rotate: `${currentLoc.heading || 0}deg` }] }}
|
|
82
|
+
image={require('./assets/arrow.png')}
|
|
83
|
+
/>
|
|
84
|
+
)}
|
|
85
|
+
</MapView>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 2. Offline Breadcrumb Trails
|
|
91
|
+
Draw a line of exactly where the user has walked today, entirely offline.
|
|
67
92
|
|
|
68
|
-
const styles = StyleSheet.create({
|
|
69
|
-
container: { flex: 1, justifyContent: 'center', alignItems: 'center', gap: 10 },
|
|
70
|
-
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20 }
|
|
71
|
-
});
|
|
72
93
|
```
|
|
94
|
+
import MapView, { Polyline } from 'react-native-maps';
|
|
95
|
+
import { useGeoSync, locationQueue } from 'use-geo-sync';
|
|
73
96
|
|
|
97
|
+
export function MapScreen() {
|
|
98
|
+
const { isTracking } = useGeoSync({ batchSize: 1000 });
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<MapView style={{ flex: 1 }}>
|
|
102
|
+
<Polyline coordinates={locationQueue} strokeColor="#000" strokeWidth={4} />
|
|
103
|
+
</MapView>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
74
107
|
## βοΈ Configuration Options (SyncConfig)
|
|
75
108
|
|
|
76
109
|
When calling `useGeoSync(config)`, you can pass the following options:
|
|
77
110
|
|
|
78
111
|
| Property | Type | Default | Description |
|
|
79
112
|
| :--- | :--- | :--- | :--- |
|
|
80
|
-
| `syncEndpoint` | `string` | `undefined` | The backend API URL
|
|
81
|
-
| `batchSize` | `number` | `20` | How many location points to collect before
|
|
113
|
+
| `syncEndpoint` | `string` | `undefined` | The backend API URL to POST batched coordinates to. |
|
|
114
|
+
| `batchSize` | `number` | `20` | How many location points to collect before syncing. |
|
|
82
115
|
| `syncIntervalMs` | `number` | `10000` | Time in milliseconds between GPS pings. |
|
|
83
116
|
|
|
84
117
|
## π License
|
package/src/backgroundTask.ts
CHANGED
|
@@ -27,13 +27,14 @@ TaskManager.defineTask(BACKGROUND_LOCATION_TASK, async ({ data, error }) => {
|
|
|
27
27
|
const { locations } = data as { locations: Location.LocationObject[] };
|
|
28
28
|
|
|
29
29
|
// 1. Transform Expo's object
|
|
30
|
-
|
|
30
|
+
const newPoints: GeoPoint[] = locations.map(loc => ({
|
|
31
31
|
id: `${loc.timestamp}-${Math.random().toString(36).substr(2, 9)}`,
|
|
32
32
|
latitude: loc.coords.latitude,
|
|
33
33
|
longitude: loc.coords.longitude,
|
|
34
34
|
timestamp: loc.timestamp,
|
|
35
35
|
accuracy: loc.coords.accuracy,
|
|
36
|
-
speed: loc.coords.speed
|
|
36
|
+
speed: loc.coords.speed,
|
|
37
|
+
heading: loc.coords.heading // <-- ADD THIS NEW LINE
|
|
37
38
|
}));
|
|
38
39
|
|
|
39
40
|
// 2. Add to Queue
|
package/src/index.ts
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
export * from './types';
|
|
4
4
|
export { useGeoPermissions } from './useGeoPermissions';
|
|
5
5
|
export { useGeoSync } from './useGeoSync';
|
|
6
|
-
export { BACKGROUND_LOCATION_TASK } from './backgroundTask';
|
|
6
|
+
export { BACKGROUND_LOCATION_TASK, locationQueue } from './backgroundTask'; // <-- ADD locationQueue
|
package/src/types.ts
CHANGED
|
@@ -3,19 +3,20 @@
|
|
|
3
3
|
import * as Location from 'expo-location';
|
|
4
4
|
|
|
5
5
|
export interface GeoPoint {
|
|
6
|
-
id: string;
|
|
6
|
+
id: string;
|
|
7
7
|
latitude: number;
|
|
8
8
|
longitude: number;
|
|
9
9
|
timestamp: number;
|
|
10
10
|
accuracy: number | null;
|
|
11
11
|
speed: number | null;
|
|
12
|
+
heading: number | null; // <-- ADD THIS NEW LINE
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export interface SyncConfig {
|
|
15
|
-
batchSize?: number;
|
|
16
|
-
syncEndpoint?: string;
|
|
17
|
-
syncIntervalMs?: number;
|
|
18
|
-
retryLimit?: number;
|
|
16
|
+
batchSize?: number;
|
|
17
|
+
syncEndpoint?: string;
|
|
18
|
+
syncIntervalMs?: number;
|
|
19
|
+
retryLimit?: number;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export type PermissionState =
|
|
@@ -31,5 +32,5 @@ export interface UseGeoSyncResult {
|
|
|
31
32
|
startTracking: () => Promise<void>;
|
|
32
33
|
stopTracking: () => Promise<void>;
|
|
33
34
|
isTracking: boolean;
|
|
34
|
-
queueLength: number;
|
|
35
|
+
queueLength: number;
|
|
35
36
|
}
|