@rejourneyco/react-native 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/README.md +29 -0
- package/android/src/main/java/com/rejourney/RejourneyModuleImpl.kt +47 -30
- package/android/src/main/java/com/rejourney/capture/CaptureEngine.kt +25 -1
- package/android/src/main/java/com/rejourney/capture/CaptureHeuristics.kt +70 -32
- package/android/src/main/java/com/rejourney/core/Constants.kt +4 -4
- package/android/src/newarch/java/com/rejourney/RejourneyModule.kt +14 -0
- package/android/src/oldarch/java/com/rejourney/RejourneyModule.kt +9 -0
- package/ios/Capture/RJCaptureEngine.m +72 -34
- package/ios/Capture/RJCaptureHeuristics.h +7 -5
- package/ios/Capture/RJCaptureHeuristics.m +138 -112
- package/ios/Capture/RJVideoEncoder.m +0 -26
- package/ios/Core/Rejourney.mm +64 -102
- package/ios/Utils/RJPerfTiming.m +0 -5
- package/ios/Utils/RJWindowUtils.m +0 -1
- package/lib/commonjs/components/Mask.js +1 -6
- package/lib/commonjs/index.js +12 -101
- package/lib/commonjs/sdk/autoTracking.js +55 -353
- package/lib/commonjs/sdk/constants.js +2 -13
- package/lib/commonjs/sdk/errorTracking.js +1 -29
- package/lib/commonjs/sdk/metricsTracking.js +3 -24
- package/lib/commonjs/sdk/navigation.js +3 -42
- package/lib/commonjs/sdk/networkInterceptor.js +7 -49
- package/lib/commonjs/sdk/utils.js +0 -5
- package/lib/module/components/Mask.js +1 -6
- package/lib/module/index.js +11 -105
- package/lib/module/sdk/autoTracking.js +55 -354
- package/lib/module/sdk/constants.js +2 -13
- package/lib/module/sdk/errorTracking.js +1 -29
- package/lib/module/sdk/index.js +0 -2
- package/lib/module/sdk/metricsTracking.js +3 -24
- package/lib/module/sdk/navigation.js +3 -42
- package/lib/module/sdk/networkInterceptor.js +7 -49
- package/lib/module/sdk/utils.js +0 -5
- package/lib/typescript/NativeRejourney.d.ts +2 -0
- package/lib/typescript/sdk/autoTracking.d.ts +5 -6
- package/lib/typescript/types/index.d.ts +0 -1
- package/package.json +11 -3
- package/src/NativeRejourney.ts +4 -0
- package/src/components/Mask.tsx +0 -3
- package/src/index.ts +11 -88
- package/src/sdk/autoTracking.ts +72 -331
- package/src/sdk/constants.ts +13 -13
- package/src/sdk/errorTracking.ts +1 -17
- package/src/sdk/index.ts +0 -2
- package/src/sdk/metricsTracking.ts +5 -33
- package/src/sdk/navigation.ts +8 -29
- package/src/sdk/networkInterceptor.ts +9 -33
- package/src/sdk/utils.ts +0 -5
- package/src/types/index.ts +0 -29
|
@@ -61,9 +61,6 @@ function getPlatform() {
|
|
|
61
61
|
function getDimensions() {
|
|
62
62
|
return getRN()?.Dimensions;
|
|
63
63
|
}
|
|
64
|
-
function getNativeModules() {
|
|
65
|
-
return getRN()?.NativeModules;
|
|
66
|
-
}
|
|
67
64
|
function getRejourneyNativeModule() {
|
|
68
65
|
const RN = getRN();
|
|
69
66
|
if (!RN) return null;
|
|
@@ -76,7 +73,7 @@ function getRejourneyNativeModule() {
|
|
|
76
73
|
try {
|
|
77
74
|
nativeModule = TurboModuleRegistry.get('Rejourney');
|
|
78
75
|
} catch {
|
|
79
|
-
// Ignore
|
|
76
|
+
// Ignore
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
79
|
if (!nativeModule && NativeModules) {
|
|
@@ -84,57 +81,27 @@ function getRejourneyNativeModule() {
|
|
|
84
81
|
}
|
|
85
82
|
return nativeModule;
|
|
86
83
|
}
|
|
87
|
-
|
|
88
|
-
// Type declarations for browser globals (only used in hybrid apps where DOM is available)
|
|
89
|
-
// These don't exist in pure React Native but are needed for error tracking in hybrid scenarios
|
|
90
|
-
|
|
91
|
-
// Cast globalThis to work with both RN and hybrid scenarios
|
|
92
84
|
const _globalThis = globalThis;
|
|
93
|
-
|
|
94
|
-
// =============================================================================
|
|
95
|
-
// Types
|
|
96
|
-
// =============================================================================
|
|
97
|
-
|
|
98
|
-
// =============================================================================
|
|
99
|
-
// State
|
|
100
|
-
// =============================================================================
|
|
101
|
-
|
|
102
85
|
let isInitialized = false;
|
|
103
86
|
let config = {};
|
|
104
|
-
|
|
105
|
-
// Rage tap tracking
|
|
106
87
|
const recentTaps = [];
|
|
107
|
-
let tapHead = 0;
|
|
108
|
-
let tapCount = 0;
|
|
88
|
+
let tapHead = 0;
|
|
89
|
+
let tapCount = 0;
|
|
109
90
|
const MAX_RECENT_TAPS = 10;
|
|
110
|
-
|
|
111
|
-
// Session metrics
|
|
112
91
|
let metrics = createEmptyMetrics();
|
|
113
92
|
let sessionStartTime = 0;
|
|
114
93
|
let maxSessionDurationMs = 10 * 60 * 1000;
|
|
115
|
-
|
|
116
|
-
// Screen tracking
|
|
117
94
|
let currentScreen = '';
|
|
118
95
|
let screensVisited = [];
|
|
119
|
-
|
|
120
|
-
// Anonymous ID
|
|
121
96
|
let anonymousId = null;
|
|
122
97
|
let anonymousIdPromise = null;
|
|
123
|
-
|
|
124
|
-
// Callbacks
|
|
125
98
|
let onRageTapDetected = null;
|
|
126
99
|
let onErrorCaptured = null;
|
|
127
100
|
let onScreenChange = null;
|
|
128
|
-
|
|
129
|
-
// Original error handlers (for restoration)
|
|
130
101
|
let originalErrorHandler;
|
|
131
102
|
let originalOnError = null;
|
|
132
103
|
let originalOnUnhandledRejection = null;
|
|
133
104
|
|
|
134
|
-
// =============================================================================
|
|
135
|
-
// Initialization
|
|
136
|
-
// =============================================================================
|
|
137
|
-
|
|
138
105
|
/**
|
|
139
106
|
* Initialize auto tracking features
|
|
140
107
|
* Called automatically by Rejourney.init() - no user action needed
|
|
@@ -152,26 +119,16 @@ function initAutoTracking(trackingConfig, callbacks = {}) {
|
|
|
152
119
|
maxSessionDurationMs: trackingConfig.maxSessionDurationMs,
|
|
153
120
|
...trackingConfig
|
|
154
121
|
};
|
|
155
|
-
|
|
156
|
-
// Session timing
|
|
157
122
|
sessionStartTime = Date.now();
|
|
158
123
|
setMaxSessionDurationMinutes(trackingConfig.maxSessionDurationMs ? trackingConfig.maxSessionDurationMs / 60000 : undefined);
|
|
159
|
-
|
|
160
|
-
// Set callbacks
|
|
161
124
|
onRageTapDetected = callbacks.onRageTap || null;
|
|
162
125
|
onErrorCaptured = callbacks.onError || null;
|
|
163
126
|
onScreenChange = callbacks.onScreen || null;
|
|
164
|
-
|
|
165
|
-
// Initialize metrics
|
|
166
|
-
metrics = createEmptyMetrics();
|
|
167
|
-
sessionStartTime = Date.now();
|
|
168
|
-
anonymousId = generateAnonymousId();
|
|
169
|
-
|
|
170
|
-
// Setup error tracking
|
|
171
127
|
setupErrorTracking();
|
|
172
|
-
|
|
173
|
-
// Setup React Navigation tracking (if available)
|
|
174
128
|
setupNavigationTracking();
|
|
129
|
+
loadAnonymousId().then(id => {
|
|
130
|
+
anonymousId = id;
|
|
131
|
+
});
|
|
175
132
|
isInitialized = true;
|
|
176
133
|
}
|
|
177
134
|
|
|
@@ -180,11 +137,7 @@ function initAutoTracking(trackingConfig, callbacks = {}) {
|
|
|
180
137
|
*/
|
|
181
138
|
function cleanupAutoTracking() {
|
|
182
139
|
if (!isInitialized) return;
|
|
183
|
-
|
|
184
|
-
// Restore original error handlers
|
|
185
140
|
restoreErrorHandlers();
|
|
186
|
-
|
|
187
|
-
// Cleanup navigation tracking
|
|
188
141
|
cleanupNavigationTracking();
|
|
189
142
|
|
|
190
143
|
// Reset state
|
|
@@ -198,10 +151,6 @@ function cleanupAutoTracking() {
|
|
|
198
151
|
isInitialized = false;
|
|
199
152
|
}
|
|
200
153
|
|
|
201
|
-
// =============================================================================
|
|
202
|
-
// Rage Tap Detection
|
|
203
|
-
// =============================================================================
|
|
204
|
-
|
|
205
154
|
/**
|
|
206
155
|
* Track a tap event for rage tap detection
|
|
207
156
|
* Called automatically from touch interceptor
|
|
@@ -209,8 +158,6 @@ function cleanupAutoTracking() {
|
|
|
209
158
|
function trackTap(tap) {
|
|
210
159
|
if (!isInitialized) return;
|
|
211
160
|
const now = Date.now();
|
|
212
|
-
|
|
213
|
-
// Add to circular buffer (O(1) instead of shift() which is O(n))
|
|
214
161
|
const insertIndex = (tapHead + tapCount) % MAX_RECENT_TAPS;
|
|
215
162
|
if (tapCount < MAX_RECENT_TAPS) {
|
|
216
163
|
recentTaps[insertIndex] = {
|
|
@@ -219,15 +166,12 @@ function trackTap(tap) {
|
|
|
219
166
|
};
|
|
220
167
|
tapCount++;
|
|
221
168
|
} else {
|
|
222
|
-
// Buffer full, overwrite oldest
|
|
223
169
|
recentTaps[tapHead] = {
|
|
224
170
|
...tap,
|
|
225
171
|
timestamp: now
|
|
226
172
|
};
|
|
227
173
|
tapHead = (tapHead + 1) % MAX_RECENT_TAPS;
|
|
228
174
|
}
|
|
229
|
-
|
|
230
|
-
// Evict old taps outside time window
|
|
231
175
|
const windowStart = now - (config.rageTapTimeWindow || 500);
|
|
232
176
|
while (tapCount > 0) {
|
|
233
177
|
const oldestTap = recentTaps[tapHead];
|
|
@@ -238,11 +182,7 @@ function trackTap(tap) {
|
|
|
238
182
|
break;
|
|
239
183
|
}
|
|
240
184
|
}
|
|
241
|
-
|
|
242
|
-
// Check for rage tap
|
|
243
185
|
detectRageTap();
|
|
244
|
-
|
|
245
|
-
// Update metrics
|
|
246
186
|
metrics.touchCount++;
|
|
247
187
|
metrics.totalEvents++;
|
|
248
188
|
}
|
|
@@ -254,14 +194,11 @@ function detectRageTap() {
|
|
|
254
194
|
const threshold = config.rageTapThreshold || 3;
|
|
255
195
|
const radius = config.rageTapRadius || 50;
|
|
256
196
|
if (tapCount < threshold) return;
|
|
257
|
-
// Check last N taps from circular buffer
|
|
258
197
|
const tapsToCheck = [];
|
|
259
198
|
for (let i = 0; i < threshold; i++) {
|
|
260
199
|
const idx = (tapHead + tapCount - threshold + i) % MAX_RECENT_TAPS;
|
|
261
200
|
tapsToCheck.push(recentTaps[idx]);
|
|
262
201
|
}
|
|
263
|
-
|
|
264
|
-
// Calculate center point
|
|
265
202
|
let centerX = 0;
|
|
266
203
|
let centerY = 0;
|
|
267
204
|
for (const tap of tapsToCheck) {
|
|
@@ -270,8 +207,6 @@ function detectRageTap() {
|
|
|
270
207
|
}
|
|
271
208
|
centerX /= tapsToCheck.length;
|
|
272
209
|
centerY /= tapsToCheck.length;
|
|
273
|
-
|
|
274
|
-
// Check if all taps are within radius of center
|
|
275
210
|
let allWithinRadius = true;
|
|
276
211
|
for (const tap of tapsToCheck) {
|
|
277
212
|
const dx = tap.x - centerX;
|
|
@@ -283,9 +218,7 @@ function detectRageTap() {
|
|
|
283
218
|
}
|
|
284
219
|
}
|
|
285
220
|
if (allWithinRadius) {
|
|
286
|
-
// Rage tap detected!
|
|
287
221
|
metrics.rageTapCount++;
|
|
288
|
-
// Clear circular buffer to prevent duplicate detection
|
|
289
222
|
tapHead = 0;
|
|
290
223
|
tapCount = 0;
|
|
291
224
|
|
|
@@ -296,10 +229,6 @@ function detectRageTap() {
|
|
|
296
229
|
}
|
|
297
230
|
}
|
|
298
231
|
|
|
299
|
-
// =============================================================================
|
|
300
|
-
// State Change Notification
|
|
301
|
-
// =============================================================================
|
|
302
|
-
|
|
303
232
|
/**
|
|
304
233
|
* Notify that a state change occurred (navigation, modal, etc.)
|
|
305
234
|
* Kept for API compatibility
|
|
@@ -308,25 +237,16 @@ function notifyStateChange() {
|
|
|
308
237
|
// No-op - kept for backward compatibility
|
|
309
238
|
}
|
|
310
239
|
|
|
311
|
-
// =============================================================================
|
|
312
|
-
// Error Tracking
|
|
313
|
-
// =============================================================================
|
|
314
|
-
|
|
315
240
|
/**
|
|
316
241
|
* Setup automatic error tracking
|
|
317
242
|
*/
|
|
318
243
|
function setupErrorTracking() {
|
|
319
|
-
// Track React Native errors
|
|
320
244
|
if (config.trackReactNativeErrors !== false) {
|
|
321
245
|
setupReactNativeErrorHandler();
|
|
322
246
|
}
|
|
323
|
-
|
|
324
|
-
// Track JavaScript errors (only works in web/debug)
|
|
325
247
|
if (config.trackJSErrors !== false && typeof _globalThis !== 'undefined') {
|
|
326
248
|
setupJSErrorHandler();
|
|
327
249
|
}
|
|
328
|
-
|
|
329
|
-
// Track unhandled promise rejections
|
|
330
250
|
if (config.trackPromiseRejections !== false && typeof _globalThis !== 'undefined') {
|
|
331
251
|
setupPromiseRejectionHandler();
|
|
332
252
|
}
|
|
@@ -337,16 +257,10 @@ function setupErrorTracking() {
|
|
|
337
257
|
*/
|
|
338
258
|
function setupReactNativeErrorHandler() {
|
|
339
259
|
try {
|
|
340
|
-
// Access ErrorUtils from global scope
|
|
341
260
|
const ErrorUtils = _globalThis.ErrorUtils;
|
|
342
261
|
if (!ErrorUtils) return;
|
|
343
|
-
|
|
344
|
-
// Store original handler
|
|
345
262
|
originalErrorHandler = ErrorUtils.getGlobalHandler();
|
|
346
|
-
|
|
347
|
-
// Set new handler
|
|
348
263
|
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
349
|
-
// Track the error
|
|
350
264
|
trackError({
|
|
351
265
|
type: 'error',
|
|
352
266
|
timestamp: Date.now(),
|
|
@@ -354,14 +268,12 @@ function setupReactNativeErrorHandler() {
|
|
|
354
268
|
stack: error.stack,
|
|
355
269
|
name: error.name || 'Error'
|
|
356
270
|
});
|
|
357
|
-
|
|
358
|
-
// Call original handler
|
|
359
271
|
if (originalErrorHandler) {
|
|
360
272
|
originalErrorHandler(error, isFatal);
|
|
361
273
|
}
|
|
362
274
|
});
|
|
363
275
|
} catch {
|
|
364
|
-
//
|
|
276
|
+
// Ignore
|
|
365
277
|
}
|
|
366
278
|
}
|
|
367
279
|
|
|
@@ -370,8 +282,6 @@ function setupReactNativeErrorHandler() {
|
|
|
370
282
|
*/
|
|
371
283
|
function setupJSErrorHandler() {
|
|
372
284
|
if (typeof _globalThis.onerror !== 'undefined') {
|
|
373
|
-
// Note: In React Native, this typically doesn't fire
|
|
374
|
-
// But we set it up anyway for hybrid apps
|
|
375
285
|
originalOnError = _globalThis.onerror;
|
|
376
286
|
_globalThis.onerror = (message, source, lineno, colno, error) => {
|
|
377
287
|
trackError({
|
|
@@ -381,8 +291,6 @@ function setupJSErrorHandler() {
|
|
|
381
291
|
stack: error?.stack || `${source}:${lineno}:${colno}`,
|
|
382
292
|
name: error?.name || 'Error'
|
|
383
293
|
});
|
|
384
|
-
|
|
385
|
-
// Call original handler
|
|
386
294
|
if (originalOnError) {
|
|
387
295
|
return originalOnError(message, source, lineno, colno, error);
|
|
388
296
|
}
|
|
@@ -415,7 +323,6 @@ function setupPromiseRejectionHandler() {
|
|
|
415
323
|
* Restore original error handlers
|
|
416
324
|
*/
|
|
417
325
|
function restoreErrorHandlers() {
|
|
418
|
-
// Restore React Native handler
|
|
419
326
|
if (originalErrorHandler) {
|
|
420
327
|
try {
|
|
421
328
|
const ErrorUtils = _globalThis.ErrorUtils;
|
|
@@ -427,14 +334,10 @@ function restoreErrorHandlers() {
|
|
|
427
334
|
}
|
|
428
335
|
originalErrorHandler = undefined;
|
|
429
336
|
}
|
|
430
|
-
|
|
431
|
-
// Restore global onerror
|
|
432
337
|
if (originalOnError !== null) {
|
|
433
338
|
_globalThis.onerror = originalOnError;
|
|
434
339
|
originalOnError = null;
|
|
435
340
|
}
|
|
436
|
-
|
|
437
|
-
// Remove promise rejection handler
|
|
438
341
|
if (originalOnUnhandledRejection && typeof _globalThis.removeEventListener !== 'undefined') {
|
|
439
342
|
_globalThis.removeEventListener('unhandledrejection', originalOnUnhandledRejection);
|
|
440
343
|
originalOnUnhandledRejection = null;
|
|
@@ -464,16 +367,10 @@ function captureError(message, stack, name) {
|
|
|
464
367
|
name: name || 'Error'
|
|
465
368
|
});
|
|
466
369
|
}
|
|
467
|
-
|
|
468
|
-
// =============================================================================
|
|
469
|
-
// Screen/Funnel Tracking - Automatic Navigation Detection
|
|
470
|
-
// =============================================================================
|
|
471
|
-
|
|
472
|
-
// Navigation detection state
|
|
473
370
|
let navigationPollingInterval = null;
|
|
474
371
|
let lastDetectedScreen = '';
|
|
475
372
|
let navigationSetupDone = false;
|
|
476
|
-
let navigationPollingErrors = 0;
|
|
373
|
+
let navigationPollingErrors = 0;
|
|
477
374
|
const MAX_POLLING_ERRORS = 10; // Stop polling after 10 consecutive errors
|
|
478
375
|
|
|
479
376
|
/**
|
|
@@ -497,8 +394,6 @@ function trackNavigationState(state) {
|
|
|
497
394
|
const {
|
|
498
395
|
normalizeScreenName
|
|
499
396
|
} = require('./navigation');
|
|
500
|
-
|
|
501
|
-
// Find the active screen recursively
|
|
502
397
|
const findActiveScreen = navState => {
|
|
503
398
|
if (!navState?.routes) return null;
|
|
504
399
|
const index = navState.index ?? navState.routes.length - 1;
|
|
@@ -513,7 +408,7 @@ function trackNavigationState(state) {
|
|
|
513
408
|
trackScreen(screenName);
|
|
514
409
|
}
|
|
515
410
|
} catch {
|
|
516
|
-
//
|
|
411
|
+
// Ignore
|
|
517
412
|
}
|
|
518
413
|
}
|
|
519
414
|
|
|
@@ -543,16 +438,11 @@ function trackNavigationState(state) {
|
|
|
543
438
|
* ```
|
|
544
439
|
*/
|
|
545
440
|
function useNavigationTracking() {
|
|
546
|
-
// Use React's useRef and useCallback to create stable references
|
|
547
441
|
const React = require('react');
|
|
548
442
|
const {
|
|
549
443
|
createNavigationContainerRef
|
|
550
444
|
} = require('@react-navigation/native');
|
|
551
|
-
|
|
552
|
-
// Create a stable navigation ref
|
|
553
445
|
const navigationRef = React.useRef(createNavigationContainerRef());
|
|
554
|
-
|
|
555
|
-
// Track initial screen when navigation is ready
|
|
556
446
|
const onReady = React.useCallback(() => {
|
|
557
447
|
try {
|
|
558
448
|
const currentRoute = navigationRef.current?.getCurrentRoute?.();
|
|
@@ -567,11 +457,9 @@ function useNavigationTracking() {
|
|
|
567
457
|
}
|
|
568
458
|
}
|
|
569
459
|
} catch {
|
|
570
|
-
//
|
|
460
|
+
// Ignore
|
|
571
461
|
}
|
|
572
462
|
}, []);
|
|
573
|
-
|
|
574
|
-
// Return props to spread on NavigationContainer
|
|
575
463
|
return {
|
|
576
464
|
ref: navigationRef.current,
|
|
577
465
|
onReady,
|
|
@@ -607,8 +495,7 @@ function setupNavigationTracking() {
|
|
|
607
495
|
_utils.logger.debug('Expo Router setup: SUCCESS on attempt', attempts);
|
|
608
496
|
}
|
|
609
497
|
} else if (attempts < maxAttempts) {
|
|
610
|
-
|
|
611
|
-
const delay = 200 * attempts; // 200, 400, 600, 800ms
|
|
498
|
+
const delay = 200 * attempts;
|
|
612
499
|
if (__DEV__) {
|
|
613
500
|
_utils.logger.debug('Expo Router not ready, retrying in', delay, 'ms');
|
|
614
501
|
}
|
|
@@ -620,8 +507,6 @@ function setupNavigationTracking() {
|
|
|
620
507
|
}
|
|
621
508
|
}
|
|
622
509
|
};
|
|
623
|
-
|
|
624
|
-
// Start first attempt after 200ms
|
|
625
510
|
setTimeout(trySetup, 200);
|
|
626
511
|
}
|
|
627
512
|
|
|
@@ -647,14 +532,10 @@ function trySetupExpoRouter() {
|
|
|
647
532
|
normalizeScreenName,
|
|
648
533
|
getScreenNameFromPath
|
|
649
534
|
} = require('./navigation');
|
|
650
|
-
|
|
651
|
-
// Poll for route changes (expo-router doesn't expose a listener API outside hooks)
|
|
652
535
|
navigationPollingInterval = setInterval(() => {
|
|
653
536
|
try {
|
|
654
537
|
let state = null;
|
|
655
538
|
let stateSource = '';
|
|
656
|
-
|
|
657
|
-
// Method 1: Public accessors on router object
|
|
658
539
|
if (typeof router.getState === 'function') {
|
|
659
540
|
state = router.getState();
|
|
660
541
|
stateSource = 'router.getState()';
|
|
@@ -662,34 +543,25 @@ function trySetupExpoRouter() {
|
|
|
662
543
|
state = router.rootState;
|
|
663
544
|
stateSource = 'router.rootState';
|
|
664
545
|
}
|
|
665
|
-
|
|
666
|
-
// Method 2: Internal store access (works for both v3 and v6+)
|
|
667
546
|
if (!state) {
|
|
668
547
|
try {
|
|
669
548
|
const storeModule = require('expo-router/build/global-state/router-store');
|
|
670
549
|
if (storeModule?.store) {
|
|
671
|
-
// v6+: store.state or store.navigationRef
|
|
672
550
|
state = storeModule.store.state;
|
|
673
551
|
if (state) stateSource = 'store.state';
|
|
674
|
-
|
|
675
|
-
// v6+: Try navigationRef if state is undefined
|
|
676
552
|
if (!state && storeModule.store.navigationRef?.current) {
|
|
677
553
|
state = storeModule.store.navigationRef.current.getRootState?.();
|
|
678
554
|
if (state) stateSource = 'navigationRef.getRootState()';
|
|
679
555
|
}
|
|
680
|
-
|
|
681
|
-
// v3: store.rootState or store.initialState
|
|
682
556
|
if (!state) {
|
|
683
557
|
state = storeModule.store.rootState || storeModule.store.initialState;
|
|
684
558
|
if (state) stateSource = 'store.rootState/initialState';
|
|
685
559
|
}
|
|
686
560
|
}
|
|
687
561
|
} catch {
|
|
688
|
-
//
|
|
562
|
+
// Ignore
|
|
689
563
|
}
|
|
690
564
|
}
|
|
691
|
-
|
|
692
|
-
// Method 3: Try accessing via a different export path for v6
|
|
693
565
|
if (!state) {
|
|
694
566
|
try {
|
|
695
567
|
const imperative = require('expo-router/build/imperative-api');
|
|
@@ -698,11 +570,11 @@ function trySetupExpoRouter() {
|
|
|
698
570
|
if (state) stateSource = 'imperative-api';
|
|
699
571
|
}
|
|
700
572
|
} catch {
|
|
701
|
-
//
|
|
573
|
+
// Ignore
|
|
702
574
|
}
|
|
703
575
|
}
|
|
704
576
|
if (state) {
|
|
705
|
-
|
|
577
|
+
navigationPollingErrors = 0;
|
|
706
578
|
navigationPollingErrors = 0;
|
|
707
579
|
const screenName = extractScreenNameFromRouterState(state, getScreenNameFromPath, normalizeScreenName);
|
|
708
580
|
if (screenName && screenName !== lastDetectedScreen) {
|
|
@@ -713,21 +585,15 @@ function trySetupExpoRouter() {
|
|
|
713
585
|
trackScreen(screenName);
|
|
714
586
|
}
|
|
715
587
|
} else {
|
|
716
|
-
// Track consecutive failures to get state
|
|
717
588
|
navigationPollingErrors++;
|
|
718
589
|
if (__DEV__ && navigationPollingErrors === 1) {
|
|
719
590
|
_utils.logger.debug('Expo Router: Could not get navigation state');
|
|
720
591
|
}
|
|
721
592
|
if (navigationPollingErrors >= MAX_POLLING_ERRORS) {
|
|
722
|
-
// Stop polling after too many errors to save CPU
|
|
723
|
-
if (__DEV__) {
|
|
724
|
-
_utils.logger.debug('Expo Router: Stopped polling after', MAX_POLLING_ERRORS, 'errors');
|
|
725
|
-
}
|
|
726
593
|
cleanupNavigationTracking();
|
|
727
594
|
}
|
|
728
595
|
}
|
|
729
596
|
} catch (e) {
|
|
730
|
-
// Error - track and potentially stop
|
|
731
597
|
navigationPollingErrors++;
|
|
732
598
|
if (__DEV__ && navigationPollingErrors === 1) {
|
|
733
599
|
_utils.logger.debug('Expo Router polling error:', e);
|
|
@@ -736,14 +602,12 @@ function trySetupExpoRouter() {
|
|
|
736
602
|
cleanupNavigationTracking();
|
|
737
603
|
}
|
|
738
604
|
}
|
|
739
|
-
}, 500);
|
|
740
|
-
|
|
605
|
+
}, 500);
|
|
741
606
|
return true;
|
|
742
607
|
} catch (e) {
|
|
743
608
|
if (__DEV__) {
|
|
744
609
|
_utils.logger.debug('Expo Router not available:', e);
|
|
745
610
|
}
|
|
746
|
-
// expo-router not installed
|
|
747
611
|
return false;
|
|
748
612
|
}
|
|
749
613
|
}
|
|
@@ -758,22 +622,12 @@ function extractScreenNameFromRouterState(state, getScreenNameFromPath, normaliz
|
|
|
758
622
|
if (!state?.routes) return null;
|
|
759
623
|
const route = state.routes[state.index ?? state.routes.length - 1];
|
|
760
624
|
if (!route) return null;
|
|
761
|
-
|
|
762
|
-
// Add current route name to accumulated segments
|
|
763
625
|
const newSegments = [...accumulatedSegments, route.name];
|
|
764
|
-
|
|
765
|
-
// If this route has nested state, recurse deeper
|
|
766
626
|
if (route.state) {
|
|
767
627
|
return extractScreenNameFromRouterState(route.state, getScreenNameFromPath, normalizeScreenName, newSegments);
|
|
768
628
|
}
|
|
769
|
-
|
|
770
|
-
// We've reached the deepest level - build the screen name
|
|
771
|
-
// Filter out group markers like (tabs), (main), (auth)
|
|
772
629
|
const cleanSegments = newSegments.filter(s => !s.startsWith('(') && !s.endsWith(')'));
|
|
773
|
-
|
|
774
|
-
// If after filtering we have no segments, use the last meaningful name
|
|
775
630
|
if (cleanSegments.length === 0) {
|
|
776
|
-
// Find the last non-group segment
|
|
777
631
|
for (let i = newSegments.length - 1; i >= 0; i--) {
|
|
778
632
|
const seg = newSegments[i];
|
|
779
633
|
if (seg && !seg.startsWith('(') && !seg.endsWith(')')) {
|
|
@@ -812,27 +666,17 @@ function trackScreen(screenName) {
|
|
|
812
666
|
}
|
|
813
667
|
const previousScreen = currentScreen;
|
|
814
668
|
currentScreen = screenName;
|
|
815
|
-
// Add to screens visited (only track for unique set, avoid large array copies)
|
|
816
669
|
screensVisited.push(screenName);
|
|
817
|
-
|
|
818
|
-
// Update unique screens count
|
|
819
670
|
const uniqueScreens = new Set(screensVisited);
|
|
820
671
|
metrics.uniqueScreensCount = uniqueScreens.size;
|
|
821
|
-
|
|
822
|
-
// Update navigation count
|
|
823
672
|
metrics.navigationCount++;
|
|
824
673
|
metrics.totalEvents++;
|
|
825
674
|
if (__DEV__) {
|
|
826
675
|
_utils.logger.debug('trackScreen:', screenName, '(total screens:', metrics.uniqueScreensCount, ')');
|
|
827
676
|
}
|
|
828
|
-
|
|
829
|
-
// Notify callback
|
|
830
677
|
if (onScreenChange) {
|
|
831
678
|
onScreenChange(screenName, previousScreen);
|
|
832
679
|
}
|
|
833
|
-
|
|
834
|
-
// IMPORTANT: Also notify native module to send to backend
|
|
835
|
-
// This is the key fix - without this, screens don't get recorded!
|
|
836
680
|
try {
|
|
837
681
|
const RejourneyNative = getRejourneyNativeModule();
|
|
838
682
|
if (RejourneyNative?.screenChanged) {
|
|
@@ -854,18 +698,12 @@ function trackScreen(screenName) {
|
|
|
854
698
|
}
|
|
855
699
|
}
|
|
856
700
|
|
|
857
|
-
// =============================================================================
|
|
858
|
-
// API Metrics Tracking
|
|
859
|
-
// =============================================================================
|
|
860
|
-
|
|
861
701
|
/**
|
|
862
702
|
* Track an API request with timing data
|
|
863
703
|
*/
|
|
864
704
|
function trackAPIRequest(success, _statusCode, durationMs = 0, responseBytes = 0) {
|
|
865
705
|
if (!isInitialized) return;
|
|
866
706
|
metrics.apiTotalCount++;
|
|
867
|
-
|
|
868
|
-
// Accumulate timing and size for avg calculation
|
|
869
707
|
if (durationMs > 0) {
|
|
870
708
|
metrics.netTotalDurationMs += durationMs;
|
|
871
709
|
}
|
|
@@ -876,16 +714,10 @@ function trackAPIRequest(success, _statusCode, durationMs = 0, responseBytes = 0
|
|
|
876
714
|
metrics.apiSuccessCount++;
|
|
877
715
|
} else {
|
|
878
716
|
metrics.apiErrorCount++;
|
|
879
|
-
|
|
880
|
-
// API errors also count toward error count for UX score
|
|
881
717
|
metrics.errorCount++;
|
|
882
718
|
}
|
|
883
719
|
}
|
|
884
720
|
|
|
885
|
-
// =============================================================================
|
|
886
|
-
// Session Metrics
|
|
887
|
-
// =============================================================================
|
|
888
|
-
|
|
889
721
|
/**
|
|
890
722
|
* Create empty metrics object
|
|
891
723
|
*/
|
|
@@ -943,18 +775,11 @@ function trackInput() {
|
|
|
943
775
|
* Get current session metrics
|
|
944
776
|
*/
|
|
945
777
|
function getSessionMetrics() {
|
|
946
|
-
// Calculate scores before returning
|
|
947
778
|
calculateScores();
|
|
948
|
-
|
|
949
|
-
// Compute average API response time
|
|
950
779
|
const netAvgDurationMs = metrics.apiTotalCount > 0 ? Math.round(metrics.netTotalDurationMs / metrics.apiTotalCount) : 0;
|
|
951
|
-
|
|
952
|
-
// Lazily populate screensVisited only when metrics are retrieved
|
|
953
|
-
// This avoids expensive array copies on every screen change
|
|
954
780
|
return {
|
|
955
781
|
...metrics,
|
|
956
782
|
screensVisited: [...screensVisited],
|
|
957
|
-
// Only copy here when needed
|
|
958
783
|
netAvgDurationMs
|
|
959
784
|
};
|
|
960
785
|
}
|
|
@@ -963,34 +788,15 @@ function getSessionMetrics() {
|
|
|
963
788
|
* Calculate session scores
|
|
964
789
|
*/
|
|
965
790
|
function calculateScores() {
|
|
966
|
-
// Interaction Score (0-100)
|
|
967
|
-
// Based on total interactions normalized to a baseline
|
|
968
791
|
const totalInteractions = metrics.touchCount + metrics.scrollCount + metrics.gestureCount + metrics.inputCount;
|
|
969
|
-
|
|
970
|
-
// Assume 50 interactions is "average" for a session
|
|
971
792
|
const avgInteractions = 50;
|
|
972
793
|
metrics.interactionScore = Math.min(100, Math.round(totalInteractions / avgInteractions * 100));
|
|
973
|
-
|
|
974
|
-
// Exploration Score (0-100)
|
|
975
|
-
// Based on unique screens visited
|
|
976
|
-
// Assume 5 screens is "average" exploration
|
|
977
794
|
const avgScreens = 5;
|
|
978
795
|
metrics.explorationScore = Math.min(100, Math.round(metrics.uniqueScreensCount / avgScreens * 100));
|
|
979
|
-
|
|
980
|
-
// UX Score (0-100)
|
|
981
|
-
// Starts at 100, deducts for issues
|
|
982
796
|
let uxScore = 100;
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
uxScore -= Math.min(
|
|
986
|
-
|
|
987
|
-
// Deduct for rage taps
|
|
988
|
-
uxScore -= Math.min(24, metrics.rageTapCount * 8); // Max 24 point deduction
|
|
989
|
-
|
|
990
|
-
// Deduct for API errors
|
|
991
|
-
uxScore -= Math.min(20, metrics.apiErrorCount * 10); // Max 20 point deduction
|
|
992
|
-
|
|
993
|
-
// Bonus for completing funnel (if screens > 3)
|
|
797
|
+
uxScore -= Math.min(30, metrics.errorCount * 15);
|
|
798
|
+
uxScore -= Math.min(24, metrics.rageTapCount * 8);
|
|
799
|
+
uxScore -= Math.min(20, metrics.apiErrorCount * 10);
|
|
994
800
|
if (metrics.uniqueScreensCount >= 3) {
|
|
995
801
|
uxScore += 5;
|
|
996
802
|
}
|
|
@@ -1008,43 +814,29 @@ function resetMetrics() {
|
|
|
1008
814
|
tapCount = 0;
|
|
1009
815
|
sessionStartTime = Date.now();
|
|
1010
816
|
}
|
|
1011
|
-
|
|
1012
|
-
// =============================================================================
|
|
1013
|
-
// Session duration helpers
|
|
1014
|
-
// =============================================================================
|
|
1015
|
-
|
|
1016
|
-
/** Clamp and set max session duration in minutes (1–10). Defaults to 10. */
|
|
1017
817
|
function setMaxSessionDurationMinutes(minutes) {
|
|
1018
818
|
const clampedMinutes = Math.min(10, Math.max(1, minutes ?? 10));
|
|
1019
819
|
maxSessionDurationMs = clampedMinutes * 60 * 1000;
|
|
1020
820
|
}
|
|
1021
|
-
|
|
1022
|
-
/** Returns true if the current session exceeded the configured max duration. */
|
|
1023
821
|
function hasExceededMaxSessionDuration() {
|
|
1024
822
|
if (!sessionStartTime) return false;
|
|
1025
823
|
return Date.now() - sessionStartTime >= maxSessionDurationMs;
|
|
1026
824
|
}
|
|
1027
|
-
|
|
1028
|
-
/** Returns remaining milliseconds until the session should stop. */
|
|
1029
825
|
function getRemainingSessionDurationMs() {
|
|
1030
826
|
if (!sessionStartTime) return maxSessionDurationMs;
|
|
1031
827
|
const remaining = maxSessionDurationMs - (Date.now() - sessionStartTime);
|
|
1032
828
|
return Math.max(0, remaining);
|
|
1033
829
|
}
|
|
1034
830
|
|
|
1035
|
-
// =============================================================================
|
|
1036
|
-
// Device Info Collection
|
|
1037
|
-
// =============================================================================
|
|
1038
|
-
|
|
1039
831
|
/**
|
|
1040
832
|
* Collect device information
|
|
1041
833
|
*/
|
|
1042
|
-
|
|
834
|
+
/**
|
|
835
|
+
* Collect device information
|
|
836
|
+
*/
|
|
837
|
+
async function collectDeviceInfo() {
|
|
1043
838
|
const Dimensions = getDimensions();
|
|
1044
839
|
const Platform = getPlatform();
|
|
1045
|
-
const NativeModules = getNativeModules();
|
|
1046
|
-
|
|
1047
|
-
// Default values if react-native isn't available
|
|
1048
840
|
let width = 0,
|
|
1049
841
|
height = 0,
|
|
1050
842
|
scale = 1;
|
|
@@ -1056,100 +848,43 @@ function collectDeviceInfo() {
|
|
|
1056
848
|
scale = screenDims?.scale || 1;
|
|
1057
849
|
}
|
|
1058
850
|
|
|
1059
|
-
//
|
|
1060
|
-
let model = 'Unknown';
|
|
1061
|
-
let manufacturer;
|
|
1062
|
-
let osVersion = 'Unknown';
|
|
1063
|
-
let appVersion;
|
|
1064
|
-
let appId;
|
|
851
|
+
// Basic JS-side info
|
|
1065
852
|
let locale;
|
|
1066
853
|
let timezone;
|
|
1067
854
|
try {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
const DeviceInfo = require('react-native-device-info');
|
|
1071
|
-
model = DeviceInfo.getModel?.() || model;
|
|
1072
|
-
manufacturer = DeviceInfo.getBrand?.() || undefined;
|
|
1073
|
-
osVersion = DeviceInfo.getSystemVersion?.() || osVersion;
|
|
1074
|
-
appVersion = DeviceInfo.getVersion?.() || undefined;
|
|
1075
|
-
appId = DeviceInfo.getBundleId?.() || undefined;
|
|
1076
|
-
locale = DeviceInfo.getDeviceLocale?.() || undefined;
|
|
1077
|
-
timezone = DeviceInfo.getTimezone?.() || undefined;
|
|
855
|
+
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
856
|
+
locale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
1078
857
|
} catch {
|
|
1079
|
-
//
|
|
1080
|
-
try {
|
|
1081
|
-
// Try expo-application for app version/id
|
|
1082
|
-
const Application = require('expo-application');
|
|
1083
|
-
appVersion = Application.nativeApplicationVersion || Application.applicationVersion || undefined;
|
|
1084
|
-
appId = Application.applicationId || undefined;
|
|
1085
|
-
} catch {
|
|
1086
|
-
// expo-application not available
|
|
1087
|
-
}
|
|
1088
|
-
try {
|
|
1089
|
-
// Try expo-constants for additional info
|
|
1090
|
-
const Constants = require('expo-constants');
|
|
1091
|
-
const expoConfig = Constants.expoConfig || Constants.manifest2?.extra?.expoClient || Constants.manifest;
|
|
1092
|
-
if (!appVersion && expoConfig?.version) {
|
|
1093
|
-
appVersion = expoConfig.version;
|
|
1094
|
-
}
|
|
1095
|
-
if (!appId && (expoConfig?.ios?.bundleIdentifier || expoConfig?.android?.package)) {
|
|
1096
|
-
appId = Platform?.OS === 'ios' ? expoConfig?.ios?.bundleIdentifier : expoConfig?.android?.package;
|
|
1097
|
-
}
|
|
1098
|
-
} catch {
|
|
1099
|
-
// expo-constants not available
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
// Fall back to basic platform info
|
|
1103
|
-
if (Platform?.OS === 'ios') {
|
|
1104
|
-
// Get basic info from constants
|
|
1105
|
-
const PlatformConstants = NativeModules?.PlatformConstants;
|
|
1106
|
-
osVersion = Platform.Version?.toString() || osVersion;
|
|
1107
|
-
model = PlatformConstants?.interfaceIdiom === 'pad' ? 'iPad' : 'iPhone';
|
|
1108
|
-
} else if (Platform?.OS === 'android') {
|
|
1109
|
-
osVersion = Platform.Version?.toString() || osVersion;
|
|
1110
|
-
model = 'Android Device';
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
// Get timezone
|
|
1115
|
-
if (!timezone) {
|
|
1116
|
-
try {
|
|
1117
|
-
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1118
|
-
} catch {
|
|
1119
|
-
timezone = undefined;
|
|
1120
|
-
}
|
|
858
|
+
// Ignore
|
|
1121
859
|
}
|
|
1122
860
|
|
|
1123
|
-
// Get
|
|
1124
|
-
|
|
861
|
+
// Get native info
|
|
862
|
+
const nativeModule = getRejourneyNativeModule();
|
|
863
|
+
let nativeInfo = {};
|
|
864
|
+
if (nativeModule && nativeModule.getDeviceInfo) {
|
|
1125
865
|
try {
|
|
1126
|
-
|
|
1127
|
-
} catch {
|
|
1128
|
-
|
|
866
|
+
nativeInfo = await nativeModule.getDeviceInfo();
|
|
867
|
+
} catch (e) {
|
|
868
|
+
if (__DEV__) {
|
|
869
|
+
console.warn('[Rejourney] Failed to get native device info:', e);
|
|
870
|
+
}
|
|
1129
871
|
}
|
|
1130
872
|
}
|
|
1131
873
|
return {
|
|
1132
|
-
model,
|
|
1133
|
-
manufacturer,
|
|
874
|
+
model: nativeInfo.model || 'Unknown',
|
|
875
|
+
manufacturer: nativeInfo.brand,
|
|
1134
876
|
os: Platform?.OS || 'ios',
|
|
1135
|
-
osVersion,
|
|
877
|
+
osVersion: nativeInfo.systemVersion || Platform?.Version?.toString() || 'Unknown',
|
|
1136
878
|
screenWidth: Math.round(width),
|
|
1137
879
|
screenHeight: Math.round(height),
|
|
1138
880
|
pixelRatio: scale,
|
|
1139
|
-
appVersion,
|
|
1140
|
-
appId,
|
|
1141
|
-
locale,
|
|
1142
|
-
timezone
|
|
881
|
+
appVersion: nativeInfo.appVersion,
|
|
882
|
+
appId: nativeInfo.bundleId,
|
|
883
|
+
locale: locale,
|
|
884
|
+
timezone: timezone
|
|
1143
885
|
};
|
|
1144
886
|
}
|
|
1145
887
|
|
|
1146
|
-
// =============================================================================
|
|
1147
|
-
// Anonymous ID Generation
|
|
1148
|
-
// =============================================================================
|
|
1149
|
-
|
|
1150
|
-
// Storage key for anonymous ID
|
|
1151
|
-
const ANONYMOUS_ID_KEY = '@rejourney_anonymous_id';
|
|
1152
|
-
|
|
1153
888
|
/**
|
|
1154
889
|
* Generate a persistent anonymous ID
|
|
1155
890
|
*/
|
|
@@ -1159,39 +894,12 @@ function generateAnonymousId() {
|
|
|
1159
894
|
return `anon_${timestamp}_${random}`;
|
|
1160
895
|
}
|
|
1161
896
|
|
|
1162
|
-
/**
|
|
1163
|
-
* Initialize anonymous ID - tries to load from storage, generates new if not found
|
|
1164
|
-
* This is called internally and runs asynchronously
|
|
1165
|
-
*/
|
|
1166
|
-
async function initAnonymousId() {
|
|
1167
|
-
try {
|
|
1168
|
-
// Try to load from AsyncStorage
|
|
1169
|
-
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
1170
|
-
const storedId = await AsyncStorage.getItem(ANONYMOUS_ID_KEY);
|
|
1171
|
-
if (storedId) {
|
|
1172
|
-
anonymousId = storedId;
|
|
1173
|
-
} else {
|
|
1174
|
-
// Generate new ID and persist
|
|
1175
|
-
anonymousId = generateAnonymousId();
|
|
1176
|
-
await AsyncStorage.setItem(ANONYMOUS_ID_KEY, anonymousId);
|
|
1177
|
-
}
|
|
1178
|
-
} catch {
|
|
1179
|
-
// AsyncStorage not available or error - just generate without persistence
|
|
1180
|
-
if (!anonymousId) {
|
|
1181
|
-
anonymousId = generateAnonymousId();
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
897
|
/**
|
|
1187
898
|
* Get the anonymous ID (synchronous - returns generated ID immediately)
|
|
1188
|
-
* For persistent ID, call initAnonymousId() first
|
|
1189
899
|
*/
|
|
1190
900
|
function getAnonymousId() {
|
|
1191
901
|
if (!anonymousId) {
|
|
1192
902
|
anonymousId = generateAnonymousId();
|
|
1193
|
-
// Try to persist asynchronously (fire and forget)
|
|
1194
|
-
initAnonymousId().catch(() => {});
|
|
1195
903
|
}
|
|
1196
904
|
return anonymousId;
|
|
1197
905
|
}
|
|
@@ -1204,11 +912,9 @@ async function ensurePersistentAnonymousId() {
|
|
|
1204
912
|
if (anonymousId) return anonymousId;
|
|
1205
913
|
if (!anonymousIdPromise) {
|
|
1206
914
|
anonymousIdPromise = (async () => {
|
|
1207
|
-
await
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
}
|
|
1211
|
-
return anonymousId;
|
|
915
|
+
const id = await loadAnonymousId();
|
|
916
|
+
anonymousId = id;
|
|
917
|
+
return id;
|
|
1212
918
|
})();
|
|
1213
919
|
}
|
|
1214
920
|
return anonymousIdPromise;
|
|
@@ -1219,27 +925,23 @@ async function ensurePersistentAnonymousId() {
|
|
|
1219
925
|
* Call this at app startup for best results
|
|
1220
926
|
*/
|
|
1221
927
|
async function loadAnonymousId() {
|
|
1222
|
-
|
|
1223
|
-
|
|
928
|
+
const nativeModule = getRejourneyNativeModule();
|
|
929
|
+
if (nativeModule && nativeModule.getUserIdentity) {
|
|
930
|
+
try {
|
|
931
|
+
return (await nativeModule.getUserIdentity()) || generateAnonymousId();
|
|
932
|
+
} catch {
|
|
933
|
+
return generateAnonymousId();
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return generateAnonymousId();
|
|
1224
937
|
}
|
|
1225
938
|
|
|
1226
939
|
/**
|
|
1227
|
-
* Set a custom anonymous ID
|
|
940
|
+
* Set a custom anonymous ID
|
|
1228
941
|
*/
|
|
1229
942
|
function setAnonymousId(id) {
|
|
1230
943
|
anonymousId = id;
|
|
1231
|
-
// Try to persist asynchronously
|
|
1232
|
-
try {
|
|
1233
|
-
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
1234
|
-
AsyncStorage.setItem(ANONYMOUS_ID_KEY, id).catch(() => {});
|
|
1235
|
-
} catch {
|
|
1236
|
-
// Ignore if AsyncStorage not available
|
|
1237
|
-
}
|
|
1238
944
|
}
|
|
1239
|
-
|
|
1240
|
-
// =============================================================================
|
|
1241
|
-
// Exports
|
|
1242
|
-
// =============================================================================
|
|
1243
945
|
var _default = exports.default = {
|
|
1244
946
|
init: initAutoTracking,
|
|
1245
947
|
cleanup: cleanupAutoTracking,
|