@tsachit/react-native-geo-service 1.0.2 → 1.0.4
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/README.md +67 -43
- package/lib/GeoDebugPanel.js +85 -15
- package/lib/GeoSessionStore.d.ts +65 -0
- package/lib/GeoSessionStore.js +158 -0
- package/lib/index.d.ts +2 -3
- package/lib/index.js +12 -30
- package/lib/types.d.ts +1 -1
- package/package.json +10 -9
- package/debug-panel.d.ts +0 -1
- package/debug-panel.js +0 -1
- package/lib/autoDebug.d.ts +0 -2
- package/lib/autoDebug.js +0 -22
- package/lib/setup.d.ts +0 -1
- package/lib/setup.js +0 -17
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Battery-efficient background geolocation for React Native — a lightweight, fre
|
|
|
6
6
|
- Keeps tracking when the app is backgrounded or killed (headless mode)
|
|
7
7
|
- Uses `FusedLocationProviderClient` on Android and `CLLocationManager` on iOS
|
|
8
8
|
- **Adaptive accuracy** — GPS turns off automatically when the device is idle and wakes the moment movement is detected
|
|
9
|
-
- **Debug panel** — draggable floating overlay
|
|
9
|
+
- **Debug panel** — draggable floating overlay showing live metrics, GPS activity, and battery saving suggestions; add `<GeoDebugOverlay />` once and it self-manages based on `debug: true` and tracking state
|
|
10
10
|
- Fully configurable from JavaScript — no API keys, no license required
|
|
11
11
|
|
|
12
12
|
---
|
|
@@ -83,13 +83,17 @@ In your app's **`index.js`** (top level, outside any component):
|
|
|
83
83
|
|
|
84
84
|
```js
|
|
85
85
|
import { AppRegistry } from 'react-native';
|
|
86
|
+
import RNGSAppRegistry from '@tsachit/react-native-geo-service';
|
|
86
87
|
import App from './App';
|
|
87
88
|
|
|
88
89
|
AppRegistry.registerComponent('YourApp', () => App);
|
|
89
90
|
|
|
90
91
|
// Handles location events when the React context is not active.
|
|
91
92
|
// Runs even when the app is killed (foreground service must be running).
|
|
92
|
-
|
|
93
|
+
// Using RNGSAppRegistry.registerHeadlessTask() is preferred over
|
|
94
|
+
// AppRegistry.registerHeadlessTask() directly — it automatically keeps
|
|
95
|
+
// the debug panel's session store in sync while the app is killed.
|
|
96
|
+
RNGSAppRegistry.registerHeadlessTask(async (location) => {
|
|
93
97
|
console.log('[Background] Location:', location);
|
|
94
98
|
// Send to your server using a pre-stored auth token (e.g. SecureStore/Keychain).
|
|
95
99
|
// Do not rely on in-memory state — this JS context is isolated.
|
|
@@ -188,10 +192,12 @@ const tracking = await RNGeoService.isTracking();
|
|
|
188
192
|
|
|
189
193
|
### Register headless task via the module
|
|
190
194
|
|
|
191
|
-
|
|
195
|
+
Use `registerHeadlessTask()` from the package instead of `AppRegistry.registerHeadlessTask()` directly — it wraps your handler to automatically keep the debug panel's session metrics in sync while the app is killed:
|
|
192
196
|
|
|
193
197
|
```ts
|
|
194
|
-
|
|
198
|
+
import RNGSAppRegistry from '@tsachit/react-native-geo-service';
|
|
199
|
+
|
|
200
|
+
RNGSAppRegistry.registerHeadlessTask(async (location) => {
|
|
195
201
|
await sendToServer(location);
|
|
196
202
|
});
|
|
197
203
|
```
|
|
@@ -246,7 +252,7 @@ Subscribe to location updates. Call `.remove()` on the returned subscription to
|
|
|
246
252
|
Subscribe to location errors (e.g. permission revoked mid-session).
|
|
247
253
|
|
|
248
254
|
### `registerHeadlessTask(handler)` *(Android only)*
|
|
249
|
-
Register a function to handle location events when the app is not in the foreground.
|
|
255
|
+
Register a function to handle location events when the app is not in the foreground. Preferred over `AppRegistry.registerHeadlessTask()` directly — automatically keeps `GeoSessionStore` in sync so the debug panel shows accurate Geopoints counts while the app is killed.
|
|
250
256
|
|
|
251
257
|
### `getBatteryInfo(): Promise<BatteryInfo>`
|
|
252
258
|
Returns battery and session tracking metrics. See [Debug mode](#debug-mode) below.
|
|
@@ -284,10 +290,10 @@ interface BatteryInfo {
|
|
|
284
290
|
levelAtStart: number; // battery level when start() was called
|
|
285
291
|
drainSinceStart: number; // total % dropped since start() (whole device)
|
|
286
292
|
|
|
287
|
-
updateCount: number; // location
|
|
293
|
+
updateCount: number; // total location received this session
|
|
288
294
|
trackingElapsedSeconds: number; // seconds since start() was called
|
|
289
295
|
gpsActiveSeconds: number; // seconds the GPS chip was actively running
|
|
290
|
-
updatesPerMinute: number; // average location
|
|
296
|
+
updatesPerMinute: number; // average total location per minute
|
|
291
297
|
drainRatePerHour: number; // battery drain rate in %/hr (whole device)
|
|
292
298
|
}
|
|
293
299
|
```
|
|
@@ -312,39 +318,38 @@ Set `debug: true` in `configure()` to enable debug features:
|
|
|
312
318
|
- **iOS** — forces the blue location arrow in the status bar while tracking is active
|
|
313
319
|
- **Android** — notification title changes to `[DEBUG] <title>` so you can confirm the foreground service is running
|
|
314
320
|
- **Both** — verbose native logging via `console.log` / `Logcat`
|
|
315
|
-
- **Both** — a floating debug panel
|
|
321
|
+
- **Both** — a floating debug panel shows live metrics and battery saving suggestions; add `<GeoDebugOverlay />` once to your component tree and it self-manages visibility
|
|
316
322
|
|
|
317
|
-
### Setup
|
|
323
|
+
### Setup
|
|
318
324
|
|
|
319
|
-
Add
|
|
325
|
+
Add `<GeoDebugOverlay />` once to your component tree, co-located with wherever you call `RNGeoService.start()`. It self-manages visibility — it only shows when `debug: true` is set in `configure()` and tracking is active.
|
|
320
326
|
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
327
|
+
```tsx
|
|
328
|
+
import { GeoDebugOverlay } from '@tsachit/react-native-geo-service';
|
|
329
|
+
|
|
330
|
+
// Render it alongside your navigation root or wherever tracking is used:
|
|
331
|
+
return (
|
|
332
|
+
<>
|
|
333
|
+
<YourNavigator />
|
|
334
|
+
<GeoDebugOverlay />
|
|
335
|
+
</>
|
|
336
|
+
);
|
|
330
337
|
```
|
|
331
338
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
### Auto-mount debug panel
|
|
335
|
-
|
|
336
|
-
Once the setup import is in place, the panel mounts and unmounts automatically:
|
|
339
|
+
Then set `debug: true` in your config:
|
|
337
340
|
|
|
338
341
|
```ts
|
|
339
|
-
// Panel appears automatically when tracking starts
|
|
340
342
|
await RNGeoService.configure({ debug: true, ... });
|
|
341
|
-
await RNGeoService.start(); //
|
|
343
|
+
await RNGeoService.start(); // panel becomes visible automatically
|
|
342
344
|
|
|
343
|
-
//
|
|
344
|
-
await RNGeoService.stop();
|
|
345
|
+
await RNGeoService.stop(); // panel hides automatically
|
|
345
346
|
```
|
|
346
347
|
|
|
347
|
-
|
|
348
|
+
> **Note:** `GeoDebugOverlay` is a standard React component — it renders nothing in production when `debug: false`. It is safe to leave in the tree at all times.
|
|
349
|
+
|
|
350
|
+
| Minimized | Opened |
|
|
351
|
+
|--------|-------------|
|
|
352
|
+
| <img width="349" height="261" alt="image" src="https://github.com/user-attachments/assets/a6b43b93-7a93-485d-a68d-f4e4fe658011" /> | <img width="363" height="348" alt="image" src="https://github.com/user-attachments/assets/9c02ebc9-28f2-4983-982b-810c98a32dfe" /> |
|
|
348
353
|
|
|
349
354
|
### Debug panel behaviour
|
|
350
355
|
|
|
@@ -353,17 +358,19 @@ The panel is a **draggable, minimizable floating overlay** that starts minimized
|
|
|
353
358
|
- **Tap the 📍 circle** to expand
|
|
354
359
|
- **Drag** by holding the striped header bar
|
|
355
360
|
- **Minimize** with the ⊖ button — collapses back to the 📍 circle
|
|
361
|
+
- **Geopoints updates in real time** on every location event — no need to wait for the poll interval
|
|
356
362
|
|
|
357
|
-
**Metrics shown
|
|
363
|
+
**Metrics shown** (all values are cumulative across app restarts — see [GeoSessionStore](#geosessionstore)):
|
|
358
364
|
|
|
359
365
|
| Metric | Description |
|
|
360
366
|
|--------|-------------|
|
|
361
|
-
|
|
|
362
|
-
|
|
|
367
|
+
| Started | Local date/time the very first tracking session began |
|
|
368
|
+
| Tracking for | Cumulative duration across all sessions |
|
|
369
|
+
| Geopoints | Total locations received across all sessions |
|
|
363
370
|
| Updates/min | Average frequency of location updates |
|
|
364
|
-
| GPS active | % of
|
|
371
|
+
| GPS active | % of total time the GPS chip was on vs idle |
|
|
365
372
|
| Battery now | Current device battery level |
|
|
366
|
-
| Drained | Total device battery % dropped since `start()` |
|
|
373
|
+
| Drained | Total device battery % dropped since first `start()` |
|
|
367
374
|
| Drain rate | Battery consumed per hour (total device, not just location) |
|
|
368
375
|
|
|
369
376
|
**Smart suggestions** are shown automatically:
|
|
@@ -374,22 +381,39 @@ The panel is a **draggable, minimizable floating overlay** that starts minimized
|
|
|
374
381
|
- 🔴 Drain rate > 8%/hr → try `'balanced'` accuracy or longer update intervals
|
|
375
382
|
- ✅ All metrics in range → confirms settings are efficient
|
|
376
383
|
|
|
377
|
-
> **Note:** Battery drain is measured at the whole-device level since iOS and Android do not expose per-app battery consumption via public APIs. Use GPS active % and updates/min as the primary indicators of how much
|
|
384
|
+
> **Note:** Battery drain is measured at the whole-device level since iOS and Android do not expose per-app battery consumption via public APIs. Use GPS active % and updates/min as the primary indicators of how much this package contributes.
|
|
378
385
|
|
|
379
|
-
### Manual
|
|
386
|
+
### Manual panel (optional)
|
|
380
387
|
|
|
381
|
-
|
|
388
|
+
For a custom poll interval or always-visible panel, use `GeoDebugPanel` directly:
|
|
382
389
|
|
|
383
390
|
```tsx
|
|
384
|
-
import { GeoDebugPanel
|
|
385
|
-
|
|
386
|
-
// Renders anywhere in your tree — self-hides when tracking is inactive:
|
|
387
|
-
<GeoDebugOverlay />
|
|
391
|
+
import { GeoDebugPanel } from '@tsachit/react-native-geo-service';
|
|
388
392
|
|
|
389
|
-
// Always-visible panel with custom poll interval:
|
|
390
393
|
<GeoDebugPanel pollInterval={15000} />
|
|
391
394
|
```
|
|
392
395
|
|
|
396
|
+
### GeoSessionStore
|
|
397
|
+
|
|
398
|
+
All debug panel metrics are stored in-memory on the native side and would normally reset every time tracking restarts (app killed, OS killed the service, device rebooted). `GeoSessionStore` persists snapshots to `AsyncStorage` so the panel shows **cumulative totals** across sessions.
|
|
399
|
+
|
|
400
|
+
Requires [`@react-native-async-storage/async-storage`](https://github.com/react-native-async-storage/async-storage) to be installed in your app (optional peer dependency — the panel silently skips persistence if it is not present).
|
|
401
|
+
|
|
402
|
+
**Session boundaries** are detected automatically: when `batteryLevelAtStart` changes between snapshots, the previous session is archived before the new one begins. This prevents double-counting when the Android foreground service keeps running after the app is reopened.
|
|
403
|
+
|
|
404
|
+
The **"↺ Reset stats"** button inside the panel clears all accumulated data and the recorded start time so you can re-measure from scratch.
|
|
405
|
+
|
|
406
|
+
If you use `RNGSAppRegistry.registerHeadlessTask()`, `GeoSessionStore` is updated automatically on each headless location event — no extra code required. If you register via `AppRegistry.registerHeadlessTask()` directly, you can increment the counter manually:
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
import { GeoSessionStore } from '@tsachit/react-native-geo-service';
|
|
410
|
+
|
|
411
|
+
AppRegistry.registerHeadlessTask('GeoServiceHeadlessTask', () => async (location) => {
|
|
412
|
+
await sendToServer(location);
|
|
413
|
+
await GeoSessionStore.onHeadlessLocation();
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
393
417
|
---
|
|
394
418
|
|
|
395
419
|
## Headless mode explained
|
|
@@ -419,7 +443,7 @@ Upon relaunch, the module detects `UIApplicationLaunchOptionsLocationKey`, resto
|
|
|
419
443
|
- On iOS, use `coarseTracking: true` if ~500m granularity is acceptable — uses cell towers only
|
|
420
444
|
- On Android, increase `updateIntervalMs` (e.g. `10000`) to give FusedLocationProvider room to batch fixes
|
|
421
445
|
- Set `motionActivity: 'automotiveNavigation'` or `'fitness'` so iOS applies activity-specific optimisations
|
|
422
|
-
- Use the `
|
|
446
|
+
- Use the debug overlay (`debug: true`) to measure real-world impact and act on its suggestions
|
|
423
447
|
|
|
424
448
|
---
|
|
425
449
|
|
package/lib/GeoDebugPanel.js
CHANGED
|
@@ -49,9 +49,14 @@ exports.GeoDebugPanel = void 0;
|
|
|
49
49
|
const react_1 = __importStar(require("react"));
|
|
50
50
|
const react_native_1 = require("react-native");
|
|
51
51
|
const index_1 = __importDefault(require("./index"));
|
|
52
|
+
const GeoSessionStore_1 = require("./GeoSessionStore");
|
|
52
53
|
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = react_native_1.Dimensions.get('window');
|
|
53
54
|
const PANEL_WIDTH = Math.round(SCREEN_WIDTH * 0.95);
|
|
54
55
|
const PILL_SIZE = 50;
|
|
56
|
+
// Conservative estimate used when the panel hasn't rendered yet (onLayout not fired)
|
|
57
|
+
const PANEL_ESTIMATED_HEIGHT = 440;
|
|
58
|
+
const PANEL_BOTTOM_MARGIN = 20;
|
|
59
|
+
const PILL_INITIAL_BOTTOM_MARGIN = 120;
|
|
55
60
|
// ─── Formatting helpers ──────────────────────────────────────────────────────
|
|
56
61
|
function formatBattery(level) {
|
|
57
62
|
return level < 0 ? 'N/A' : `${level.toFixed(1)}%`;
|
|
@@ -152,17 +157,35 @@ function makePanResponder(pan, lastPos, maxWidth, maxHeight, onTap) {
|
|
|
152
157
|
});
|
|
153
158
|
}
|
|
154
159
|
// ─── Component ───────────────────────────────────────────────────────────────
|
|
160
|
+
function formatStartedAt(ts) {
|
|
161
|
+
if (!ts)
|
|
162
|
+
return '—';
|
|
163
|
+
const d = new Date(ts);
|
|
164
|
+
return d.toLocaleString(undefined, {
|
|
165
|
+
month: 'short', day: 'numeric',
|
|
166
|
+
hour: '2-digit', minute: '2-digit',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
155
169
|
const GeoDebugPanel = ({ pollInterval = 30000 }) => {
|
|
156
|
-
var _a, _b, _c, _d, _e
|
|
170
|
+
var _a, _b, _c, _d, _e;
|
|
157
171
|
const [info, setInfo] = (0, react_1.useState)(null);
|
|
158
172
|
const [minimized, setMinimized] = (0, react_1.useState)(true);
|
|
159
|
-
const
|
|
160
|
-
|
|
173
|
+
const [storeData, setStoreData] = (0, react_1.useState)({
|
|
174
|
+
accumulated: { updateCount: 0, elapsedSeconds: 0, gpsActiveSeconds: 0, drain: 0 },
|
|
175
|
+
lastSnapshot: null,
|
|
176
|
+
trackingStartedAt: null,
|
|
177
|
+
});
|
|
178
|
+
// Live count updated on every location event so Geopoints doesn't wait for the poll
|
|
179
|
+
const [realtimeCount, setRealtimeCount] = (0, react_1.useState)(0);
|
|
180
|
+
const initialY = SCREEN_HEIGHT - PANEL_ESTIMATED_HEIGHT - PILL_INITIAL_BOTTOM_MARGIN;
|
|
181
|
+
const pan = (0, react_1.useRef)(new react_native_1.Animated.ValueXY({ x: 8, y: initialY })).current;
|
|
182
|
+
const lastPos = (0, react_1.useRef)({ x: 8, y: initialY });
|
|
161
183
|
const panelHeight = (0, react_1.useRef)(0);
|
|
162
184
|
const pillPanResponder = (0, react_1.useRef)(makePanResponder(pan, lastPos, PILL_SIZE, PILL_SIZE, () => {
|
|
185
|
+
const expandedHeight = panelHeight.current > 0 ? panelHeight.current : PANEL_ESTIMATED_HEIGHT;
|
|
163
186
|
const clamped = {
|
|
164
187
|
x: Math.min(lastPos.current.x, SCREEN_WIDTH - PANEL_WIDTH),
|
|
165
|
-
y: Math.min(lastPos.current.y, SCREEN_HEIGHT -
|
|
188
|
+
y: Math.min(lastPos.current.y, SCREEN_HEIGHT - expandedHeight - PANEL_BOTTOM_MARGIN),
|
|
166
189
|
};
|
|
167
190
|
lastPos.current = clamped;
|
|
168
191
|
pan.setValue(clamped);
|
|
@@ -188,13 +211,40 @@ const GeoDebugPanel = ({ pollInterval = 30000 }) => {
|
|
|
188
211
|
try {
|
|
189
212
|
const data = yield index_1.default.getBatteryInfo();
|
|
190
213
|
setInfo(data);
|
|
214
|
+
// Persist snapshot and reload accumulated so totals stay up to date
|
|
215
|
+
yield GeoSessionStore_1.GeoSessionStore.saveSnapshot(data);
|
|
216
|
+
const store = yield GeoSessionStore_1.GeoSessionStore.load();
|
|
217
|
+
setStoreData(store);
|
|
191
218
|
}
|
|
192
219
|
catch (_) { }
|
|
193
220
|
}), []);
|
|
221
|
+
const handleReset = (0, react_1.useCallback)(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
222
|
+
yield GeoSessionStore_1.GeoSessionStore.clear();
|
|
223
|
+
setStoreData({ accumulated: { updateCount: 0, elapsedSeconds: 0, gpsActiveSeconds: 0, drain: 0 }, lastSnapshot: null, trackingStartedAt: null });
|
|
224
|
+
setRealtimeCount(0);
|
|
225
|
+
}), []);
|
|
194
226
|
(0, react_1.useEffect)(() => {
|
|
227
|
+
// Load accumulated history on mount
|
|
228
|
+
GeoSessionStore_1.GeoSessionStore.load().then(setStoreData).catch(() => { });
|
|
195
229
|
refresh();
|
|
196
230
|
const id = setInterval(refresh, pollInterval);
|
|
197
|
-
|
|
231
|
+
// Real-time Geopoints counter — increments on every location event without waiting for the poll
|
|
232
|
+
const locationSub = index_1.default.onLocation(() => {
|
|
233
|
+
setRealtimeCount(c => c + 1);
|
|
234
|
+
});
|
|
235
|
+
// Save snapshot when app is sent to background so stats survive unexpected kills
|
|
236
|
+
const appStateSub = react_native_1.AppState.addEventListener('change', status => {
|
|
237
|
+
if (status === 'background' || status === 'inactive') {
|
|
238
|
+
index_1.default.getBatteryInfo()
|
|
239
|
+
.then(GeoSessionStore_1.GeoSessionStore.saveSnapshot)
|
|
240
|
+
.catch(() => { });
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
return () => {
|
|
244
|
+
clearInterval(id);
|
|
245
|
+
locationSub.remove();
|
|
246
|
+
appStateSub.remove();
|
|
247
|
+
};
|
|
198
248
|
}, [refresh, pollInterval]);
|
|
199
249
|
// ── Minimized pill ──
|
|
200
250
|
if (minimized) {
|
|
@@ -203,16 +253,25 @@ const GeoDebugPanel = ({ pollInterval = 30000 }) => {
|
|
|
203
253
|
</react_native_1.Animated.View>);
|
|
204
254
|
}
|
|
205
255
|
// Safely normalise — native side returns undefined for new fields until rebuilt
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const
|
|
256
|
+
const sessionElapsed = (_a = info === null || info === void 0 ? void 0 : info.trackingElapsedSeconds) !== null && _a !== void 0 ? _a : 0;
|
|
257
|
+
const sessionGpsActive = (_b = info === null || info === void 0 ? void 0 : info.gpsActiveSeconds) !== null && _b !== void 0 ? _b : 0;
|
|
258
|
+
const sessionDrain = (_c = info === null || info === void 0 ? void 0 : info.drainSinceStart) !== null && _c !== void 0 ? _c : 0;
|
|
259
|
+
const level = (_d = info === null || info === void 0 ? void 0 : info.level) !== null && _d !== void 0 ? _d : -1;
|
|
260
|
+
// Use the higher of the native poll count vs the live subscription count
|
|
261
|
+
// so we never show a number lower than what the native side knows about
|
|
262
|
+
const sessionUpdates = Math.max((_e = info === null || info === void 0 ? void 0 : info.updateCount) !== null && _e !== void 0 ? _e : 0, realtimeCount);
|
|
263
|
+
// Combine current session with accumulated history from previous sessions
|
|
264
|
+
const acc = storeData.accumulated;
|
|
265
|
+
const elapsed = acc.elapsedSeconds + sessionElapsed;
|
|
266
|
+
const updates = acc.updateCount + sessionUpdates;
|
|
267
|
+
const gpsActive = acc.gpsActiveSeconds + sessionGpsActive;
|
|
268
|
+
const drain = acc.drain + sessionDrain;
|
|
269
|
+
const upm = elapsed > 0 ? updates / (elapsed / 60) : 0;
|
|
270
|
+
const drainRate = elapsed > 0 ? drain / (elapsed / 3600) : 0;
|
|
213
271
|
const safeInfo = info
|
|
214
272
|
? Object.assign(Object.assign({}, info), { trackingElapsedSeconds: elapsed, updateCount: updates, updatesPerMinute: upm, gpsActiveSeconds: gpsActive, drainRatePerHour: drainRate }) : null;
|
|
215
273
|
const suggestions = safeInfo ? getSuggestions(safeInfo) : [];
|
|
274
|
+
const startedAt = formatStartedAt(storeData.trackingStartedAt);
|
|
216
275
|
// ── Expanded panel ──
|
|
217
276
|
return (<react_native_1.Animated.View style={[styles.container, { transform: pan.getTranslateTransform() }]} onLayout={e => { panelHeight.current = e.nativeEvent.layout.height; }}>
|
|
218
277
|
{/* Drag header */}
|
|
@@ -231,8 +290,9 @@ const GeoDebugPanel = ({ pollInterval = 30000 }) => {
|
|
|
231
290
|
{info ? (<>
|
|
232
291
|
{/* ── Metrics grid ── */}
|
|
233
292
|
<react_native_1.View style={styles.grid}>
|
|
234
|
-
<MetricBox label="
|
|
235
|
-
<MetricBox label="
|
|
293
|
+
<MetricBox label="Started" value={startedAt} desc="first session began"/>
|
|
294
|
+
<MetricBox label="Tracking for" value={elapsed > 0 ? formatElapsed(elapsed) : '—'} desc="cumulative duration"/>
|
|
295
|
+
<MetricBox label="Geopoints" value={`${updates}`} desc="total locations received"/>
|
|
236
296
|
<MetricBox label="Updates/min" value={upm > 0 ? upm.toFixed(1) : '—'} desc="higher = more battery"/>
|
|
237
297
|
<MetricBox label="GPS active" value={elapsed > 0 ? formatGpsPercent(gpsActive, elapsed) : '—'} desc="lower = adaptive saving"/>
|
|
238
298
|
<MetricBox label="Battery now" value={formatBattery(level)} desc={info.isCharging ? '⚡ charging' : 'not charging'}/>
|
|
@@ -248,6 +308,13 @@ const GeoDebugPanel = ({ pollInterval = 30000 }) => {
|
|
|
248
308
|
<react_native_1.Text style={[styles.suggestionText, { color: s.color }]}>{s.text}</react_native_1.Text>
|
|
249
309
|
</react_native_1.View>))}
|
|
250
310
|
</>) : (<react_native_1.Text style={styles.label}>Waiting for data…</react_native_1.Text>)}
|
|
311
|
+
|
|
312
|
+
{/* ── Reset button ── */}
|
|
313
|
+
<react_native_1.View style={styles.resetRow}>
|
|
314
|
+
<react_native_1.TouchableOpacity onPress={handleReset} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
|
|
315
|
+
<react_native_1.Text style={styles.resetBtn}>↺ Reset stats</react_native_1.Text>
|
|
316
|
+
</react_native_1.TouchableOpacity>
|
|
317
|
+
</react_native_1.View>
|
|
251
318
|
</react_native_1.View>
|
|
252
319
|
</react_native_1.Animated.View>);
|
|
253
320
|
};
|
|
@@ -298,7 +365,7 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
298
365
|
minWidth: '30%', flex: 1,
|
|
299
366
|
alignItems: 'center',
|
|
300
367
|
},
|
|
301
|
-
metricValue: { color: '#fff', fontWeight: 'bold', fontSize: 15 },
|
|
368
|
+
metricValue: { color: '#fff', fontWeight: 'bold', fontSize: 15, textAlign: 'center' },
|
|
302
369
|
metricLabel: { color: 'rgba(255,255,255,0.5)', fontSize: 9, marginTop: 2, textAlign: 'center' },
|
|
303
370
|
metricSub: { color: 'rgba(255,255,255,0.3)', fontSize: 8, textAlign: 'center' },
|
|
304
371
|
// Suggestions
|
|
@@ -307,6 +374,9 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
307
374
|
suggestion: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: 5, gap: 6 },
|
|
308
375
|
suggestionEmoji: { fontSize: 12, lineHeight: 16 },
|
|
309
376
|
suggestionText: { fontSize: 11, lineHeight: 16, flex: 1 },
|
|
377
|
+
// Reset
|
|
378
|
+
resetRow: { alignItems: 'flex-end', marginTop: 10 },
|
|
379
|
+
resetBtn: { color: 'rgba(255,255,255,0.45)', fontSize: 13, paddingVertical: 6, paddingHorizontal: 10 },
|
|
310
380
|
// Minimized pill
|
|
311
381
|
pill: {
|
|
312
382
|
position: 'absolute',
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GeoSessionStore — persists debug panel metrics across app sessions.
|
|
3
|
+
*
|
|
4
|
+
* Metrics live in-memory on the native side and reset whenever tracking
|
|
5
|
+
* restarts (app killed, OS killed the service, device rebooted). This store
|
|
6
|
+
* snapshots values to AsyncStorage so the debug panel can show cumulative
|
|
7
|
+
* totals spanning multiple sessions.
|
|
8
|
+
*
|
|
9
|
+
* Storage key: @rn_geo_service/debug_session
|
|
10
|
+
*
|
|
11
|
+
* Schema:
|
|
12
|
+
* accumulated — sum of all fully-closed previous sessions
|
|
13
|
+
* lastSnapshot — most recent known state; used to detect session boundaries
|
|
14
|
+
* trackingStartedAt — Unix ms timestamp of the very first start() ever recorded
|
|
15
|
+
* (not overwritten until clear() is called)
|
|
16
|
+
*/
|
|
17
|
+
import type { BatteryInfo } from './types';
|
|
18
|
+
export interface AccumulatedStats {
|
|
19
|
+
updateCount: number;
|
|
20
|
+
elapsedSeconds: number;
|
|
21
|
+
gpsActiveSeconds: number;
|
|
22
|
+
drain: number;
|
|
23
|
+
}
|
|
24
|
+
export interface SnapshotStats extends AccumulatedStats {
|
|
25
|
+
batteryLevelAtStart: number;
|
|
26
|
+
}
|
|
27
|
+
export interface StoreData {
|
|
28
|
+
accumulated: AccumulatedStats;
|
|
29
|
+
lastSnapshot: SnapshotStats | null;
|
|
30
|
+
/** Unix ms — when the very first session began. Null until first saveSnapshot(). */
|
|
31
|
+
trackingStartedAt: number | null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Load accumulated stats + trackingStartedAt from storage.
|
|
35
|
+
* Returns zero defaults if nothing has been stored yet.
|
|
36
|
+
*/
|
|
37
|
+
declare function load(): Promise<StoreData>;
|
|
38
|
+
/**
|
|
39
|
+
* Snapshot the current session's BatteryInfo into storage.
|
|
40
|
+
*
|
|
41
|
+
* - Detects session boundaries via batteryLevelAtStart:
|
|
42
|
+
* if it differs from the stored lastSnapshot, the previous session is
|
|
43
|
+
* archived into accumulated before storing the new snapshot.
|
|
44
|
+
* - Records trackingStartedAt on the very first call (never overwritten).
|
|
45
|
+
*/
|
|
46
|
+
declare function saveSnapshot(info: BatteryInfo): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Called from the Android headless task when the app is killed but the
|
|
49
|
+
* foreground service is still delivering location updates.
|
|
50
|
+
* Increments the updateCount in lastSnapshot by 1.
|
|
51
|
+
* Safe to call from a HeadlessJS context (no native bridge required).
|
|
52
|
+
*/
|
|
53
|
+
declare function onHeadlessLocation(): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Clear all accumulated data, lastSnapshot, and trackingStartedAt.
|
|
56
|
+
* The panel reverts to current-session-only view after calling this.
|
|
57
|
+
*/
|
|
58
|
+
declare function clear(): Promise<void>;
|
|
59
|
+
export declare const GeoSessionStore: {
|
|
60
|
+
load: typeof load;
|
|
61
|
+
saveSnapshot: typeof saveSnapshot;
|
|
62
|
+
onHeadlessLocation: typeof onHeadlessLocation;
|
|
63
|
+
clear: typeof clear;
|
|
64
|
+
};
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GeoSessionStore — persists debug panel metrics across app sessions.
|
|
4
|
+
*
|
|
5
|
+
* Metrics live in-memory on the native side and reset whenever tracking
|
|
6
|
+
* restarts (app killed, OS killed the service, device rebooted). This store
|
|
7
|
+
* snapshots values to AsyncStorage so the debug panel can show cumulative
|
|
8
|
+
* totals spanning multiple sessions.
|
|
9
|
+
*
|
|
10
|
+
* Storage key: @rn_geo_service/debug_session
|
|
11
|
+
*
|
|
12
|
+
* Schema:
|
|
13
|
+
* accumulated — sum of all fully-closed previous sessions
|
|
14
|
+
* lastSnapshot — most recent known state; used to detect session boundaries
|
|
15
|
+
* trackingStartedAt — Unix ms timestamp of the very first start() ever recorded
|
|
16
|
+
* (not overwritten until clear() is called)
|
|
17
|
+
*/
|
|
18
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
19
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
20
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
21
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
22
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
23
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
24
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.GeoSessionStore = void 0;
|
|
29
|
+
const STORAGE_KEY = '@rn_geo_service/debug_session';
|
|
30
|
+
// Lazily required so the package doesn't hard-crash if AsyncStorage is not
|
|
31
|
+
// installed in the host app (it's a peerDependency).
|
|
32
|
+
function getStorage() {
|
|
33
|
+
try {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
35
|
+
return require('@react-native-async-storage/async-storage').default;
|
|
36
|
+
}
|
|
37
|
+
catch (_) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const ZERO_ACCUMULATED = {
|
|
42
|
+
updateCount: 0,
|
|
43
|
+
elapsedSeconds: 0,
|
|
44
|
+
gpsActiveSeconds: 0,
|
|
45
|
+
drain: 0,
|
|
46
|
+
};
|
|
47
|
+
function addStats(a, b) {
|
|
48
|
+
return {
|
|
49
|
+
updateCount: a.updateCount + b.updateCount,
|
|
50
|
+
elapsedSeconds: a.elapsedSeconds + b.elapsedSeconds,
|
|
51
|
+
gpsActiveSeconds: a.gpsActiveSeconds + b.gpsActiveSeconds,
|
|
52
|
+
drain: a.drain + b.drain,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function readRaw() {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
const storage = getStorage();
|
|
58
|
+
if (!storage)
|
|
59
|
+
return { accumulated: Object.assign({}, ZERO_ACCUMULATED), lastSnapshot: null, trackingStartedAt: null };
|
|
60
|
+
try {
|
|
61
|
+
const raw = yield storage.getItem(STORAGE_KEY);
|
|
62
|
+
if (!raw)
|
|
63
|
+
return { accumulated: Object.assign({}, ZERO_ACCUMULATED), lastSnapshot: null, trackingStartedAt: null };
|
|
64
|
+
return JSON.parse(raw);
|
|
65
|
+
}
|
|
66
|
+
catch (_) {
|
|
67
|
+
return { accumulated: Object.assign({}, ZERO_ACCUMULATED), lastSnapshot: null, trackingStartedAt: null };
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function writeRaw(data) {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
const storage = getStorage();
|
|
74
|
+
if (!storage)
|
|
75
|
+
return;
|
|
76
|
+
try {
|
|
77
|
+
yield storage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
78
|
+
}
|
|
79
|
+
catch (_) { }
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Load accumulated stats + trackingStartedAt from storage.
|
|
84
|
+
* Returns zero defaults if nothing has been stored yet.
|
|
85
|
+
*/
|
|
86
|
+
function load() {
|
|
87
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
return readRaw();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Snapshot the current session's BatteryInfo into storage.
|
|
93
|
+
*
|
|
94
|
+
* - Detects session boundaries via batteryLevelAtStart:
|
|
95
|
+
* if it differs from the stored lastSnapshot, the previous session is
|
|
96
|
+
* archived into accumulated before storing the new snapshot.
|
|
97
|
+
* - Records trackingStartedAt on the very first call (never overwritten).
|
|
98
|
+
*/
|
|
99
|
+
function saveSnapshot(info) {
|
|
100
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
var _a, _b, _c, _d, _e;
|
|
102
|
+
const data = yield readRaw();
|
|
103
|
+
const newSnapshot = {
|
|
104
|
+
updateCount: (_a = info.updateCount) !== null && _a !== void 0 ? _a : 0,
|
|
105
|
+
elapsedSeconds: (_b = info.trackingElapsedSeconds) !== null && _b !== void 0 ? _b : 0,
|
|
106
|
+
gpsActiveSeconds: (_c = info.gpsActiveSeconds) !== null && _c !== void 0 ? _c : 0,
|
|
107
|
+
drain: (_d = info.drainSinceStart) !== null && _d !== void 0 ? _d : 0,
|
|
108
|
+
batteryLevelAtStart: (_e = info.levelAtStart) !== null && _e !== void 0 ? _e : -1,
|
|
109
|
+
};
|
|
110
|
+
let { accumulated, trackingStartedAt } = data;
|
|
111
|
+
// Detect session boundary — a different batteryLevelAtStart means start() was called again
|
|
112
|
+
if (data.lastSnapshot !== null &&
|
|
113
|
+
data.lastSnapshot.batteryLevelAtStart !== newSnapshot.batteryLevelAtStart) {
|
|
114
|
+
accumulated = addStats(accumulated, data.lastSnapshot);
|
|
115
|
+
}
|
|
116
|
+
// Record start time once — on the very first snapshot ever, or after a clear()
|
|
117
|
+
if (trackingStartedAt === null) {
|
|
118
|
+
trackingStartedAt = Date.now();
|
|
119
|
+
}
|
|
120
|
+
yield writeRaw({ accumulated, lastSnapshot: newSnapshot, trackingStartedAt });
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Called from the Android headless task when the app is killed but the
|
|
125
|
+
* foreground service is still delivering location updates.
|
|
126
|
+
* Increments the updateCount in lastSnapshot by 1.
|
|
127
|
+
* Safe to call from a HeadlessJS context (no native bridge required).
|
|
128
|
+
*/
|
|
129
|
+
function onHeadlessLocation() {
|
|
130
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
var _a;
|
|
132
|
+
const data = yield readRaw();
|
|
133
|
+
const snap = (_a = data.lastSnapshot) !== null && _a !== void 0 ? _a : {
|
|
134
|
+
updateCount: 0,
|
|
135
|
+
elapsedSeconds: 0,
|
|
136
|
+
gpsActiveSeconds: 0,
|
|
137
|
+
drain: 0,
|
|
138
|
+
batteryLevelAtStart: -1,
|
|
139
|
+
};
|
|
140
|
+
yield writeRaw(Object.assign(Object.assign({}, data), { lastSnapshot: Object.assign(Object.assign({}, snap), { updateCount: snap.updateCount + 1 }) }));
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Clear all accumulated data, lastSnapshot, and trackingStartedAt.
|
|
145
|
+
* The panel reverts to current-session-only view after calling this.
|
|
146
|
+
*/
|
|
147
|
+
function clear() {
|
|
148
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
const storage = getStorage();
|
|
150
|
+
if (!storage)
|
|
151
|
+
return;
|
|
152
|
+
try {
|
|
153
|
+
yield storage.removeItem(STORAGE_KEY);
|
|
154
|
+
}
|
|
155
|
+
catch (_) { }
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
exports.GeoSessionStore = { load, saveSnapshot, onHeadlessLocation, clear };
|
package/lib/index.d.ts
CHANGED
|
@@ -4,9 +4,6 @@ export declare function _isDebugMode(): boolean;
|
|
|
4
4
|
/**
|
|
5
5
|
* Configure the geo service. Call this before start().
|
|
6
6
|
* Safe to call multiple times; subsequent calls update the config.
|
|
7
|
-
*
|
|
8
|
-
* When debug: true, a draggable debug overlay is mounted automatically —
|
|
9
|
-
* no component needs to be added to the app.
|
|
10
7
|
*/
|
|
11
8
|
declare function configure(config: GeoServiceConfig): Promise<void>;
|
|
12
9
|
/**
|
|
@@ -89,3 +86,5 @@ declare const RNGeoService: {
|
|
|
89
86
|
export default RNGeoService;
|
|
90
87
|
export { GeoDebugPanel } from './GeoDebugPanel';
|
|
91
88
|
export { GeoDebugOverlay } from './GeoDebugOverlay';
|
|
89
|
+
export { GeoSessionStore } from './GeoSessionStore';
|
|
90
|
+
export type { AccumulatedStats, StoreData } from './GeoSessionStore';
|
package/lib/index.js
CHANGED
|
@@ -23,7 +23,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
23
23
|
});
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.GeoDebugOverlay = exports.GeoDebugPanel = void 0;
|
|
26
|
+
exports.GeoSessionStore = exports.GeoDebugOverlay = exports.GeoDebugPanel = void 0;
|
|
27
27
|
exports._isDebugMode = _isDebugMode;
|
|
28
28
|
const react_native_1 = require("react-native");
|
|
29
29
|
__exportStar(require("./types"), exports);
|
|
@@ -61,28 +61,13 @@ function _isDebugMode() { return _debugMode; }
|
|
|
61
61
|
/**
|
|
62
62
|
* Configure the geo service. Call this before start().
|
|
63
63
|
* Safe to call multiple times; subsequent calls update the config.
|
|
64
|
-
*
|
|
65
|
-
* When debug: true, a draggable debug overlay is mounted automatically —
|
|
66
|
-
* no component needs to be added to the app.
|
|
67
64
|
*/
|
|
68
65
|
function configure(config) {
|
|
69
66
|
return __awaiter(this, void 0, void 0, function* () {
|
|
70
67
|
var _a;
|
|
71
|
-
const wasDebug = _debugMode;
|
|
72
68
|
_debugMode = (_a = config.debug) !== null && _a !== void 0 ? _a : false;
|
|
73
69
|
const merged = Object.assign(Object.assign({}, DEFAULT_CONFIG), config);
|
|
74
|
-
|
|
75
|
-
// Lazy require to avoid circular dependency at module init time.
|
|
76
|
-
// autoDebug → GeoDebugOverlay → GeoDebugPanel → index (all lazy references).
|
|
77
|
-
if (_debugMode && !wasDebug) {
|
|
78
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
79
|
-
require('./autoDebug').mountDebugOverlay();
|
|
80
|
-
}
|
|
81
|
-
else if (!_debugMode && wasDebug) {
|
|
82
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
83
|
-
require('./autoDebug').unmountDebugOverlay();
|
|
84
|
-
}
|
|
85
|
-
return result;
|
|
70
|
+
return nativeModule.configure(merged);
|
|
86
71
|
});
|
|
87
72
|
}
|
|
88
73
|
/**
|
|
@@ -92,12 +77,7 @@ function configure(config) {
|
|
|
92
77
|
*/
|
|
93
78
|
function start() {
|
|
94
79
|
return __awaiter(this, void 0, void 0, function* () {
|
|
95
|
-
|
|
96
|
-
if (_debugMode) {
|
|
97
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
98
|
-
require('./autoDebug').mountDebugOverlay();
|
|
99
|
-
}
|
|
100
|
-
return result;
|
|
80
|
+
return nativeModule.start();
|
|
101
81
|
});
|
|
102
82
|
}
|
|
103
83
|
/**
|
|
@@ -105,12 +85,7 @@ function start() {
|
|
|
105
85
|
*/
|
|
106
86
|
function stop() {
|
|
107
87
|
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
-
|
|
109
|
-
if (_debugMode) {
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
111
|
-
require('./autoDebug').unmountDebugOverlay();
|
|
112
|
-
}
|
|
113
|
-
return result;
|
|
88
|
+
return nativeModule.stop();
|
|
114
89
|
});
|
|
115
90
|
}
|
|
116
91
|
/**
|
|
@@ -173,7 +148,12 @@ function registerHeadlessTask(handler) {
|
|
|
173
148
|
if (react_native_1.Platform.OS !== 'android')
|
|
174
149
|
return;
|
|
175
150
|
const taskName = DEFAULT_CONFIG.backgroundTaskName;
|
|
176
|
-
react_native_1.AppRegistry.registerHeadlessTask(taskName, () =>
|
|
151
|
+
react_native_1.AppRegistry.registerHeadlessTask(taskName, () => (location) => __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
// Keep GeoSessionStore's updateCount in sync while the app is killed
|
|
153
|
+
const { GeoSessionStore } = require('./GeoSessionStore');
|
|
154
|
+
yield GeoSessionStore.onHeadlessLocation();
|
|
155
|
+
yield handler(location);
|
|
156
|
+
}));
|
|
177
157
|
}
|
|
178
158
|
/**
|
|
179
159
|
* Returns battery information including current level and drain since tracking started.
|
|
@@ -210,3 +190,5 @@ var GeoDebugPanel_1 = require("./GeoDebugPanel");
|
|
|
210
190
|
Object.defineProperty(exports, "GeoDebugPanel", { enumerable: true, get: function () { return GeoDebugPanel_1.GeoDebugPanel; } });
|
|
211
191
|
var GeoDebugOverlay_1 = require("./GeoDebugOverlay");
|
|
212
192
|
Object.defineProperty(exports, "GeoDebugOverlay", { enumerable: true, get: function () { return GeoDebugOverlay_1.GeoDebugOverlay; } });
|
|
193
|
+
var GeoSessionStore_1 = require("./GeoSessionStore");
|
|
194
|
+
Object.defineProperty(exports, "GeoSessionStore", { enumerable: true, get: function () { return GeoSessionStore_1.GeoSessionStore; } });
|
package/lib/types.d.ts
CHANGED
|
@@ -146,7 +146,7 @@ export interface BatteryInfo {
|
|
|
146
146
|
levelAtStart: number;
|
|
147
147
|
/** Percentage points drained since tracking started */
|
|
148
148
|
drainSinceStart: number;
|
|
149
|
-
/** How many location
|
|
149
|
+
/** How many total location have been delivered since start() */
|
|
150
150
|
updateCount: number;
|
|
151
151
|
/** Total seconds since start() was called */
|
|
152
152
|
trackingElapsedSeconds: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsachit/react-native-geo-service",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Battery-efficient background geolocation for React Native with headless support",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -8,9 +8,7 @@
|
|
|
8
8
|
"lib",
|
|
9
9
|
"android",
|
|
10
10
|
"ios",
|
|
11
|
-
"react-native-geo-service.podspec"
|
|
12
|
-
"debug-panel.js",
|
|
13
|
-
"debug-panel.d.ts"
|
|
11
|
+
"react-native-geo-service.podspec"
|
|
14
12
|
],
|
|
15
13
|
"scripts": {
|
|
16
14
|
"build": "tsc",
|
|
@@ -27,7 +25,13 @@
|
|
|
27
25
|
"author": "Sachit Karki",
|
|
28
26
|
"license": "MIT",
|
|
29
27
|
"peerDependencies": {
|
|
30
|
-
"react-native": ">=0.60.0"
|
|
28
|
+
"react-native": ">=0.60.0",
|
|
29
|
+
"@react-native-async-storage/async-storage": ">=1.0.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"@react-native-async-storage/async-storage": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
31
35
|
},
|
|
32
36
|
"devDependencies": {
|
|
33
37
|
"@types/react": "^18.3.28",
|
|
@@ -41,8 +45,5 @@
|
|
|
41
45
|
"bugs": {
|
|
42
46
|
"url": "https://github.com/tsachit/react-native-geo-service/issues"
|
|
43
47
|
},
|
|
44
|
-
"homepage": "https://github.com/tsachit/react-native-geo-service#readme"
|
|
45
|
-
"dependencies": {
|
|
46
|
-
"react-native-root-siblings": "^5.0.1"
|
|
47
|
-
}
|
|
48
|
+
"homepage": "https://github.com/tsachit/react-native-geo-service#readme"
|
|
48
49
|
}
|
package/debug-panel.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/debug-panel.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
require('./lib/setup');
|
package/lib/autoDebug.d.ts
DELETED
package/lib/autoDebug.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.mountDebugOverlay = mountDebugOverlay;
|
|
7
|
-
exports.unmountDebugOverlay = unmountDebugOverlay;
|
|
8
|
-
const react_1 = __importDefault(require("react"));
|
|
9
|
-
const react_native_root_siblings_1 = __importDefault(require("react-native-root-siblings"));
|
|
10
|
-
const GeoDebugOverlay_1 = require("./GeoDebugOverlay");
|
|
11
|
-
let sibling = null;
|
|
12
|
-
function mountDebugOverlay() {
|
|
13
|
-
if (!sibling) {
|
|
14
|
-
sibling = new react_native_root_siblings_1.default(react_1.default.createElement(GeoDebugOverlay_1.GeoDebugOverlay));
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function unmountDebugOverlay() {
|
|
18
|
-
if (sibling) {
|
|
19
|
-
sibling.destroy();
|
|
20
|
-
sibling = null;
|
|
21
|
-
}
|
|
22
|
-
}
|
package/lib/setup.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/lib/setup.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
/**
|
|
4
|
-
* Import this file ONCE at the top of your app's index.js, before
|
|
5
|
-
* AppRegistry.registerComponent. This registers RootSiblingParent as
|
|
6
|
-
* the app wrapper so GeoService can render the debug overlay automatically
|
|
7
|
-
* when configure({ debug: true }) is called — no component needed in the tree.
|
|
8
|
-
*
|
|
9
|
-
* index.js:
|
|
10
|
-
* import '@tsachit/react-native-geo-service/setup'; // ← add this line
|
|
11
|
-
* import { AppRegistry } from 'react-native';
|
|
12
|
-
* import App from './App';
|
|
13
|
-
* AppRegistry.registerComponent('MyApp', () => App);
|
|
14
|
-
*/
|
|
15
|
-
const react_native_1 = require("react-native");
|
|
16
|
-
const react_native_root_siblings_1 = require("react-native-root-siblings");
|
|
17
|
-
react_native_1.AppRegistry.setWrapperComponentProvider(() => react_native_root_siblings_1.RootSiblingParent);
|