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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-geo-sync",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A React Native/Expo headless toolkit for background location sync and queuing.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
package/readme.md CHANGED
@@ -1,4 +1,4 @@
1
- Markdown# use-geo-sync 🌍
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
- - **πŸ›  Zero-Config Architecture:** No complex Redux/Zustand setups required. It just works.
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 a complete example of how to implement background tracking in your app:
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 { useEffect } from 'react';
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
- permissionState,
36
- requestPermissions,
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 style={styles.container}>
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 Background Permissions" onPress={requestPermissions} />
42
+ <Button title="Grant Permissions" onPress={requestPermissions} />
57
43
  )}
58
-
59
44
  <Button
60
- title={isTracking ? "Stop Tracking" : "Start Tracking"}
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 where the queued location batch will be sent via POST request. |
81
- | `batchSize` | `number` | `20` | How many location points to collect before triggering a sync to the backend. |
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
@@ -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
- const newPoints: GeoPoint[] = locations.map(loc => ({
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; // Unique ID for the queue
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; // How many points to hold before syncing (default: 20)
16
- syncEndpoint?: string; // The backend URL to POST data to
17
- syncIntervalMs?: number; // Time-based sync fallback
18
- retryLimit?: number; // How many times to retry a failed sync
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; // Number of unsynced points
35
+ queueLength: number;
35
36
  }