@datalyr/react-native 1.6.4 → 1.7.0
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 +99 -4
- package/lib/auto-events.d.ts +9 -2
- package/lib/auto-events.js +22 -15
- package/lib/datalyr-sdk.js +15 -5
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/screen-tracking.d.ts +92 -0
- package/lib/screen-tracking.js +130 -0
- package/package.json +1 -1
- package/src/auto-events.ts +23 -18
- package/src/datalyr-sdk-expo.ts +16 -5
- package/src/datalyr-sdk.ts +16 -6
- package/src/expo.ts +33 -0
- package/src/index.ts +7 -0
- package/src/screen-tracking.ts +201 -0
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ Mobile analytics and attribution SDK for React Native and Expo. Track events, id
|
|
|
27
27
|
- [Deferred Attribution](#deferred-attribution)
|
|
28
28
|
- [Customer Journey](#customer-journey)
|
|
29
29
|
- [Event Queue](#event-queue)
|
|
30
|
+
- [Automatic Screen Tracking](#automatic-screen-tracking)
|
|
30
31
|
- [Auto Events](#auto-events)
|
|
31
32
|
- [SKAdNetwork](#skadnetwork)
|
|
32
33
|
- [Platform Integrations](#platform-integrations)
|
|
@@ -178,10 +179,10 @@ await Datalyr.initialize({
|
|
|
178
179
|
|
|
179
180
|
```typescript
|
|
180
181
|
interface AutoEventConfig {
|
|
181
|
-
trackSessions?: boolean; // Track session_start / session_end
|
|
182
|
-
trackScreenViews?: boolean; //
|
|
183
|
-
trackAppUpdates?: boolean; // Track app_update events
|
|
184
|
-
trackPerformance?: boolean; // Track performance metrics
|
|
182
|
+
trackSessions?: boolean; // Track session_start / session_end (default: true)
|
|
183
|
+
trackScreenViews?: boolean; // Enable screen view events via screen() (default: true)
|
|
184
|
+
trackAppUpdates?: boolean; // Track app_update events (default: true)
|
|
185
|
+
trackPerformance?: boolean; // Track performance metrics (default: false)
|
|
185
186
|
sessionTimeoutMs?: number; // Session timeout in ms
|
|
186
187
|
}
|
|
187
188
|
```
|
|
@@ -233,6 +234,8 @@ await Datalyr.screen('Product Details', {
|
|
|
233
234
|
});
|
|
234
235
|
```
|
|
235
236
|
|
|
237
|
+
Each `screen()` call fires a single `pageview` event with the `screen` property set. Session data (`session_id`, `pageviews_in_session`, `previous_screen`) is automatically attached.
|
|
238
|
+
|
|
236
239
|
### E-Commerce Events
|
|
237
240
|
|
|
238
241
|
Standard e-commerce events:
|
|
@@ -504,6 +507,98 @@ When the device is offline:
|
|
|
504
507
|
|
|
505
508
|
---
|
|
506
509
|
|
|
510
|
+
## Automatic Screen Tracking
|
|
511
|
+
|
|
512
|
+
Track screen views automatically when using React Navigation (v5+/v6+). The `datalyrScreenTracking` helper wires into the navigation container and fires a `pageview` event on every route change.
|
|
513
|
+
|
|
514
|
+
### React Navigation
|
|
515
|
+
|
|
516
|
+
```tsx
|
|
517
|
+
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
|
|
518
|
+
import { datalyrScreenTracking } from '@datalyr/react-native';
|
|
519
|
+
|
|
520
|
+
function App() {
|
|
521
|
+
const navigationRef = useNavigationContainerRef();
|
|
522
|
+
const screenTracking = datalyrScreenTracking(navigationRef);
|
|
523
|
+
|
|
524
|
+
return (
|
|
525
|
+
<NavigationContainer
|
|
526
|
+
ref={navigationRef}
|
|
527
|
+
onReady={screenTracking.onReady}
|
|
528
|
+
onStateChange={screenTracking.onStateChange}
|
|
529
|
+
>
|
|
530
|
+
{/* ...screens */}
|
|
531
|
+
</NavigationContainer>
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Expo with React Navigation
|
|
537
|
+
|
|
538
|
+
For Expo projects using React Navigation, import from the Expo entry point:
|
|
539
|
+
|
|
540
|
+
```tsx
|
|
541
|
+
import { datalyrScreenTracking } from '@datalyr/react-native/expo';
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
The API is identical to the React Navigation example above.
|
|
545
|
+
|
|
546
|
+
### Expo Router
|
|
547
|
+
|
|
548
|
+
Expo Router does not expose a `NavigationContainer`, so automatic tracking is not available. Use `datalyr.screen()` manually in your layout files instead:
|
|
549
|
+
|
|
550
|
+
```tsx
|
|
551
|
+
import { datalyr } from '@datalyr/react-native/expo';
|
|
552
|
+
import { usePathname } from 'expo-router';
|
|
553
|
+
import { useEffect } from 'react';
|
|
554
|
+
|
|
555
|
+
export default function RootLayout() {
|
|
556
|
+
const pathname = usePathname();
|
|
557
|
+
|
|
558
|
+
useEffect(() => {
|
|
559
|
+
datalyr.screen(pathname);
|
|
560
|
+
}, [pathname]);
|
|
561
|
+
|
|
562
|
+
return <Slot />;
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Configuration
|
|
567
|
+
|
|
568
|
+
You can customize screen name transforms, filtering, and property extraction:
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
const screenTracking = datalyrScreenTracking(navigationRef, {
|
|
572
|
+
// Clean up route names
|
|
573
|
+
transformScreenName: (name) => name.replace('Screen', ''),
|
|
574
|
+
|
|
575
|
+
// Skip certain screens
|
|
576
|
+
shouldTrackScreen: (name) => !['Splash', 'Loading'].includes(name),
|
|
577
|
+
|
|
578
|
+
// Extract route params as event properties
|
|
579
|
+
extractProperties: (name, params) => ({
|
|
580
|
+
product_id: params?.productId,
|
|
581
|
+
}),
|
|
582
|
+
});
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Advanced: Custom Tracking Function
|
|
586
|
+
|
|
587
|
+
If you need to control which SDK instance is used, use `createScreenTrackingListeners` instead:
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
import { createScreenTrackingListeners } from '@datalyr/react-native';
|
|
591
|
+
|
|
592
|
+
const { onReady, onStateChange } = createScreenTrackingListeners(
|
|
593
|
+
navigationRef,
|
|
594
|
+
(screenName, properties) => myCustomSdk.screen(screenName, properties),
|
|
595
|
+
);
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
> **Note:** If you enable automatic screen tracking, avoid also calling `Datalyr.screen()` manually for the same screens, as this will produce duplicate events.
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
507
602
|
## Auto Events
|
|
508
603
|
|
|
509
604
|
Enable automatic lifecycle tracking:
|
package/lib/auto-events.d.ts
CHANGED
|
@@ -45,9 +45,16 @@ export declare class AutoEventsManager {
|
|
|
45
45
|
*/
|
|
46
46
|
handleAppBackground(): Promise<void>;
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* Update session counters for a screen view.
|
|
49
|
+
* The actual pageview event is fired by the SDK's screen() method —
|
|
50
|
+
* this only updates internal session state to avoid double-firing.
|
|
49
51
|
*/
|
|
50
|
-
|
|
52
|
+
recordScreenView(screenName: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Get session data to enrich a pageview event.
|
|
55
|
+
* Called by the SDK's screen() method to attach session info.
|
|
56
|
+
*/
|
|
57
|
+
getScreenViewEnrichment(): Record<string, any> | null;
|
|
51
58
|
/**
|
|
52
59
|
* Track app launch performance
|
|
53
60
|
*/
|
package/lib/auto-events.js
CHANGED
|
@@ -155,35 +155,42 @@ export class AutoEventsManager {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
/**
|
|
158
|
-
*
|
|
158
|
+
* Update session counters for a screen view.
|
|
159
|
+
* The actual pageview event is fired by the SDK's screen() method —
|
|
160
|
+
* this only updates internal session state to avoid double-firing.
|
|
159
161
|
*/
|
|
160
|
-
async
|
|
162
|
+
async recordScreenView(screenName) {
|
|
161
163
|
try {
|
|
162
164
|
if (!this.config.trackScreenViews)
|
|
163
165
|
return;
|
|
164
|
-
// Don't
|
|
166
|
+
// Don't count the same screen twice in a row
|
|
165
167
|
if (this.lastScreenName === screenName)
|
|
166
168
|
return;
|
|
167
|
-
|
|
168
|
-
screen_name: screenName,
|
|
169
|
-
previous_screen: this.lastScreenName,
|
|
170
|
-
timestamp: Date.now(),
|
|
171
|
-
...properties,
|
|
172
|
-
};
|
|
173
|
-
// Add session data if available
|
|
169
|
+
// Update session counters (no event fired here)
|
|
174
170
|
if (this.currentSession) {
|
|
175
171
|
this.currentSession.screenViews++;
|
|
176
|
-
|
|
177
|
-
screenProperties.pageviews_in_session = this.currentSession.screenViews;
|
|
172
|
+
this.currentSession.lastActivity = Date.now();
|
|
178
173
|
}
|
|
179
|
-
await this.trackEvent('pageview', screenProperties);
|
|
180
174
|
this.lastScreenName = screenName;
|
|
181
|
-
debugLog('
|
|
175
|
+
debugLog('Screen view counted:', screenName);
|
|
182
176
|
}
|
|
183
177
|
catch (error) {
|
|
184
|
-
errorLog('Error
|
|
178
|
+
errorLog('Error updating screen view:', error);
|
|
185
179
|
}
|
|
186
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Get session data to enrich a pageview event.
|
|
183
|
+
* Called by the SDK's screen() method to attach session info.
|
|
184
|
+
*/
|
|
185
|
+
getScreenViewEnrichment() {
|
|
186
|
+
if (!this.currentSession)
|
|
187
|
+
return null;
|
|
188
|
+
return {
|
|
189
|
+
session_id: this.currentSession.sessionId,
|
|
190
|
+
pageviews_in_session: this.currentSession.screenViews,
|
|
191
|
+
previous_screen: this.lastScreenName,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
187
194
|
/**
|
|
188
195
|
* Track app launch performance
|
|
189
196
|
*/
|
package/lib/datalyr-sdk.js
CHANGED
|
@@ -177,7 +177,7 @@ export class DatalyrSDK {
|
|
|
177
177
|
const installData = await attributionManager.trackInstall();
|
|
178
178
|
await this.track('app_install', {
|
|
179
179
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
180
|
-
sdk_version: '1.
|
|
180
|
+
sdk_version: '1.7.0',
|
|
181
181
|
...installData,
|
|
182
182
|
});
|
|
183
183
|
}
|
|
@@ -226,11 +226,21 @@ export class DatalyrSDK {
|
|
|
226
226
|
screen: screenName,
|
|
227
227
|
...properties,
|
|
228
228
|
};
|
|
229
|
-
|
|
230
|
-
//
|
|
229
|
+
// Enrich with session data (pageview count, previous screen) if available.
|
|
230
|
+
// User-provided properties take precedence over enrichment.
|
|
231
231
|
if (this.autoEventsManager) {
|
|
232
|
-
|
|
232
|
+
const enrichment = this.autoEventsManager.getScreenViewEnrichment();
|
|
233
|
+
if (enrichment) {
|
|
234
|
+
for (const [key, value] of Object.entries(enrichment)) {
|
|
235
|
+
if (!(key in screenData)) {
|
|
236
|
+
screenData[key] = value;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Update session counters (does NOT fire a second event)
|
|
241
|
+
await this.autoEventsManager.recordScreenView(screenName);
|
|
233
242
|
}
|
|
243
|
+
await this.track('pageview', screenData);
|
|
234
244
|
}
|
|
235
245
|
/**
|
|
236
246
|
* Identify a user
|
|
@@ -898,7 +908,7 @@ export class DatalyrSDK {
|
|
|
898
908
|
carrier: deviceInfo.carrier,
|
|
899
909
|
network_type: getNetworkType(),
|
|
900
910
|
timestamp: Date.now(),
|
|
901
|
-
sdk_version: '1.
|
|
911
|
+
sdk_version: '1.7.0',
|
|
902
912
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
903
913
|
...(advertiserInfo ? {
|
|
904
914
|
idfa: advertiserInfo.idfa,
|
package/lib/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export * from './utils';
|
|
|
10
10
|
export * from './http-client';
|
|
11
11
|
export * from './event-queue';
|
|
12
12
|
export { DatalyrSDK };
|
|
13
|
+
export { datalyrScreenTracking, createScreenTrackingListeners, } from './screen-tracking';
|
|
14
|
+
export type { ScreenTrackingConfig, NavigationContainerRef } from './screen-tracking';
|
|
13
15
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
14
16
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
15
17
|
export { appleSearchAdsIntegration, playInstallReferrerIntegration } from './integrations';
|
package/lib/index.js
CHANGED
|
@@ -16,6 +16,8 @@ export * from './http-client';
|
|
|
16
16
|
export * from './event-queue';
|
|
17
17
|
// Also export the SDK class for advanced usage
|
|
18
18
|
export { DatalyrSDK };
|
|
19
|
+
// Export automatic screen tracking for React Navigation
|
|
20
|
+
export { datalyrScreenTracking, createScreenTrackingListeners, } from './screen-tracking';
|
|
19
21
|
// Export SKAdNetwork components
|
|
20
22
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
21
23
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic screen tracking for React Navigation (v5+ / v6+).
|
|
3
|
+
*
|
|
4
|
+
* The simplest integration — just spread onto your NavigationContainer:
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
|
|
8
|
+
* import { datalyrScreenTracking } from '@datalyr/react-native';
|
|
9
|
+
*
|
|
10
|
+
* const navigationRef = useNavigationContainerRef();
|
|
11
|
+
* const screenTracking = datalyrScreenTracking(navigationRef);
|
|
12
|
+
*
|
|
13
|
+
* <NavigationContainer
|
|
14
|
+
* ref={navigationRef}
|
|
15
|
+
* onReady={screenTracking.onReady}
|
|
16
|
+
* onStateChange={screenTracking.onStateChange}
|
|
17
|
+
* />
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Note: If you enable automatic screen tracking, avoid also calling
|
|
21
|
+
* `Datalyr.screen()` / `datalyr.screen()` manually for the same screens,
|
|
22
|
+
* as this will produce duplicate events.
|
|
23
|
+
*
|
|
24
|
+
* For Expo Router (file-based routing), automatic tracking is not needed.
|
|
25
|
+
* Use the `datalyr.screen()` method in your layout files instead.
|
|
26
|
+
*/
|
|
27
|
+
/** Minimal subset of React Navigation's NavigationContainerRef that we need. */
|
|
28
|
+
export interface NavigationContainerRef {
|
|
29
|
+
getCurrentRoute(): {
|
|
30
|
+
name: string;
|
|
31
|
+
params?: Record<string, any>;
|
|
32
|
+
} | undefined;
|
|
33
|
+
}
|
|
34
|
+
export interface ScreenTrackingConfig {
|
|
35
|
+
/**
|
|
36
|
+
* Transform the route name before tracking.
|
|
37
|
+
* Useful for cleaning up or grouping screen names.
|
|
38
|
+
* @example (name) => name.replace('Screen', '')
|
|
39
|
+
*/
|
|
40
|
+
transformScreenName?: (routeName: string) => string;
|
|
41
|
+
/**
|
|
42
|
+
* Filter which screens should be tracked.
|
|
43
|
+
* Return false to skip tracking for a given screen.
|
|
44
|
+
* @example (name) => !['Loading', 'Splash'].includes(name)
|
|
45
|
+
*/
|
|
46
|
+
shouldTrackScreen?: (routeName: string) => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Extract additional properties from the route to include in the screen event.
|
|
49
|
+
* @example (name, params) => ({ product_id: params?.productId })
|
|
50
|
+
*/
|
|
51
|
+
extractProperties?: (routeName: string, params?: Record<string, any>) => Record<string, any>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create `onReady` and `onStateChange` callbacks that automatically
|
|
55
|
+
* fire screen events through the Datalyr SDK whenever the active
|
|
56
|
+
* React Navigation route changes.
|
|
57
|
+
*
|
|
58
|
+
* @param navigationRef A React Navigation `NavigationContainerRef`
|
|
59
|
+
* (from `useNavigationContainerRef()` or
|
|
60
|
+
* `createNavigationContainerRef()`).
|
|
61
|
+
* @param trackScreen The function that records a screen event.
|
|
62
|
+
* Pass `datalyr.screen.bind(datalyr)` or the
|
|
63
|
+
* Datalyr static class's `Datalyr.screen`.
|
|
64
|
+
* If omitted, the default singleton is used.
|
|
65
|
+
* @param config Optional filtering / transform config.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createScreenTrackingListeners(navigationRef: NavigationContainerRef, trackScreen: (screenName: string, properties?: Record<string, any>) => Promise<void>, config?: ScreenTrackingConfig): {
|
|
68
|
+
onReady: () => void;
|
|
69
|
+
onStateChange: () => void;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Auto-wire screen tracking to the default Datalyr singleton.
|
|
73
|
+
* This is the recommended API for most users.
|
|
74
|
+
*
|
|
75
|
+
* ```tsx
|
|
76
|
+
* const navigationRef = useNavigationContainerRef();
|
|
77
|
+
* const screenTracking = datalyrScreenTracking(navigationRef);
|
|
78
|
+
*
|
|
79
|
+
* <NavigationContainer
|
|
80
|
+
* ref={navigationRef}
|
|
81
|
+
* onReady={screenTracking.onReady}
|
|
82
|
+
* onStateChange={screenTracking.onStateChange}
|
|
83
|
+
* />
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @param navigationRef React Navigation container ref
|
|
87
|
+
* @param config Optional screen name transforms and filters
|
|
88
|
+
*/
|
|
89
|
+
export declare function datalyrScreenTracking(navigationRef: NavigationContainerRef, config?: ScreenTrackingConfig): {
|
|
90
|
+
onReady: () => void;
|
|
91
|
+
onStateChange: () => void;
|
|
92
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic screen tracking for React Navigation (v5+ / v6+).
|
|
3
|
+
*
|
|
4
|
+
* The simplest integration — just spread onto your NavigationContainer:
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
|
|
8
|
+
* import { datalyrScreenTracking } from '@datalyr/react-native';
|
|
9
|
+
*
|
|
10
|
+
* const navigationRef = useNavigationContainerRef();
|
|
11
|
+
* const screenTracking = datalyrScreenTracking(navigationRef);
|
|
12
|
+
*
|
|
13
|
+
* <NavigationContainer
|
|
14
|
+
* ref={navigationRef}
|
|
15
|
+
* onReady={screenTracking.onReady}
|
|
16
|
+
* onStateChange={screenTracking.onStateChange}
|
|
17
|
+
* />
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Note: If you enable automatic screen tracking, avoid also calling
|
|
21
|
+
* `Datalyr.screen()` / `datalyr.screen()` manually for the same screens,
|
|
22
|
+
* as this will produce duplicate events.
|
|
23
|
+
*
|
|
24
|
+
* For Expo Router (file-based routing), automatic tracking is not needed.
|
|
25
|
+
* Use the `datalyr.screen()` method in your layout files instead.
|
|
26
|
+
*/
|
|
27
|
+
import { debugLog, errorLog } from './utils';
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Core implementation
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Create `onReady` and `onStateChange` callbacks that automatically
|
|
33
|
+
* fire screen events through the Datalyr SDK whenever the active
|
|
34
|
+
* React Navigation route changes.
|
|
35
|
+
*
|
|
36
|
+
* @param navigationRef A React Navigation `NavigationContainerRef`
|
|
37
|
+
* (from `useNavigationContainerRef()` or
|
|
38
|
+
* `createNavigationContainerRef()`).
|
|
39
|
+
* @param trackScreen The function that records a screen event.
|
|
40
|
+
* Pass `datalyr.screen.bind(datalyr)` or the
|
|
41
|
+
* Datalyr static class's `Datalyr.screen`.
|
|
42
|
+
* If omitted, the default singleton is used.
|
|
43
|
+
* @param config Optional filtering / transform config.
|
|
44
|
+
*/
|
|
45
|
+
export function createScreenTrackingListeners(navigationRef, trackScreen, config) {
|
|
46
|
+
let currentRouteName;
|
|
47
|
+
const resolveScreenName = (routeName) => (config === null || config === void 0 ? void 0 : config.transformScreenName) ? config.transformScreenName(routeName) : routeName;
|
|
48
|
+
const buildProperties = (routeName, params, previousRouteName) => {
|
|
49
|
+
const props = { source: 'auto_navigation' };
|
|
50
|
+
if (previousRouteName) {
|
|
51
|
+
props.previous_screen = resolveScreenName(previousRouteName);
|
|
52
|
+
}
|
|
53
|
+
if (config === null || config === void 0 ? void 0 : config.extractProperties) {
|
|
54
|
+
Object.assign(props, config.extractProperties(routeName, params));
|
|
55
|
+
}
|
|
56
|
+
return props;
|
|
57
|
+
};
|
|
58
|
+
const shouldTrack = (routeName) => !(config === null || config === void 0 ? void 0 : config.shouldTrackScreen) || config.shouldTrackScreen(routeName);
|
|
59
|
+
const safeTrack = (screenName, properties) => {
|
|
60
|
+
try {
|
|
61
|
+
trackScreen(screenName, properties).catch((err) => {
|
|
62
|
+
errorLog('Auto screen tracking failed:', err);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
errorLog('Auto screen tracking failed:', err);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const onReady = () => {
|
|
70
|
+
const route = navigationRef.getCurrentRoute();
|
|
71
|
+
if (!route)
|
|
72
|
+
return;
|
|
73
|
+
currentRouteName = route.name;
|
|
74
|
+
if (shouldTrack(route.name)) {
|
|
75
|
+
const screenName = resolveScreenName(route.name);
|
|
76
|
+
safeTrack(screenName, buildProperties(route.name, route.params));
|
|
77
|
+
debugLog('Auto screen tracking: initial screen', screenName);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const onStateChange = () => {
|
|
81
|
+
const route = navigationRef.getCurrentRoute();
|
|
82
|
+
if (!route)
|
|
83
|
+
return;
|
|
84
|
+
const previousRouteName = currentRouteName;
|
|
85
|
+
currentRouteName = route.name;
|
|
86
|
+
// Don't fire for same-screen param changes
|
|
87
|
+
if (previousRouteName === currentRouteName)
|
|
88
|
+
return;
|
|
89
|
+
if (!shouldTrack(route.name))
|
|
90
|
+
return;
|
|
91
|
+
const screenName = resolveScreenName(route.name);
|
|
92
|
+
safeTrack(screenName, buildProperties(route.name, route.params, previousRouteName));
|
|
93
|
+
debugLog('Auto screen tracking: navigated to', screenName);
|
|
94
|
+
};
|
|
95
|
+
return { onReady, onStateChange };
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Convenience wrapper — wires to the default Datalyr singleton
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* Auto-wire screen tracking to the default Datalyr singleton.
|
|
102
|
+
* This is the recommended API for most users.
|
|
103
|
+
*
|
|
104
|
+
* ```tsx
|
|
105
|
+
* const navigationRef = useNavigationContainerRef();
|
|
106
|
+
* const screenTracking = datalyrScreenTracking(navigationRef);
|
|
107
|
+
*
|
|
108
|
+
* <NavigationContainer
|
|
109
|
+
* ref={navigationRef}
|
|
110
|
+
* onReady={screenTracking.onReady}
|
|
111
|
+
* onStateChange={screenTracking.onStateChange}
|
|
112
|
+
* />
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @param navigationRef React Navigation container ref
|
|
116
|
+
* @param config Optional screen name transforms and filters
|
|
117
|
+
*/
|
|
118
|
+
export function datalyrScreenTracking(navigationRef, config) {
|
|
119
|
+
// Lazily resolved reference to the SDK singleton.
|
|
120
|
+
// We avoid a top-level import to prevent circular deps (index.ts re-exports this file).
|
|
121
|
+
let sdk = null;
|
|
122
|
+
const getSdk = () => {
|
|
123
|
+
if (!sdk) {
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
125
|
+
sdk = require('./datalyr-sdk').default;
|
|
126
|
+
}
|
|
127
|
+
return sdk;
|
|
128
|
+
};
|
|
129
|
+
return createScreenTrackingListeners(navigationRef, (screenName, properties) => getSdk().screen(screenName, properties), config);
|
|
130
|
+
}
|
package/package.json
CHANGED
package/src/auto-events.ts
CHANGED
|
@@ -195,39 +195,44 @@ export class AutoEventsManager {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
|
-
*
|
|
198
|
+
* Update session counters for a screen view.
|
|
199
|
+
* The actual pageview event is fired by the SDK's screen() method —
|
|
200
|
+
* this only updates internal session state to avoid double-firing.
|
|
199
201
|
*/
|
|
200
|
-
async
|
|
202
|
+
async recordScreenView(screenName: string): Promise<void> {
|
|
201
203
|
try {
|
|
202
204
|
if (!this.config.trackScreenViews) return;
|
|
203
|
-
|
|
204
|
-
// Don't
|
|
205
|
+
|
|
206
|
+
// Don't count the same screen twice in a row
|
|
205
207
|
if (this.lastScreenName === screenName) return;
|
|
206
|
-
|
|
207
|
-
const screenProperties: Record<string, any> = {
|
|
208
|
-
screen_name: screenName,
|
|
209
|
-
previous_screen: this.lastScreenName,
|
|
210
|
-
timestamp: Date.now(),
|
|
211
|
-
...properties,
|
|
212
|
-
};
|
|
213
208
|
|
|
214
|
-
//
|
|
209
|
+
// Update session counters (no event fired here)
|
|
215
210
|
if (this.currentSession) {
|
|
216
211
|
this.currentSession.screenViews++;
|
|
217
|
-
|
|
218
|
-
screenProperties.pageviews_in_session = this.currentSession.screenViews;
|
|
212
|
+
this.currentSession.lastActivity = Date.now();
|
|
219
213
|
}
|
|
220
214
|
|
|
221
|
-
await this.trackEvent('pageview', screenProperties);
|
|
222
|
-
|
|
223
215
|
this.lastScreenName = screenName;
|
|
224
|
-
debugLog('
|
|
216
|
+
debugLog('Screen view counted:', screenName);
|
|
225
217
|
|
|
226
218
|
} catch (error) {
|
|
227
|
-
errorLog('Error
|
|
219
|
+
errorLog('Error updating screen view:', error as Error);
|
|
228
220
|
}
|
|
229
221
|
}
|
|
230
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Get session data to enrich a pageview event.
|
|
225
|
+
* Called by the SDK's screen() method to attach session info.
|
|
226
|
+
*/
|
|
227
|
+
getScreenViewEnrichment(): Record<string, any> | null {
|
|
228
|
+
if (!this.currentSession) return null;
|
|
229
|
+
return {
|
|
230
|
+
session_id: this.currentSession.sessionId,
|
|
231
|
+
pageviews_in_session: this.currentSession.screenViews,
|
|
232
|
+
previous_screen: this.lastScreenName,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
231
236
|
/**
|
|
232
237
|
* Track app launch performance
|
|
233
238
|
*/
|
package/src/datalyr-sdk-expo.ts
CHANGED
|
@@ -196,7 +196,7 @@ export class DatalyrSDKExpo {
|
|
|
196
196
|
const installData = await attributionManager.trackInstall();
|
|
197
197
|
await this.track('app_install', {
|
|
198
198
|
platform: Platform.OS,
|
|
199
|
-
sdk_version: '1.
|
|
199
|
+
sdk_version: '1.7.0',
|
|
200
200
|
sdk_variant: 'expo',
|
|
201
201
|
...installData,
|
|
202
202
|
});
|
|
@@ -248,11 +248,22 @@ export class DatalyrSDKExpo {
|
|
|
248
248
|
...properties,
|
|
249
249
|
};
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
251
|
+
// Enrich with session data (pageview count, previous screen) if available.
|
|
252
|
+
// User-provided properties take precedence over enrichment.
|
|
253
253
|
if (this.autoEventsManager) {
|
|
254
|
-
|
|
254
|
+
const enrichment = this.autoEventsManager.getScreenViewEnrichment();
|
|
255
|
+
if (enrichment) {
|
|
256
|
+
for (const [key, value] of Object.entries(enrichment)) {
|
|
257
|
+
if (!(key in screenData)) {
|
|
258
|
+
screenData[key] = value;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Update session counters (does NOT fire a second event)
|
|
263
|
+
await this.autoEventsManager.recordScreenView(screenName);
|
|
255
264
|
}
|
|
265
|
+
|
|
266
|
+
await this.track('pageview', screenData);
|
|
256
267
|
}
|
|
257
268
|
|
|
258
269
|
async identify(userId: string, properties?: UserProperties): Promise<void> {
|
|
@@ -781,7 +792,7 @@ export class DatalyrSDKExpo {
|
|
|
781
792
|
carrier: deviceInfo.carrier,
|
|
782
793
|
network_type: networkType,
|
|
783
794
|
timestamp: Date.now(),
|
|
784
|
-
sdk_version: '1.
|
|
795
|
+
sdk_version: '1.7.0',
|
|
785
796
|
sdk_variant: 'expo',
|
|
786
797
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
787
798
|
...(advertiserInfo ? {
|
package/src/datalyr-sdk.ts
CHANGED
|
@@ -228,7 +228,7 @@ export class DatalyrSDK {
|
|
|
228
228
|
const installData = await attributionManager.trackInstall();
|
|
229
229
|
await this.track('app_install', {
|
|
230
230
|
platform: Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'android',
|
|
231
|
-
sdk_version: '1.
|
|
231
|
+
sdk_version: '1.7.0',
|
|
232
232
|
...installData,
|
|
233
233
|
});
|
|
234
234
|
}
|
|
@@ -285,12 +285,22 @@ export class DatalyrSDK {
|
|
|
285
285
|
...properties,
|
|
286
286
|
};
|
|
287
287
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
// Also notify auto-events manager for automatic screen tracking
|
|
288
|
+
// Enrich with session data (pageview count, previous screen) if available.
|
|
289
|
+
// User-provided properties take precedence over enrichment.
|
|
291
290
|
if (this.autoEventsManager) {
|
|
292
|
-
|
|
291
|
+
const enrichment = this.autoEventsManager.getScreenViewEnrichment();
|
|
292
|
+
if (enrichment) {
|
|
293
|
+
for (const [key, value] of Object.entries(enrichment)) {
|
|
294
|
+
if (!(key in screenData)) {
|
|
295
|
+
screenData[key] = value;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Update session counters (does NOT fire a second event)
|
|
300
|
+
await this.autoEventsManager.recordScreenView(screenName);
|
|
293
301
|
}
|
|
302
|
+
|
|
303
|
+
await this.track('pageview', screenData);
|
|
294
304
|
}
|
|
295
305
|
|
|
296
306
|
/**
|
|
@@ -1076,7 +1086,7 @@ export class DatalyrSDK {
|
|
|
1076
1086
|
carrier: deviceInfo.carrier,
|
|
1077
1087
|
network_type: getNetworkType(),
|
|
1078
1088
|
timestamp: Date.now(),
|
|
1079
|
-
sdk_version: '1.
|
|
1089
|
+
sdk_version: '1.7.0',
|
|
1080
1090
|
// Advertiser data (IDFA/GAID, ATT status) for server-side postback
|
|
1081
1091
|
...(advertiserInfo ? {
|
|
1082
1092
|
idfa: advertiserInfo.idfa,
|
package/src/expo.ts
CHANGED
|
@@ -56,6 +56,39 @@ export * from './event-queue';
|
|
|
56
56
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
57
57
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
58
58
|
|
|
59
|
+
// Export automatic screen tracking for React Navigation
|
|
60
|
+
export { createScreenTrackingListeners } from './screen-tracking';
|
|
61
|
+
export type { ScreenTrackingConfig, NavigationContainerRef } from './screen-tracking';
|
|
62
|
+
|
|
63
|
+
// Expo-specific convenience: auto-wires to the Expo singleton
|
|
64
|
+
import { createScreenTrackingListeners as _createListeners } from './screen-tracking';
|
|
65
|
+
import type { NavigationContainerRef as _NavRef, ScreenTrackingConfig as _Config } from './screen-tracking';
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Auto-wire screen tracking to the Expo Datalyr singleton.
|
|
69
|
+
*
|
|
70
|
+
* ```tsx
|
|
71
|
+
* const navigationRef = useNavigationContainerRef();
|
|
72
|
+
* const screenTracking = datalyrScreenTracking(navigationRef);
|
|
73
|
+
*
|
|
74
|
+
* <NavigationContainer
|
|
75
|
+
* ref={navigationRef}
|
|
76
|
+
* onReady={screenTracking.onReady}
|
|
77
|
+
* onStateChange={screenTracking.onStateChange}
|
|
78
|
+
* />
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export function datalyrScreenTracking(
|
|
82
|
+
navigationRef: _NavRef,
|
|
83
|
+
config?: _Config,
|
|
84
|
+
): { onReady: () => void; onStateChange: () => void } {
|
|
85
|
+
return _createListeners(
|
|
86
|
+
navigationRef,
|
|
87
|
+
(screenName, properties) => datalyrExpo.screen(screenName, properties),
|
|
88
|
+
config,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
59
92
|
// Export platform integrations
|
|
60
93
|
export { appleSearchAdsIntegration } from './integrations';
|
|
61
94
|
export type { AppleSearchAdsAttribution } from './native/DatalyrNativeBridge';
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,13 @@ export * from './event-queue';
|
|
|
24
24
|
// Also export the SDK class for advanced usage
|
|
25
25
|
export { DatalyrSDK };
|
|
26
26
|
|
|
27
|
+
// Export automatic screen tracking for React Navigation
|
|
28
|
+
export {
|
|
29
|
+
datalyrScreenTracking,
|
|
30
|
+
createScreenTrackingListeners,
|
|
31
|
+
} from './screen-tracking';
|
|
32
|
+
export type { ScreenTrackingConfig, NavigationContainerRef } from './screen-tracking';
|
|
33
|
+
|
|
27
34
|
// Export SKAdNetwork components
|
|
28
35
|
export { ConversionValueEncoder, ConversionTemplates } from './ConversionValueEncoder';
|
|
29
36
|
export { SKAdNetworkBridge } from './native/SKAdNetworkBridge';
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic screen tracking for React Navigation (v5+ / v6+).
|
|
3
|
+
*
|
|
4
|
+
* The simplest integration — just spread onto your NavigationContainer:
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
|
|
8
|
+
* import { datalyrScreenTracking } from '@datalyr/react-native';
|
|
9
|
+
*
|
|
10
|
+
* const navigationRef = useNavigationContainerRef();
|
|
11
|
+
* const screenTracking = datalyrScreenTracking(navigationRef);
|
|
12
|
+
*
|
|
13
|
+
* <NavigationContainer
|
|
14
|
+
* ref={navigationRef}
|
|
15
|
+
* onReady={screenTracking.onReady}
|
|
16
|
+
* onStateChange={screenTracking.onStateChange}
|
|
17
|
+
* />
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Note: If you enable automatic screen tracking, avoid also calling
|
|
21
|
+
* `Datalyr.screen()` / `datalyr.screen()` manually for the same screens,
|
|
22
|
+
* as this will produce duplicate events.
|
|
23
|
+
*
|
|
24
|
+
* For Expo Router (file-based routing), automatic tracking is not needed.
|
|
25
|
+
* Use the `datalyr.screen()` method in your layout files instead.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { debugLog, errorLog } from './utils';
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Minimal React Navigation types — avoids adding @react-navigation as a
|
|
32
|
+
// peer dependency. Only the methods we actually call are listed here.
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/** Minimal subset of React Navigation's NavigationContainerRef that we need. */
|
|
36
|
+
export interface NavigationContainerRef {
|
|
37
|
+
getCurrentRoute(): { name: string; params?: Record<string, any> } | undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Configuration
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
export interface ScreenTrackingConfig {
|
|
45
|
+
/**
|
|
46
|
+
* Transform the route name before tracking.
|
|
47
|
+
* Useful for cleaning up or grouping screen names.
|
|
48
|
+
* @example (name) => name.replace('Screen', '')
|
|
49
|
+
*/
|
|
50
|
+
transformScreenName?: (routeName: string) => string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Filter which screens should be tracked.
|
|
54
|
+
* Return false to skip tracking for a given screen.
|
|
55
|
+
* @example (name) => !['Loading', 'Splash'].includes(name)
|
|
56
|
+
*/
|
|
57
|
+
shouldTrackScreen?: (routeName: string) => boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extract additional properties from the route to include in the screen event.
|
|
61
|
+
* @example (name, params) => ({ product_id: params?.productId })
|
|
62
|
+
*/
|
|
63
|
+
extractProperties?: (routeName: string, params?: Record<string, any>) => Record<string, any>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Core implementation
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create `onReady` and `onStateChange` callbacks that automatically
|
|
72
|
+
* fire screen events through the Datalyr SDK whenever the active
|
|
73
|
+
* React Navigation route changes.
|
|
74
|
+
*
|
|
75
|
+
* @param navigationRef A React Navigation `NavigationContainerRef`
|
|
76
|
+
* (from `useNavigationContainerRef()` or
|
|
77
|
+
* `createNavigationContainerRef()`).
|
|
78
|
+
* @param trackScreen The function that records a screen event.
|
|
79
|
+
* Pass `datalyr.screen.bind(datalyr)` or the
|
|
80
|
+
* Datalyr static class's `Datalyr.screen`.
|
|
81
|
+
* If omitted, the default singleton is used.
|
|
82
|
+
* @param config Optional filtering / transform config.
|
|
83
|
+
*/
|
|
84
|
+
export function createScreenTrackingListeners(
|
|
85
|
+
navigationRef: NavigationContainerRef,
|
|
86
|
+
trackScreen: (screenName: string, properties?: Record<string, any>) => Promise<void>,
|
|
87
|
+
config?: ScreenTrackingConfig,
|
|
88
|
+
): { onReady: () => void; onStateChange: () => void } {
|
|
89
|
+
let currentRouteName: string | undefined;
|
|
90
|
+
|
|
91
|
+
const resolveScreenName = (routeName: string): string =>
|
|
92
|
+
config?.transformScreenName ? config.transformScreenName(routeName) : routeName;
|
|
93
|
+
|
|
94
|
+
const buildProperties = (
|
|
95
|
+
routeName: string,
|
|
96
|
+
params?: Record<string, any>,
|
|
97
|
+
previousRouteName?: string,
|
|
98
|
+
): Record<string, any> => {
|
|
99
|
+
const props: Record<string, any> = { source: 'auto_navigation' };
|
|
100
|
+
|
|
101
|
+
if (previousRouteName) {
|
|
102
|
+
props.previous_screen = resolveScreenName(previousRouteName);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (config?.extractProperties) {
|
|
106
|
+
Object.assign(props, config.extractProperties(routeName, params));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return props;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const shouldTrack = (routeName: string): boolean =>
|
|
113
|
+
!config?.shouldTrackScreen || config.shouldTrackScreen(routeName);
|
|
114
|
+
|
|
115
|
+
const safeTrack = (screenName: string, properties: Record<string, any>) => {
|
|
116
|
+
try {
|
|
117
|
+
trackScreen(screenName, properties).catch((err) => {
|
|
118
|
+
errorLog('Auto screen tracking failed:', err as Error);
|
|
119
|
+
});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
errorLog('Auto screen tracking failed:', err as Error);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const onReady = () => {
|
|
126
|
+
const route = navigationRef.getCurrentRoute();
|
|
127
|
+
if (!route) return;
|
|
128
|
+
|
|
129
|
+
currentRouteName = route.name;
|
|
130
|
+
|
|
131
|
+
if (shouldTrack(route.name)) {
|
|
132
|
+
const screenName = resolveScreenName(route.name);
|
|
133
|
+
safeTrack(screenName, buildProperties(route.name, route.params));
|
|
134
|
+
debugLog('Auto screen tracking: initial screen', screenName);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const onStateChange = () => {
|
|
139
|
+
const route = navigationRef.getCurrentRoute();
|
|
140
|
+
if (!route) return;
|
|
141
|
+
|
|
142
|
+
const previousRouteName = currentRouteName;
|
|
143
|
+
currentRouteName = route.name;
|
|
144
|
+
|
|
145
|
+
// Don't fire for same-screen param changes
|
|
146
|
+
if (previousRouteName === currentRouteName) return;
|
|
147
|
+
|
|
148
|
+
if (!shouldTrack(route.name)) return;
|
|
149
|
+
|
|
150
|
+
const screenName = resolveScreenName(route.name);
|
|
151
|
+
safeTrack(screenName, buildProperties(route.name, route.params, previousRouteName));
|
|
152
|
+
debugLog('Auto screen tracking: navigated to', screenName);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return { onReady, onStateChange };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Convenience wrapper — wires to the default Datalyr singleton
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Auto-wire screen tracking to the default Datalyr singleton.
|
|
164
|
+
* This is the recommended API for most users.
|
|
165
|
+
*
|
|
166
|
+
* ```tsx
|
|
167
|
+
* const navigationRef = useNavigationContainerRef();
|
|
168
|
+
* const screenTracking = datalyrScreenTracking(navigationRef);
|
|
169
|
+
*
|
|
170
|
+
* <NavigationContainer
|
|
171
|
+
* ref={navigationRef}
|
|
172
|
+
* onReady={screenTracking.onReady}
|
|
173
|
+
* onStateChange={screenTracking.onStateChange}
|
|
174
|
+
* />
|
|
175
|
+
* ```
|
|
176
|
+
*
|
|
177
|
+
* @param navigationRef React Navigation container ref
|
|
178
|
+
* @param config Optional screen name transforms and filters
|
|
179
|
+
*/
|
|
180
|
+
export function datalyrScreenTracking(
|
|
181
|
+
navigationRef: NavigationContainerRef,
|
|
182
|
+
config?: ScreenTrackingConfig,
|
|
183
|
+
): { onReady: () => void; onStateChange: () => void } {
|
|
184
|
+
// Lazily resolved reference to the SDK singleton.
|
|
185
|
+
// We avoid a top-level import to prevent circular deps (index.ts re-exports this file).
|
|
186
|
+
let sdk: { screen: (name: string, props?: Record<string, any>) => Promise<void> } | null = null;
|
|
187
|
+
|
|
188
|
+
const getSdk = () => {
|
|
189
|
+
if (!sdk) {
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
191
|
+
sdk = (require('./datalyr-sdk') as { default: typeof sdk }).default;
|
|
192
|
+
}
|
|
193
|
+
return sdk!;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return createScreenTrackingListeners(
|
|
197
|
+
navigationRef,
|
|
198
|
+
(screenName, properties) => getSdk().screen(screenName, properties),
|
|
199
|
+
config,
|
|
200
|
+
);
|
|
201
|
+
}
|