@datalyr/react-native 1.2.1 → 1.3.1
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/CHANGELOG.md +19 -0
- package/README.md +145 -9
- package/android/build.gradle +54 -0
- package/android/src/main/AndroidManifest.xml +14 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrNativeModule.java +423 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrPackage.java +30 -0
- package/android/src/main/java/com/datalyr/reactnative/DatalyrPlayInstallReferrerModule.java +229 -0
- package/datalyr-react-native.podspec +2 -2
- package/ios/DatalyrSKAdNetwork.m +400 -1
- package/ios/PrivacyInfo.xcprivacy +48 -0
- package/lib/ConversionValueEncoder.d.ts +13 -1
- package/lib/ConversionValueEncoder.js +57 -23
- package/lib/datalyr-sdk.d.ts +31 -2
- package/lib/datalyr-sdk.js +138 -30
- package/lib/index.d.ts +5 -1
- package/lib/index.js +4 -1
- package/lib/integrations/index.d.ts +3 -1
- package/lib/integrations/index.js +2 -1
- package/lib/integrations/meta-integration.d.ts +1 -0
- package/lib/integrations/meta-integration.js +4 -3
- package/lib/integrations/play-install-referrer.d.ts +78 -0
- package/lib/integrations/play-install-referrer.js +166 -0
- package/lib/integrations/tiktok-integration.d.ts +1 -0
- package/lib/integrations/tiktok-integration.js +4 -3
- package/lib/journey.d.ts +106 -0
- package/lib/journey.js +258 -0
- package/lib/native/DatalyrNativeBridge.d.ts +42 -3
- package/lib/native/DatalyrNativeBridge.js +63 -9
- package/lib/native/SKAdNetworkBridge.d.ts +142 -0
- package/lib/native/SKAdNetworkBridge.js +328 -0
- package/lib/network-status.d.ts +84 -0
- package/lib/network-status.js +281 -0
- package/lib/types.d.ts +51 -0
- package/lib/utils.d.ts +6 -1
- package/lib/utils.js +52 -2
- package/package.json +13 -4
- package/src/ConversionValueEncoder.ts +67 -26
- package/src/datalyr-sdk-expo.ts +55 -6
- package/src/datalyr-sdk.ts +161 -38
- package/src/expo.ts +4 -0
- package/src/index.ts +7 -1
- package/src/integrations/index.ts +3 -1
- package/src/integrations/meta-integration.ts +4 -3
- package/src/integrations/play-install-referrer.ts +218 -0
- package/src/integrations/tiktok-integration.ts +4 -3
- package/src/journey.ts +338 -0
- package/src/native/DatalyrNativeBridge.ts +99 -13
- package/src/native/SKAdNetworkBridge.ts +481 -2
- package/src/network-status.ts +312 -0
- package/src/types.ts +74 -6
- package/src/utils.ts +62 -6
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export type NetworkState = {
|
|
2
|
+
isConnected: boolean;
|
|
3
|
+
isInternetReachable: boolean | null;
|
|
4
|
+
type: 'wifi' | 'cellular' | 'ethernet' | 'bluetooth' | 'vpn' | 'none' | 'unknown';
|
|
5
|
+
};
|
|
6
|
+
export type NetworkStateListener = (state: NetworkState) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Network status manager that detects online/offline status
|
|
9
|
+
* Uses @react-native-community/netinfo for React Native or expo-network for Expo
|
|
10
|
+
*/
|
|
11
|
+
declare class NetworkStatusManager {
|
|
12
|
+
private state;
|
|
13
|
+
private listeners;
|
|
14
|
+
private unsubscribe;
|
|
15
|
+
private initialized;
|
|
16
|
+
private netInfoModule;
|
|
17
|
+
private expoNetworkModule;
|
|
18
|
+
/**
|
|
19
|
+
* Initialize network status monitoring
|
|
20
|
+
* Call this during SDK initialization
|
|
21
|
+
*/
|
|
22
|
+
initialize(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Initialize with @react-native-community/netinfo
|
|
25
|
+
*/
|
|
26
|
+
private initializeWithNetInfo;
|
|
27
|
+
/**
|
|
28
|
+
* Update state from NetInfo response
|
|
29
|
+
*/
|
|
30
|
+
private updateStateFromNetInfo;
|
|
31
|
+
/**
|
|
32
|
+
* Map NetInfo type to our simplified type
|
|
33
|
+
*/
|
|
34
|
+
private mapNetInfoType;
|
|
35
|
+
/**
|
|
36
|
+
* Initialize with expo-network
|
|
37
|
+
*/
|
|
38
|
+
private initializeWithExpoNetwork;
|
|
39
|
+
/**
|
|
40
|
+
* Update state from expo-network response
|
|
41
|
+
*/
|
|
42
|
+
private updateStateFromExpoNetwork;
|
|
43
|
+
/**
|
|
44
|
+
* Map expo-network type to our simplified type
|
|
45
|
+
*/
|
|
46
|
+
private mapExpoNetworkType;
|
|
47
|
+
/**
|
|
48
|
+
* Poll expo-network for changes (since it doesn't have a listener API)
|
|
49
|
+
*/
|
|
50
|
+
private pollingInterval;
|
|
51
|
+
private startExpoNetworkPolling;
|
|
52
|
+
/**
|
|
53
|
+
* Notify all listeners of state change
|
|
54
|
+
*/
|
|
55
|
+
private notifyListeners;
|
|
56
|
+
/**
|
|
57
|
+
* Get current network state
|
|
58
|
+
*/
|
|
59
|
+
getState(): NetworkState;
|
|
60
|
+
/**
|
|
61
|
+
* Check if device is currently online
|
|
62
|
+
*/
|
|
63
|
+
isOnline(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Get current network type
|
|
66
|
+
*/
|
|
67
|
+
getNetworkType(): NetworkState['type'];
|
|
68
|
+
/**
|
|
69
|
+
* Subscribe to network state changes
|
|
70
|
+
* Returns an unsubscribe function
|
|
71
|
+
*/
|
|
72
|
+
subscribe(listener: NetworkStateListener): () => void;
|
|
73
|
+
/**
|
|
74
|
+
* Refresh network state manually
|
|
75
|
+
* Useful when returning from background
|
|
76
|
+
*/
|
|
77
|
+
refresh(): Promise<NetworkState>;
|
|
78
|
+
/**
|
|
79
|
+
* Cleanup and stop monitoring
|
|
80
|
+
*/
|
|
81
|
+
destroy(): void;
|
|
82
|
+
}
|
|
83
|
+
export declare const networkStatusManager: NetworkStatusManager;
|
|
84
|
+
export default networkStatusManager;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { debugLog, errorLog } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* Network status manager that detects online/offline status
|
|
4
|
+
* Uses @react-native-community/netinfo for React Native or expo-network for Expo
|
|
5
|
+
*/
|
|
6
|
+
class NetworkStatusManager {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.state = {
|
|
9
|
+
isConnected: true, // Default to true until we know otherwise
|
|
10
|
+
isInternetReachable: null,
|
|
11
|
+
type: 'unknown',
|
|
12
|
+
};
|
|
13
|
+
this.listeners = new Set();
|
|
14
|
+
this.unsubscribe = null;
|
|
15
|
+
this.initialized = false;
|
|
16
|
+
this.netInfoModule = null;
|
|
17
|
+
this.expoNetworkModule = null;
|
|
18
|
+
/**
|
|
19
|
+
* Poll expo-network for changes (since it doesn't have a listener API)
|
|
20
|
+
*/
|
|
21
|
+
this.pollingInterval = null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Initialize network status monitoring
|
|
25
|
+
* Call this during SDK initialization
|
|
26
|
+
*/
|
|
27
|
+
async initialize() {
|
|
28
|
+
if (this.initialized) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Try @react-native-community/netinfo first (most common in RN apps)
|
|
32
|
+
try {
|
|
33
|
+
this.netInfoModule = require('@react-native-community/netinfo').default;
|
|
34
|
+
await this.initializeWithNetInfo();
|
|
35
|
+
this.initialized = true;
|
|
36
|
+
debugLog('Network status initialized with @react-native-community/netinfo');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
catch (_a) {
|
|
40
|
+
// Module not available, try expo-network
|
|
41
|
+
}
|
|
42
|
+
// Try expo-network (for Expo apps)
|
|
43
|
+
try {
|
|
44
|
+
this.expoNetworkModule = require('expo-network');
|
|
45
|
+
await this.initializeWithExpoNetwork();
|
|
46
|
+
this.initialized = true;
|
|
47
|
+
debugLog('Network status initialized with expo-network');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
catch (_b) {
|
|
51
|
+
// Module not available
|
|
52
|
+
}
|
|
53
|
+
// Fallback: assume online (no network monitoring available)
|
|
54
|
+
debugLog('No network status module available, defaulting to online');
|
|
55
|
+
this.state = {
|
|
56
|
+
isConnected: true,
|
|
57
|
+
isInternetReachable: true,
|
|
58
|
+
type: 'unknown',
|
|
59
|
+
};
|
|
60
|
+
this.initialized = true;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Initialize with @react-native-community/netinfo
|
|
64
|
+
*/
|
|
65
|
+
async initializeWithNetInfo() {
|
|
66
|
+
const NetInfo = this.netInfoModule;
|
|
67
|
+
// Get initial state
|
|
68
|
+
try {
|
|
69
|
+
const netState = await NetInfo.fetch();
|
|
70
|
+
this.updateStateFromNetInfo(netState);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
errorLog('Failed to fetch initial network state:', error);
|
|
74
|
+
}
|
|
75
|
+
// Subscribe to changes
|
|
76
|
+
this.unsubscribe = NetInfo.addEventListener((netState) => {
|
|
77
|
+
this.updateStateFromNetInfo(netState);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Update state from NetInfo response
|
|
82
|
+
*/
|
|
83
|
+
updateStateFromNetInfo(netState) {
|
|
84
|
+
var _a;
|
|
85
|
+
const previouslyConnected = this.state.isConnected;
|
|
86
|
+
this.state = {
|
|
87
|
+
isConnected: (_a = netState.isConnected) !== null && _a !== void 0 ? _a : true,
|
|
88
|
+
isInternetReachable: netState.isInternetReachable,
|
|
89
|
+
type: this.mapNetInfoType(netState.type),
|
|
90
|
+
};
|
|
91
|
+
// Notify listeners if connection status changed
|
|
92
|
+
if (previouslyConnected !== this.state.isConnected) {
|
|
93
|
+
debugLog(`Network status changed: ${this.state.isConnected ? 'online' : 'offline'} (${this.state.type})`);
|
|
94
|
+
this.notifyListeners();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Map NetInfo type to our simplified type
|
|
99
|
+
*/
|
|
100
|
+
mapNetInfoType(type) {
|
|
101
|
+
switch (type) {
|
|
102
|
+
case 'wifi':
|
|
103
|
+
return 'wifi';
|
|
104
|
+
case 'cellular':
|
|
105
|
+
return 'cellular';
|
|
106
|
+
case 'ethernet':
|
|
107
|
+
return 'ethernet';
|
|
108
|
+
case 'bluetooth':
|
|
109
|
+
return 'bluetooth';
|
|
110
|
+
case 'vpn':
|
|
111
|
+
return 'vpn';
|
|
112
|
+
case 'none':
|
|
113
|
+
return 'none';
|
|
114
|
+
default:
|
|
115
|
+
return 'unknown';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Initialize with expo-network
|
|
120
|
+
*/
|
|
121
|
+
async initializeWithExpoNetwork() {
|
|
122
|
+
const Network = this.expoNetworkModule;
|
|
123
|
+
// Get initial state
|
|
124
|
+
try {
|
|
125
|
+
const networkState = await Network.getNetworkStateAsync();
|
|
126
|
+
this.updateStateFromExpoNetwork(networkState);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
errorLog('Failed to fetch initial network state from expo-network:', error);
|
|
130
|
+
}
|
|
131
|
+
// Note: expo-network doesn't have a listener API like netinfo
|
|
132
|
+
// We'll poll periodically or rely on app state changes
|
|
133
|
+
this.startExpoNetworkPolling();
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Update state from expo-network response
|
|
137
|
+
*/
|
|
138
|
+
updateStateFromExpoNetwork(networkState) {
|
|
139
|
+
var _a, _b;
|
|
140
|
+
const previouslyConnected = this.state.isConnected;
|
|
141
|
+
this.state = {
|
|
142
|
+
isConnected: (_a = networkState.isConnected) !== null && _a !== void 0 ? _a : true,
|
|
143
|
+
isInternetReachable: (_b = networkState.isInternetReachable) !== null && _b !== void 0 ? _b : null,
|
|
144
|
+
type: this.mapExpoNetworkType(networkState.type),
|
|
145
|
+
};
|
|
146
|
+
if (previouslyConnected !== this.state.isConnected) {
|
|
147
|
+
debugLog(`Network status changed: ${this.state.isConnected ? 'online' : 'offline'} (${this.state.type})`);
|
|
148
|
+
this.notifyListeners();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Map expo-network type to our simplified type
|
|
153
|
+
*/
|
|
154
|
+
mapExpoNetworkType(type) {
|
|
155
|
+
switch (type) {
|
|
156
|
+
case 'WIFI':
|
|
157
|
+
return 'wifi';
|
|
158
|
+
case 'CELLULAR':
|
|
159
|
+
return 'cellular';
|
|
160
|
+
case 'ETHERNET':
|
|
161
|
+
return 'ethernet';
|
|
162
|
+
case 'BLUETOOTH':
|
|
163
|
+
return 'bluetooth';
|
|
164
|
+
case 'VPN':
|
|
165
|
+
return 'vpn';
|
|
166
|
+
case 'NONE':
|
|
167
|
+
return 'none';
|
|
168
|
+
default:
|
|
169
|
+
return 'unknown';
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
startExpoNetworkPolling() {
|
|
173
|
+
// Poll every 5 seconds for network changes
|
|
174
|
+
this.pollingInterval = setInterval(async () => {
|
|
175
|
+
try {
|
|
176
|
+
if (this.expoNetworkModule) {
|
|
177
|
+
const networkState = await this.expoNetworkModule.getNetworkStateAsync();
|
|
178
|
+
this.updateStateFromExpoNetwork(networkState);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
// Ignore polling errors
|
|
183
|
+
}
|
|
184
|
+
}, 5000);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Notify all listeners of state change
|
|
188
|
+
*/
|
|
189
|
+
notifyListeners() {
|
|
190
|
+
this.listeners.forEach((listener) => {
|
|
191
|
+
try {
|
|
192
|
+
listener(this.state);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
errorLog('Error in network state listener:', error);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get current network state
|
|
201
|
+
*/
|
|
202
|
+
getState() {
|
|
203
|
+
return { ...this.state };
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Check if device is currently online
|
|
207
|
+
*/
|
|
208
|
+
isOnline() {
|
|
209
|
+
// Consider online if connected OR if we're not sure about internet reachability
|
|
210
|
+
return this.state.isConnected && (this.state.isInternetReachable !== false);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get current network type
|
|
214
|
+
*/
|
|
215
|
+
getNetworkType() {
|
|
216
|
+
return this.state.type;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Subscribe to network state changes
|
|
220
|
+
* Returns an unsubscribe function
|
|
221
|
+
*/
|
|
222
|
+
subscribe(listener) {
|
|
223
|
+
this.listeners.add(listener);
|
|
224
|
+
// Immediately call with current state
|
|
225
|
+
try {
|
|
226
|
+
listener(this.state);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
errorLog('Error calling network state listener:', error);
|
|
230
|
+
}
|
|
231
|
+
// Return unsubscribe function
|
|
232
|
+
return () => {
|
|
233
|
+
this.listeners.delete(listener);
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Refresh network state manually
|
|
238
|
+
* Useful when returning from background
|
|
239
|
+
*/
|
|
240
|
+
async refresh() {
|
|
241
|
+
if (this.netInfoModule) {
|
|
242
|
+
try {
|
|
243
|
+
const netState = await this.netInfoModule.fetch();
|
|
244
|
+
this.updateStateFromNetInfo(netState);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
errorLog('Failed to refresh network state:', error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else if (this.expoNetworkModule) {
|
|
251
|
+
try {
|
|
252
|
+
const networkState = await this.expoNetworkModule.getNetworkStateAsync();
|
|
253
|
+
this.updateStateFromExpoNetwork(networkState);
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
errorLog('Failed to refresh network state from expo-network:', error);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return this.state;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Cleanup and stop monitoring
|
|
263
|
+
*/
|
|
264
|
+
destroy() {
|
|
265
|
+
if (this.unsubscribe) {
|
|
266
|
+
this.unsubscribe();
|
|
267
|
+
this.unsubscribe = null;
|
|
268
|
+
}
|
|
269
|
+
if (this.pollingInterval) {
|
|
270
|
+
clearInterval(this.pollingInterval);
|
|
271
|
+
this.pollingInterval = null;
|
|
272
|
+
}
|
|
273
|
+
this.listeners.clear();
|
|
274
|
+
this.initialized = false;
|
|
275
|
+
debugLog('Network status manager destroyed');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Export singleton instance
|
|
279
|
+
export const networkStatusManager = new NetworkStatusManager();
|
|
280
|
+
// Export for direct access
|
|
281
|
+
export default networkStatusManager;
|
package/lib/types.d.ts
CHANGED
|
@@ -32,32 +32,83 @@ export interface DeferredDeepLinkResult {
|
|
|
32
32
|
adsetId?: string;
|
|
33
33
|
adId?: string;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Core SDK Configuration
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* await Datalyr.initialize({
|
|
41
|
+
* apiKey: 'dk_your_api_key',
|
|
42
|
+
* debug: true,
|
|
43
|
+
* enableAutoEvents: true,
|
|
44
|
+
* enableAttribution: true,
|
|
45
|
+
* skadTemplate: 'ecommerce',
|
|
46
|
+
* meta: { appId: 'FB_APP_ID' },
|
|
47
|
+
* tiktok: { appId: 'APP_ID', tiktokAppId: 'TIKTOK_APP_ID' },
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
35
51
|
export interface DatalyrConfig {
|
|
52
|
+
/** Required: API key from Datalyr dashboard (starts with 'dk_') */
|
|
36
53
|
apiKey: string;
|
|
54
|
+
/** Optional: Workspace ID for multi-workspace setups */
|
|
37
55
|
workspaceId?: string;
|
|
56
|
+
/** Enable console logging for debugging. Default: false */
|
|
38
57
|
debug?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* API endpoint URL. Default: 'https://api.datalyr.com'
|
|
60
|
+
* @deprecated Use `endpoint` instead
|
|
61
|
+
*/
|
|
39
62
|
apiUrl?: string;
|
|
63
|
+
/** API endpoint URL. Default: 'https://api.datalyr.com' */
|
|
40
64
|
endpoint?: string;
|
|
65
|
+
/** Use server-side tracking. Default: true */
|
|
41
66
|
useServerTracking?: boolean;
|
|
67
|
+
/** Maximum retry attempts for failed requests. Default: 3 */
|
|
42
68
|
maxRetries?: number;
|
|
69
|
+
/** Delay between retries in milliseconds. Default: 1000 */
|
|
43
70
|
retryDelay?: number;
|
|
71
|
+
/** Request timeout in milliseconds. Default: 15000 */
|
|
44
72
|
timeout?: number;
|
|
73
|
+
/** Number of events per batch. Default: 10 */
|
|
45
74
|
batchSize?: number;
|
|
75
|
+
/** Interval between automatic flushes in milliseconds. Default: 30000 */
|
|
46
76
|
flushInterval?: number;
|
|
77
|
+
/** Maximum events to store in queue. Default: 100 */
|
|
47
78
|
maxQueueSize?: number;
|
|
79
|
+
/**
|
|
80
|
+
* Maximum events to store in queue. Default: 100
|
|
81
|
+
* @deprecated Use `maxQueueSize` instead
|
|
82
|
+
*/
|
|
48
83
|
maxEventQueueSize?: number;
|
|
84
|
+
/** Respect browser Do Not Track setting. Default: true */
|
|
49
85
|
respectDoNotTrack?: boolean;
|
|
86
|
+
/** Enable automatic event tracking (sessions, app lifecycle). Default: false */
|
|
50
87
|
enableAutoEvents?: boolean;
|
|
88
|
+
/** Enable attribution tracking (deep links, install referrer). Default: false */
|
|
51
89
|
enableAttribution?: boolean;
|
|
90
|
+
/** Enable web-to-app attribution matching via email. Default: true */
|
|
52
91
|
enableWebToAppAttribution?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Auto-events configuration
|
|
94
|
+
* @deprecated Use `autoEventConfig` instead
|
|
95
|
+
*/
|
|
53
96
|
autoEvents?: AutoEventConfig;
|
|
97
|
+
/** Auto-events configuration */
|
|
54
98
|
autoEventConfig?: AutoEventConfig;
|
|
99
|
+
/**
|
|
100
|
+
* Retry configuration
|
|
101
|
+
* @deprecated Use `maxRetries` and `retryDelay` instead
|
|
102
|
+
*/
|
|
55
103
|
retryConfig?: {
|
|
56
104
|
maxRetries: number;
|
|
57
105
|
retryDelay: number;
|
|
58
106
|
};
|
|
107
|
+
/** SKAdNetwork template for automatic conversion value encoding (iOS only) */
|
|
59
108
|
skadTemplate?: 'ecommerce' | 'gaming' | 'subscription';
|
|
109
|
+
/** Meta (Facebook) SDK Configuration */
|
|
60
110
|
meta?: MetaConfig;
|
|
111
|
+
/** TikTok SDK Configuration */
|
|
61
112
|
tiktok?: TikTokConfig;
|
|
62
113
|
}
|
|
63
114
|
export interface EventData {
|
package/lib/utils.d.ts
CHANGED
|
@@ -36,9 +36,14 @@ export declare const getOrCreateAnonymousId: () => Promise<string>;
|
|
|
36
36
|
*/
|
|
37
37
|
export declare const getOrCreateSessionId: () => Promise<string>;
|
|
38
38
|
/**
|
|
39
|
-
* Collect comprehensive device information
|
|
39
|
+
* Collect comprehensive device information (cached after first call)
|
|
40
|
+
* Device info is cached because it rarely changes during app session
|
|
40
41
|
*/
|
|
41
42
|
export declare const getDeviceInfo: () => Promise<DeviceInfoType>;
|
|
43
|
+
/**
|
|
44
|
+
* Clear the cached device info (useful for testing or after app update)
|
|
45
|
+
*/
|
|
46
|
+
export declare const clearDeviceInfoCache: () => void;
|
|
42
47
|
/**
|
|
43
48
|
* Create fingerprint data for attribution
|
|
44
49
|
*/
|
package/lib/utils.js
CHANGED
|
@@ -114,10 +114,43 @@ export const getOrCreateSessionId = async () => {
|
|
|
114
114
|
return generateSessionId(); // Fallback to memory-only ID
|
|
115
115
|
}
|
|
116
116
|
};
|
|
117
|
+
// Cached device info to avoid repeated async calls
|
|
118
|
+
let cachedDeviceInfo = null;
|
|
119
|
+
let deviceInfoPromise = null;
|
|
117
120
|
/**
|
|
118
|
-
* Collect comprehensive device information
|
|
121
|
+
* Collect comprehensive device information (cached after first call)
|
|
122
|
+
* Device info is cached because it rarely changes during app session
|
|
119
123
|
*/
|
|
120
124
|
export const getDeviceInfo = async () => {
|
|
125
|
+
// Return cached info if available
|
|
126
|
+
if (cachedDeviceInfo) {
|
|
127
|
+
return cachedDeviceInfo;
|
|
128
|
+
}
|
|
129
|
+
// If a fetch is already in progress, wait for it (prevents concurrent fetches)
|
|
130
|
+
if (deviceInfoPromise) {
|
|
131
|
+
return deviceInfoPromise;
|
|
132
|
+
}
|
|
133
|
+
// Start fetching and cache the promise
|
|
134
|
+
deviceInfoPromise = fetchDeviceInfoInternal();
|
|
135
|
+
try {
|
|
136
|
+
cachedDeviceInfo = await deviceInfoPromise;
|
|
137
|
+
return cachedDeviceInfo;
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
deviceInfoPromise = null;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Clear the cached device info (useful for testing or after app update)
|
|
145
|
+
*/
|
|
146
|
+
export const clearDeviceInfoCache = () => {
|
|
147
|
+
cachedDeviceInfo = null;
|
|
148
|
+
deviceInfoPromise = null;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Internal function to fetch device info
|
|
152
|
+
*/
|
|
153
|
+
const fetchDeviceInfoInternal = async () => {
|
|
121
154
|
const { width, height } = Dimensions.get('window');
|
|
122
155
|
// If DeviceInfo is not available (like in Expo Go), use fallback
|
|
123
156
|
if (!DeviceInfo) {
|
|
@@ -204,11 +237,28 @@ export const createFingerprintData = async () => {
|
|
|
204
237
|
},
|
|
205
238
|
};
|
|
206
239
|
};
|
|
240
|
+
// Import network status manager for network type detection
|
|
241
|
+
let networkStatusManagerRef = null;
|
|
242
|
+
// Lazy load to avoid circular dependencies
|
|
243
|
+
const getNetworkStatusManager = () => {
|
|
244
|
+
if (!networkStatusManagerRef) {
|
|
245
|
+
try {
|
|
246
|
+
networkStatusManagerRef = require('./network-status').networkStatusManager;
|
|
247
|
+
}
|
|
248
|
+
catch (_a) {
|
|
249
|
+
// Module not loaded yet
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return networkStatusManagerRef;
|
|
253
|
+
};
|
|
207
254
|
/**
|
|
208
255
|
* Get network connection type
|
|
209
256
|
*/
|
|
210
257
|
export const getNetworkType = () => {
|
|
211
|
-
|
|
258
|
+
const manager = getNetworkStatusManager();
|
|
259
|
+
if (manager) {
|
|
260
|
+
return manager.getNetworkType();
|
|
261
|
+
}
|
|
212
262
|
return 'unknown';
|
|
213
263
|
};
|
|
214
264
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalyr/react-native",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Datalyr SDK for React Native & Expo - Server-side attribution tracking with bundled Meta and TikTok SDKs",
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "Datalyr SDK for React Native & Expo - Server-side attribution tracking with bundled Meta and TikTok SDKs for iOS and Android",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"lib/",
|
|
16
16
|
"src/",
|
|
17
17
|
"ios/",
|
|
18
|
+
"android/",
|
|
18
19
|
"README.md",
|
|
19
20
|
"CHANGELOG.md",
|
|
20
21
|
"LICENSE",
|
|
@@ -38,11 +39,15 @@
|
|
|
38
39
|
"tiktok-attribution",
|
|
39
40
|
"google-attribution",
|
|
40
41
|
"ios-attribution",
|
|
42
|
+
"android-attribution",
|
|
41
43
|
"apple-search-ads",
|
|
44
|
+
"play-install-referrer",
|
|
45
|
+
"gclid",
|
|
42
46
|
"conversion-tracking",
|
|
43
47
|
"revenue-optimization",
|
|
44
48
|
"deferred-deep-linking",
|
|
45
|
-
"fbclid"
|
|
49
|
+
"fbclid",
|
|
50
|
+
"ttclid"
|
|
46
51
|
],
|
|
47
52
|
"author": "Datalyr",
|
|
48
53
|
"license": "MIT",
|
|
@@ -54,7 +59,8 @@
|
|
|
54
59
|
"peerDependencies": {
|
|
55
60
|
"react": ">=18.0.0",
|
|
56
61
|
"react-native": ">=0.72.0",
|
|
57
|
-
"react-native-device-info": ">=12.0.0"
|
|
62
|
+
"react-native-device-info": ">=12.0.0",
|
|
63
|
+
"@react-native-community/netinfo": ">=11.0.0"
|
|
58
64
|
},
|
|
59
65
|
"dependencies": {
|
|
60
66
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
@@ -101,6 +107,9 @@
|
|
|
101
107
|
},
|
|
102
108
|
"react-native-device-info": {
|
|
103
109
|
"optional": true
|
|
110
|
+
},
|
|
111
|
+
"@react-native-community/netinfo": {
|
|
112
|
+
"optional": true
|
|
104
113
|
}
|
|
105
114
|
}
|
|
106
115
|
}
|