@truewatchtech/react-native-mobile 0.4.3-alpha.2 → 0.4.3

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.
Files changed (95) hide show
  1. package/FTMobileReactNativeSDK.podspec +1 -1
  2. package/README.md +0 -34
  3. package/android/build.gradle +2 -2
  4. package/android/src/main/java/com/ft/sdk/reactnative/FTLogImpl.java +10 -2
  5. package/android/src/main/java/com/ft/sdk/reactnative/FTMobileImpl.java +45 -3
  6. package/android/src/main/java/com/ft/sdk/reactnative/FTRUMImpl.java +51 -9
  7. package/android/src/main/java/com/ft/sdk/reactnative/FTTraceImpl.java +5 -2
  8. package/android/src/main/java/com/ft/sdk/reactnative/ViewTiming.java +17 -0
  9. package/android/src/main/java/com/ft/sdk/reactnative/VisitorIdConfigMapper.java +50 -0
  10. package/android/src/newarch/java/com/ft/sdk/reactnative/FTMobileModule.java +5 -0
  11. package/android/src/newarch/java/com/ft/sdk/reactnative/FTRUMModule.java +18 -0
  12. package/android/src/oldarch/java/com/ft/sdk/reactnative/FTMobileModule.java +5 -0
  13. package/android/src/oldarch/java/com/ft/sdk/reactnative/FTRUMModule.java +15 -0
  14. package/android/src/test/java/com/ft/sdk/reactnative/FTMobileImplTest.java +53 -0
  15. package/ios/FTMobileReactNative.mm +37 -2
  16. package/ios/FTReactNativeLog.mm +7 -2
  17. package/ios/FTReactNativeRUM.mm +94 -4
  18. package/ios/FTReactNativeResource.m +6 -0
  19. package/ios/FTReactNativeTrace.mm +4 -2
  20. package/ios/FTReactNativeViewTiming.h +18 -0
  21. package/ios/FTWebSocketResourceData.h +4 -0
  22. package/ios/FTWebSocketResourceData.m +31 -0
  23. package/lib/commonjs/ft_logger.js +3 -1
  24. package/lib/commonjs/ft_logger.js.map +1 -1
  25. package/lib/commonjs/ft_mobile_agent.js +13 -1
  26. package/lib/commonjs/ft_mobile_agent.js.map +1 -1
  27. package/lib/commonjs/ft_rum.js +19 -3
  28. package/lib/commonjs/ft_rum.js.map +1 -1
  29. package/lib/commonjs/ft_tracing.js +2 -1
  30. package/lib/commonjs/ft_tracing.js.map +1 -1
  31. package/lib/commonjs/index.js +7 -0
  32. package/lib/commonjs/index.js.map +1 -1
  33. package/lib/commonjs/rum/FTRumViewTracking.js +147 -0
  34. package/lib/commonjs/rum/FTRumViewTracking.js.map +1 -0
  35. package/lib/commonjs/rum/FTRumWebSocketNative.js.map +1 -1
  36. package/lib/commonjs/rum/FTRumWebSocketTiming.js +19 -1
  37. package/lib/commonjs/rum/FTRumWebSocketTiming.js.map +1 -1
  38. package/lib/commonjs/rum/FTRumWebSocketTracking.js +7 -11
  39. package/lib/commonjs/rum/FTRumWebSocketTracking.js.map +1 -1
  40. package/lib/commonjs/specs/NativeFTMobileReactNative.js.map +1 -1
  41. package/lib/commonjs/specs/NativeFTReactNativeRUM.js.map +1 -1
  42. package/lib/commonjs/version.js +1 -1
  43. package/lib/commonjs/version.js.map +1 -1
  44. package/lib/commonjs/visitorIdConfig.js +37 -0
  45. package/lib/commonjs/visitorIdConfig.js.map +1 -0
  46. package/lib/module/ft_logger.js +3 -1
  47. package/lib/module/ft_logger.js.map +1 -1
  48. package/lib/module/ft_mobile_agent.js +13 -1
  49. package/lib/module/ft_mobile_agent.js.map +1 -1
  50. package/lib/module/ft_rum.js +19 -3
  51. package/lib/module/ft_rum.js.map +1 -1
  52. package/lib/module/ft_tracing.js +2 -1
  53. package/lib/module/ft_tracing.js.map +1 -1
  54. package/lib/module/index.js +1 -0
  55. package/lib/module/index.js.map +1 -1
  56. package/lib/module/rum/FTRumViewTracking.js +140 -0
  57. package/lib/module/rum/FTRumViewTracking.js.map +1 -0
  58. package/lib/module/rum/FTRumWebSocketNative.js.map +1 -1
  59. package/lib/module/rum/FTRumWebSocketTiming.js +19 -1
  60. package/lib/module/rum/FTRumWebSocketTiming.js.map +1 -1
  61. package/lib/module/rum/FTRumWebSocketTracking.js +7 -11
  62. package/lib/module/rum/FTRumWebSocketTracking.js.map +1 -1
  63. package/lib/module/specs/NativeFTMobileReactNative.js.map +1 -1
  64. package/lib/module/specs/NativeFTReactNativeRUM.js.map +1 -1
  65. package/lib/module/version.js +1 -1
  66. package/lib/module/version.js.map +1 -1
  67. package/lib/module/visitorIdConfig.js +31 -0
  68. package/lib/module/visitorIdConfig.js.map +1 -0
  69. package/lib/typescript/ft_logger.d.ts +11 -1
  70. package/lib/typescript/ft_mobile_agent.d.ts +25 -0
  71. package/lib/typescript/ft_rum.d.ts +33 -5
  72. package/lib/typescript/ft_tracing.d.ts +9 -1
  73. package/lib/typescript/index.d.ts +2 -0
  74. package/lib/typescript/rum/FTRumViewTracking.d.ts +32 -0
  75. package/lib/typescript/rum/FTRumWebSocketNative.d.ts +2 -3
  76. package/lib/typescript/rum/FTRumWebSocketTiming.d.ts +6 -1
  77. package/lib/typescript/rum/FTRumWebSocketTracking.d.ts +2 -4
  78. package/lib/typescript/specs/NativeFTMobileReactNative.d.ts +1 -0
  79. package/lib/typescript/specs/NativeFTReactNativeRUM.d.ts +7 -1
  80. package/lib/typescript/version.d.ts +1 -1
  81. package/lib/typescript/visitorIdConfig.d.ts +11 -0
  82. package/package.json +4 -3
  83. package/src/ft_logger.tsx +11 -1
  84. package/src/ft_mobile_agent.tsx +34 -1
  85. package/src/ft_rum.tsx +50 -5
  86. package/src/ft_tracing.tsx +9 -1
  87. package/src/index.tsx +4 -0
  88. package/src/rum/FTRumViewTracking.ts +179 -0
  89. package/src/rum/FTRumWebSocketNative.ts +2 -1
  90. package/src/rum/FTRumWebSocketTiming.ts +34 -2
  91. package/src/rum/FTRumWebSocketTracking.ts +9 -14
  92. package/src/specs/NativeFTMobileReactNative.ts +1 -0
  93. package/src/specs/NativeFTReactNativeRUM.ts +15 -1
  94. package/src/version.ts +1 -1
  95. package/src/visitorIdConfig.ts +123 -0
@@ -21,7 +21,7 @@ Pod::Spec.new do |s|
21
21
 
22
22
 
23
23
  s.dependency "React-Core"
24
- s.dependency 'TrueWatchSDK', '1.6.7'
24
+ s.dependency 'TrueWatchSDK', '1.6.8'
25
25
 
26
26
  xcconfig = {
27
27
  "HEADER_SEARCH_PATHS" => "$(inherited) " +
package/README.md CHANGED
@@ -28,40 +28,6 @@ import {
28
28
 
29
29
  ```
30
30
 
31
- ## WebSocket handshake tracking
32
-
33
- To collect iOS WebSocket handshakes while native Resource auto-collection is disabled:
34
-
35
- ```ts
36
- await FTReactNativeRUM.setConfig({
37
- androidAppId: 'YOUR_ANDROID_APP_ID',
38
- iOSAppId: 'YOUR_IOS_APP_ID',
39
- enableNativeUserResource: false,
40
- enableIOSWebSocketResource: true,
41
- });
42
- ```
43
-
44
- `enableIOSWebSocketResource` defaults to `false` and is independent of
45
- `enableNativeUserResource`. It only affects iOS; Android WebSocket collection
46
- continues through the native SDK under `enableNativeUserResource`.
47
- When disabled, new iOS connections are not instrumented or injected with
48
- WebSocket Trace headers. Handshakes already started can finish normally.
49
- When enabled, Trace injection additionally requires `enableNativeAutoTrace`.
50
- Only connections created after tracking starts are observed.
51
-
52
- iOS starts JS capture and native setup together, without waiting for a native
53
- readiness reply. JS measures the handshake and injects Trace headers; the native
54
- bridge handles Resource reporting and optionally adds a response snapshot.
55
- Missing socket association keeps the native URL filtering, sampling and privacy
56
- path. Only older native packages without this bridge use the legacy JS route.
57
- Connections created before the hooks are installed may have no native snapshot.
58
-
59
- iOS handshake tracking reads `error.message` first. When it is absent, the SDK
60
- can use `close.reason` from the immediately following abnormal close (`1006`),
61
- before the next microtask. The first error still ends handshake timing; normal
62
- close reasons are not collected. Native HTTP response snapshots take precedence
63
- over error-text inference.
64
-
65
31
  ## More
66
32
 
67
33
  [View Doc Here](https://docs.truewatch.com/real-user-monitoring/react-native/app-access/)
@@ -185,8 +185,8 @@ repositories {
185
185
  dependencies {
186
186
  // noinspection GradleDynamicVersion
187
187
  implementation 'com.facebook.react:react-native:+'
188
- implementation 'com.truewatch.ft.mobile.sdk.tracker.agent:ft-sdk:1.7.5'
189
- implementation 'com.truewatch.ft.mobile.sdk.tracker.agent:ft-native:1.1.2'
188
+ implementation 'com.truewatch.ft.mobile.sdk.tracker.agent:ft-sdk:1.7.6'
189
+ implementation 'com.truewatch.ft.mobile.sdk.tracker.agent:ft-native:1.1.4'
190
190
  // implementation files('../example/android/app/libs/ft-sdk-debug.aar')
191
191
  // implementation files('../example/android/app/libs/ft-session-replay-debug.aar')
192
192
 
@@ -21,8 +21,10 @@ public class FTLogImpl {
21
21
  Map<String, Object> map = context.toHashMap();
22
22
  Integer discardStrategy = ReactNativeUtils.convertToNativeInt(map.get("discardStrategy"));
23
23
  Double sampleRate = (Double) map.get("sampleRate");
24
+ Double logSamplingRate = (Double) map.get("logSamplingRate");
24
25
  ArrayList<Object> logTypeReadArr = (ArrayList<Object>) map.get("logLevelFilters");
25
26
  Boolean enableLinkRumData = (Boolean) map.get("enableLinkRumData");
27
+ Boolean enableWebViewLog = (Boolean) map.get("enableWebViewLog");
26
28
  Boolean enableCustomLog = (Boolean) map.get("enableCustomLog");
27
29
  HashMap<String, Object> globalContext = (HashMap<String, Object>) map.get("globalContext");
28
30
  Integer logCacheLimitCount = ReactNativeUtils.convertToNativeInt(map.get("logCacheLimitCount"));
@@ -47,8 +49,10 @@ public class FTLogImpl {
47
49
  FTLoggerConfig logConfig = new FTLoggerConfig()
48
50
  .setLogCacheDiscardStrategy(logCacheDiscard);
49
51
 
50
- if (sampleRate != null) {
51
- logConfig.setSamplingRate(sampleRate.floatValue());
52
+ if (logSamplingRate != null) {
53
+ logConfig.setLogSamplingRate(logSamplingRate.floatValue());
54
+ } else if (sampleRate != null) {
55
+ logConfig.setLogSamplingRate((float) (sampleRate * 100.0));
52
56
  }
53
57
 
54
58
  if (logTypeReadArr != null) {
@@ -70,6 +74,10 @@ public class FTLogImpl {
70
74
  logConfig.setEnableLinkRumData(enableLinkRumData);
71
75
  }
72
76
 
77
+ if (enableWebViewLog != null) {
78
+ logConfig.setEnableWebViewLog(enableWebViewLog);
79
+ }
80
+
73
81
  if (enableCustomLog != null) {
74
82
  logConfig.setEnableCustomLog(enableCustomLog);
75
83
  }
@@ -171,6 +171,24 @@ public class FTMobileImpl {
171
171
  if (override.hasKey("logSampleRate") && !override.isNull("logSampleRate")) {
172
172
  configBean.setLogSampleRate((float) override.getDouble("logSampleRate"));
173
173
  }
174
+ if (override.hasKey("sessionSamplingRate") && !override.isNull("sessionSamplingRate")) {
175
+ configBean.setSessionSamplingRate((float) override.getDouble("sessionSamplingRate"));
176
+ }
177
+ if (override.hasKey("sessionOnErrorSamplingRate") && !override.isNull("sessionOnErrorSamplingRate")) {
178
+ configBean.setSessionOnErrorSamplingRate((float) override.getDouble("sessionOnErrorSamplingRate"));
179
+ }
180
+ if (override.hasKey("traceSamplingRate") && !override.isNull("traceSamplingRate")) {
181
+ configBean.setTraceSamplingRate((float) override.getDouble("traceSamplingRate"));
182
+ }
183
+ if (override.hasKey("logSamplingRate") && !override.isNull("logSamplingRate")) {
184
+ configBean.setLogSamplingRate((float) override.getDouble("logSamplingRate"));
185
+ }
186
+ if (override.hasKey("sessionReplaySamplingRate") && !override.isNull("sessionReplaySamplingRate")) {
187
+ configBean.setSessionReplaySamplingRate((float) override.getDouble("sessionReplaySamplingRate"));
188
+ }
189
+ if (override.hasKey("sessionReplayOnErrorSamplingRate") && !override.isNull("sessionReplayOnErrorSamplingRate")) {
190
+ configBean.setSessionReplayOnErrorSamplingRate((float) override.getDouble("sessionReplayOnErrorSamplingRate"));
191
+ }
174
192
  if (override.hasKey("logLevelFilters") && !override.isNull("logLevelFilters")) {
175
193
  ReadableArray filtersArray = override.getArray("logLevelFilters");
176
194
  String[] filters = new String[filtersArray.size()];
@@ -182,6 +200,9 @@ public class FTMobileImpl {
182
200
  if (override.hasKey("logEnableCustomLog") && !override.isNull("logEnableCustomLog")) {
183
201
  configBean.setLogEnableCustomLog(override.getBoolean("logEnableCustomLog"));
184
202
  }
203
+ if (override.hasKey("logEnableWebViewLog") && !override.isNull("logEnableWebViewLog")) {
204
+ configBean.setLogEnableWebViewLog(override.getBoolean("logEnableWebViewLog"));
205
+ }
185
206
  if (override.hasKey("logEnableConsoleLog") && !override.isNull("logEnableConsoleLog")) {
186
207
  configBean.setLogEnableConsoleLog(override.getBoolean("logEnableConsoleLog"));
187
208
  }
@@ -367,6 +388,19 @@ public class FTMobileImpl {
367
388
  return convertedFilters;
368
389
  }
369
390
 
391
+ static FTSDKConfig buildSDKConfig(@Nullable String datakitUrl,
392
+ @Nullable String datawayUrl,
393
+ @Nullable String clientToken) {
394
+ if (datakitUrl != null && datakitUrl.length() > 0) {
395
+ return FTSDKConfig.builder(datakitUrl);
396
+ }
397
+ if (datawayUrl != null && datawayUrl.length() > 0
398
+ && clientToken != null && clientToken.length() > 0) {
399
+ return FTSDKConfig.builder(datawayUrl, clientToken);
400
+ }
401
+ return FTSDKConfig.builder();
402
+ }
403
+
370
404
  public void sdkConfig(ReadableMap context, Promise promise) {
371
405
  Map<String, Object> map = context.toHashMap();
372
406
  String datakitUrl = (String) map.get("datakitUrl");
@@ -393,9 +427,12 @@ public class FTMobileImpl {
393
427
  this.remoteConfigOverrideRules = context.hasKey("remoteConfigOverrideRules")
394
428
  ? context.getArray("remoteConfigOverrideRules") : null;
395
429
 
396
- FTSDKConfig sdkConfig = (datakitUrl != null)
397
- ? FTSDKConfig.builder(datakitUrl)
398
- : FTSDKConfig.builder(datawayUrl, cliToken);
430
+ FTSDKConfig sdkConfig = buildSDKConfig(datakitUrl, datawayUrl, cliToken);
431
+
432
+ if (map.containsKey("allowWebViewHost")) {
433
+ List<String> hosts = (List<String>) map.get("allowWebViewHost");
434
+ sdkConfig.setAllowWebViewHost(hosts == null ? null : hosts.toArray(new String[0]));
435
+ }
399
436
 
400
437
  if (env != null) {
401
438
  EnvType envType;
@@ -523,6 +560,11 @@ public class FTMobileImpl {
523
560
  promise.resolve(null);
524
561
  }
525
562
 
563
+ public void resetVisitorId(Promise promise) {
564
+ FTSdk.resetVisitorId();
565
+ promise.resolve(null);
566
+ }
567
+
526
568
  public void bindRUMUserData(String userId, String userName, String userEmail, ReadableMap extra, Promise promise) {
527
569
  UserData userData = new UserData();
528
570
  userData.setId(userId);
@@ -14,6 +14,7 @@ import com.ft.sdk.FTRUMGlobalManager;
14
14
  import com.ft.sdk.FTSdk;
15
15
  import com.ft.sdk.RUMCacheDiscard;
16
16
  import com.ft.sdk.garble.bean.AppState;
17
+ import com.ft.sdk.garble.bean.ViewTrackingType;
17
18
  import com.ft.sdk.garble.bean.NetStatusBean;
18
19
  import com.ft.sdk.garble.bean.ResourceParams;
19
20
  import com.ft.sdk.reactnative.utils.ReactNativeUtils;
@@ -47,9 +48,18 @@ public class FTRUMImpl implements LifecycleEventListener {
47
48
  @ReactMethod
48
49
  public void setConfig(ReadableMap context, Promise promise) {
49
50
  Map<String, Object> map = context.toHashMap();
51
+ com.ft.sdk.FTVisitorIdConfig visitorIdConfig;
52
+ try {
53
+ visitorIdConfig = VisitorIdConfigMapper.fromMap(map.get("visitorIdConfig"));
54
+ } catch (IllegalArgumentException error) {
55
+ promise.reject("E_VISITOR_ID_CONFIG", error.getMessage(), error);
56
+ return;
57
+ }
50
58
  String rumAppId = (String) map.get("androidAppId");
51
59
  Double sampleRate = (Double) map.get("sampleRate");
60
+ Double sessionSamplingRate = (Double) map.get("sessionSamplingRate");
52
61
  Double sessionOnErrorSampleRate = (Double) map.get("sessionOnErrorSampleRate");
62
+ Double sessionOnErrorSamplingRate = (Double) map.get("sessionOnErrorSamplingRate");
53
63
 
54
64
  Boolean enableNativeUserAction = (Boolean) map.get("enableNativeUserAction");
55
65
  Boolean enableNativeUserView = (Boolean) map.get("enableNativeUserView");
@@ -70,12 +80,16 @@ public class FTRUMImpl implements LifecycleEventListener {
70
80
  Boolean enableTraceWebView = (Boolean) map.get("enableTraceWebView");
71
81
  ArrayList<String> allowWebViewHost = (ArrayList<String>) map.get("allowWebViewHost");
72
82
 
73
- FTRUMConfig rumConfig = new FTRUMConfig().setRumAppId(rumAppId);
74
- if (sampleRate != null) {
75
- rumConfig.setSamplingRate(sampleRate.floatValue());
83
+ FTRUMConfig rumConfig = new FTRUMConfig().setRumAppId(rumAppId).setVisitorIdConfig(visitorIdConfig);
84
+ if (sessionSamplingRate != null) {
85
+ rumConfig.setSessionSamplingRate(sessionSamplingRate.floatValue());
86
+ } else if (sampleRate != null) {
87
+ rumConfig.setSessionSamplingRate((float) (sampleRate * 100.0));
76
88
  }
77
- if (sessionOnErrorSampleRate != null) {
78
- rumConfig.setSessionErrorSampleRate(sessionOnErrorSampleRate.floatValue());
89
+ if (sessionOnErrorSamplingRate != null) {
90
+ rumConfig.setSessionOnErrorSamplingRate(sessionOnErrorSamplingRate.floatValue());
91
+ } else if (sessionOnErrorSampleRate != null) {
92
+ rumConfig.setSessionOnErrorSamplingRate((float) (sessionOnErrorSampleRate * 100.0));
79
93
  }
80
94
  if (enableNativeUserAction != null) {
81
95
  rumConfig.setEnableTraceUserAction(enableNativeUserAction);
@@ -153,10 +167,8 @@ public class FTRUMImpl implements LifecycleEventListener {
153
167
  if (allowWebViewHost != null) {
154
168
  String[] allowWebViewHostArr = new String[allowWebViewHost.size()];
155
169
  allowWebViewHost.toArray(allowWebViewHostArr);
156
- FTSdk ftSdk = FTSdk.get();
157
- if (ftSdk != null) {
158
- ftSdk.getBaseConfig().setAllowWebViewHost(allowWebViewHostArr);
159
- }
170
+ // Let the native SDK apply legacy hosts only when base hosts are omitted.
171
+ rumConfig.setAllowWebViewHost(allowWebViewHostArr);
160
172
  }
161
173
 
162
174
  FTSdk.initRUMWithConfig(rumConfig);
@@ -242,6 +254,36 @@ public class FTRUMImpl implements LifecycleEventListener {
242
254
  promise.resolve(null);
243
255
  }
244
256
 
257
+ @ReactMethod
258
+ public void onCreateViewWithLoadStart(String viewName, double duration, double loadStartEpochMs, Promise promise) {
259
+ long nanoseconds = ViewTiming.durationNanoseconds(duration);
260
+ FTRUMGlobalManager.get().onCreateView(viewName, nanoseconds,
261
+ nanoseconds > 0 ? ViewTiming.epochNanoseconds(loadStartEpochMs) : 0);
262
+ promise.resolve(null);
263
+ }
264
+
265
+ @ReactMethod
266
+ public void startViewWithTrackingType(String viewName, ReadableMap map, double trackingType, Promise promise) {
267
+ if (trackingType != 0 && trackingType != 1) {
268
+ promise.reject("E_VIEW_TRACKING_TYPE", "trackingType must be Manual (0) or Automatic (1)");
269
+ return;
270
+ }
271
+ FTRUMGlobalManager.get().startView(viewName, map == null ? null : map.toHashMap(),
272
+ trackingType == 1 ? ViewTrackingType.AUTOMATIC : ViewTrackingType.MANUAL);
273
+ promise.resolve(null);
274
+ }
275
+
276
+ @ReactMethod
277
+ public void updateViewLoadingTime(double duration, Promise promise) {
278
+ if (Double.isNaN(duration) || Double.isInfinite(duration) || duration < 0
279
+ || duration > 9007199254740991d || duration != Math.floor(duration)) {
280
+ promise.reject("E_VIEW_LOADING_TIME", "duration must be a non-negative safe integer in nanoseconds");
281
+ return;
282
+ }
283
+ FTRUMGlobalManager.get().updateLoadTime((long) duration);
284
+ promise.resolve(null);
285
+ }
286
+
245
287
  @ReactMethod
246
288
  public void onCreateView(String viewName, Double duration, Promise promise) {
247
289
  FTRUMGlobalManager.get().onCreateView(viewName, duration.longValue());
@@ -21,13 +21,16 @@ public class FTTraceImpl {
21
21
  public void setConfig(ReadableMap context, Promise promise) {
22
22
  Map<String, Object> map = context.toHashMap();
23
23
  Double sampleRate = (Double) map.get("sampleRate");
24
+ Double traceSamplingRate = (Double) map.get("traceSamplingRate");
24
25
  Integer traceType = ReactNativeUtils.convertToNativeInt(map.get("traceType"));
25
26
  Boolean enableLinkRUMData = (Boolean) map.get("enableLinkRUMData");
26
27
  Boolean enableNativeAutoTrace = (Boolean) map.get("enableNativeAutoTrace");
27
28
 
28
29
  FTTraceConfig traceConfig = new FTTraceConfig();
29
- if (sampleRate != null) {
30
- traceConfig.setSamplingRate(sampleRate.floatValue());
30
+ if (traceSamplingRate != null) {
31
+ traceConfig.setTraceSamplingRate(traceSamplingRate.floatValue());
32
+ } else if (sampleRate != null) {
33
+ traceConfig.setTraceSamplingRate((float) (sampleRate * 100.0));
31
34
  }
32
35
 
33
36
  if (traceType != null) {
@@ -0,0 +1,17 @@
1
+ package com.ft.sdk.reactnative;
2
+
3
+ final class ViewTiming {
4
+ private ViewTiming() {}
5
+
6
+ static long durationNanoseconds(double value) {
7
+ return (!Double.isNaN(value) && !Double.isInfinite(value)) && value > 0 && value < 0x1p63 ? (long) value : 0;
8
+ }
9
+
10
+ static long epochNanoseconds(double milliseconds) {
11
+ if ((Double.isNaN(milliseconds) || Double.isInfinite(milliseconds)) || milliseconds <= 0 || milliseconds >= 0x1p63 / 1000000.0) return 0;
12
+ long whole = (long) milliseconds;
13
+ long fraction = (long) ((milliseconds - whole) * 1000000.0);
14
+ if (whole > Long.MAX_VALUE / 1000000 || fraction > Long.MAX_VALUE - whole * 1000000) return 0;
15
+ return whole * 1000000 + fraction;
16
+ }
17
+ }
@@ -0,0 +1,50 @@
1
+ package com.ft.sdk.reactnative;
2
+
3
+ import com.ft.sdk.FTVisitorIdConfig;
4
+ import java.net.URI;
5
+ import java.util.List;
6
+ import java.util.Map;
7
+ import java.util.regex.Pattern;
8
+
9
+ final class VisitorIdConfigMapper {
10
+ private VisitorIdConfigMapper() {}
11
+
12
+ static FTVisitorIdConfig fromMap(Object value) {
13
+ if (value == null) return null;
14
+ if (!(value instanceof Map)) throw new IllegalArgumentException("visitorIdConfig must be an object");
15
+ Map<?, ?> map = (Map<?, ?>) value;
16
+ if (!(map.get("header") instanceof String)) throw new IllegalArgumentException("Visitor ID header is required");
17
+ FTVisitorIdConfig config = new FTVisitorIdConfig().setHeader((String) map.get("header"));
18
+ if (map.containsKey("ttlHours")) {
19
+ if (!(map.get("ttlHours") instanceof Number)) throw new IllegalArgumentException("Visitor ID TTL must be numeric");
20
+ double hours = ((Number) map.get("ttlHours")).doubleValue();
21
+ double milliseconds = hours * 3600000;
22
+ if (Double.isNaN(hours) || Double.isInfinite(hours) || milliseconds < 1 || milliseconds >= Long.MAX_VALUE) {
23
+ throw new IllegalArgumentException("Visitor ID TTL is out of range");
24
+ }
25
+ config.setTtlHours(hours);
26
+ }
27
+ int rules = 0;
28
+ for (String key : new String[]{"urlPrefixes", "regularExpressions"}) {
29
+ Object values = map.get(key);
30
+ if (values == null && !map.containsKey(key)) continue;
31
+ if (!(values instanceof List)) throw new IllegalArgumentException(key + " must be an array");
32
+ for (Object item : (List<?>) values) {
33
+ if (!(item instanceof String) || ((String) item).isEmpty()) throw new IllegalArgumentException("Invalid Visitor ID rule");
34
+ String rule = (String) item;
35
+ if ("urlPrefixes".equals(key)) {
36
+ URI uri = URI.create(rule);
37
+ if (uri.getHost() == null || !("http".equalsIgnoreCase(uri.getScheme()) || "https".equalsIgnoreCase(uri.getScheme()))) {
38
+ throw new IllegalArgumentException("Visitor ID prefix must be an HTTP(S) URL");
39
+ }
40
+ config.addMatch(rule);
41
+ } else {
42
+ config.addMatch(Pattern.compile(rule));
43
+ }
44
+ rules++;
45
+ }
46
+ }
47
+ if (rules == 0) throw new IllegalArgumentException("Visitor ID requires URL matching rules");
48
+ return config;
49
+ }
50
+ }
@@ -39,6 +39,11 @@ public class FTMobileModule extends NativeFTMobileReactNativeSpec {
39
39
  impl.bindRUMUserData(userId, userName, userEmail, extra, promise);
40
40
  }
41
41
 
42
+ @ReactMethod
43
+ public void resetVisitorId(Promise promise) {
44
+ impl.resetVisitorId(promise);
45
+ }
46
+
42
47
  @ReactMethod
43
48
  public void unbindRUMUserData(Promise promise) {
44
49
  impl.unbindRUMUserData(promise);
@@ -58,6 +58,24 @@ public class FTRUMModule extends NativeFTReactNativeRUMSpec {
58
58
  impl.addAction(actionName, actionType, map, promise);
59
59
  }
60
60
 
61
+ @Override
62
+ @ReactMethod
63
+ public void onCreateViewWithLoadStart(String viewName, double duration, double loadStartEpochMs, Promise promise) {
64
+ impl.onCreateViewWithLoadStart(viewName, duration, loadStartEpochMs, promise);
65
+ }
66
+
67
+ @Override
68
+ @ReactMethod
69
+ public void startViewWithTrackingType(String viewName, ReadableMap map, double trackingType, Promise promise) {
70
+ impl.startViewWithTrackingType(viewName, map, trackingType, promise);
71
+ }
72
+
73
+ @Override
74
+ @ReactMethod
75
+ public void updateViewLoadingTime(double duration, Promise promise) {
76
+ impl.updateViewLoadingTime(duration, promise);
77
+ }
78
+
61
79
  @Override
62
80
  @ReactMethod
63
81
  public void onCreateView(String viewName, double duration, Promise promise) {
@@ -38,6 +38,11 @@ public class FTMobileModule extends ReactContextBaseJavaModule {
38
38
  impl.bindRUMUserData(userId, userName, userEmail, extra, promise);
39
39
  }
40
40
 
41
+ @ReactMethod
42
+ public void resetVisitorId(Promise promise) {
43
+ impl.resetVisitorId(promise);
44
+ }
45
+
41
46
  @ReactMethod
42
47
  public void unbindRUMUserData(Promise promise) {
43
48
  impl.unbindRUMUserData(promise);
@@ -56,6 +56,21 @@ public class FTRUMModule extends ReactContextBaseJavaModule {
56
56
  impl.addAction(actionName, actionType, map, promise);
57
57
  }
58
58
 
59
+ @ReactMethod
60
+ public void onCreateViewWithLoadStart(String viewName, double duration, double loadStartEpochMs, Promise promise) {
61
+ impl.onCreateViewWithLoadStart(viewName, duration, loadStartEpochMs, promise);
62
+ }
63
+
64
+ @ReactMethod
65
+ public void startViewWithTrackingType(String viewName, ReadableMap map, double trackingType, Promise promise) {
66
+ impl.startViewWithTrackingType(viewName, map, trackingType, promise);
67
+ }
68
+
69
+ @ReactMethod
70
+ public void updateViewLoadingTime(double duration, Promise promise) {
71
+ impl.updateViewLoadingTime(duration, promise);
72
+ }
73
+
59
74
  @ReactMethod
60
75
  public void onCreateView(String viewName, Double duration, Promise promise) {
61
76
  impl.onCreateView(viewName, duration, promise);
@@ -0,0 +1,53 @@
1
+ package com.ft.sdk.reactnative;
2
+
3
+ import static org.junit.Assert.assertEquals;
4
+
5
+ import com.ft.sdk.FTSDKConfig;
6
+
7
+ import org.junit.Test;
8
+
9
+ public class FTMobileImplTest {
10
+ @Test
11
+ public void buildsConfigWithoutInitialUploadAddress() {
12
+ FTSDKConfig config = FTMobileImpl.buildSDKConfig(null, null, null);
13
+
14
+ assertEquals("", config.getDatakitUrl());
15
+ assertEquals("", config.getDatawayUrl());
16
+ assertEquals("", config.getClientToken());
17
+ }
18
+
19
+ @Test
20
+ public void ignoresIncompleteDatawayConfig() {
21
+ FTSDKConfig config = FTMobileImpl.buildSDKConfig(null, "https://ingest.example.com", null);
22
+
23
+ assertEquals("", config.getDatakitUrl());
24
+ assertEquals("", config.getDatawayUrl());
25
+ assertEquals("", config.getClientToken());
26
+ }
27
+
28
+ @Test
29
+ public void keepsExistingDatakitConfigBehavior() {
30
+ FTSDKConfig config = FTMobileImpl.buildSDKConfig(
31
+ "http://10.0.0.1:9529",
32
+ "https://ingest.example.com",
33
+ "client-token"
34
+ );
35
+
36
+ assertEquals("http://10.0.0.1:9529", config.getDatakitUrl());
37
+ assertEquals("", config.getDatawayUrl());
38
+ assertEquals("", config.getClientToken());
39
+ }
40
+
41
+ @Test
42
+ public void keepsExistingDatawayConfigBehavior() {
43
+ FTSDKConfig config = FTMobileImpl.buildSDKConfig(
44
+ null,
45
+ "https://ingest.example.com",
46
+ "client-token"
47
+ );
48
+
49
+ assertEquals("", config.getDatakitUrl());
50
+ assertEquals("https://ingest.example.com", config.getDatawayUrl());
51
+ assertEquals("client-token", config.getClientToken());
52
+ }
53
+ }
@@ -7,6 +7,7 @@
7
7
  //
8
8
 
9
9
  #import "FTMobileReactNative.h"
10
+ #import <TrueWatchSDK/FTSDKAgent.h>
10
11
  #import <React/RCTConvert.h>
11
12
  #import <TrueWatchSDK/FTMobileAgent.h>
12
13
  #import <TrueWatchSDK/FTSDKConfig+Private.h>
@@ -275,12 +276,33 @@ RCT_EXPORT_MODULE()
275
276
  if ([override.allKeys containsObject:@"logSampleRate"]) {
276
277
  model.logSampleRate = @([RCTConvert double:override[@"logSampleRate"]]);
277
278
  }
279
+ if ([override.allKeys containsObject:@"sessionSamplingRate"]) {
280
+ model.sessionSamplingRate = @([RCTConvert double:override[@"sessionSamplingRate"]]);
281
+ }
282
+ if ([override.allKeys containsObject:@"sessionOnErrorSamplingRate"]) {
283
+ model.sessionOnErrorSamplingRate = @([RCTConvert double:override[@"sessionOnErrorSamplingRate"]]);
284
+ }
285
+ if ([override.allKeys containsObject:@"traceSamplingRate"]) {
286
+ model.traceSamplingRate = @([RCTConvert double:override[@"traceSamplingRate"]]);
287
+ }
288
+ if ([override.allKeys containsObject:@"logSamplingRate"]) {
289
+ model.logSamplingRate = @([RCTConvert double:override[@"logSamplingRate"]]);
290
+ }
291
+ if ([override.allKeys containsObject:@"sessionReplaySamplingRate"]) {
292
+ model.sessionReplaySamplingRate = @([RCTConvert double:override[@"sessionReplaySamplingRate"]]);
293
+ }
294
+ if ([override.allKeys containsObject:@"sessionReplayOnErrorSamplingRate"]) {
295
+ model.sessionReplayOnErrorSamplingRate = @([RCTConvert double:override[@"sessionReplayOnErrorSamplingRate"]]);
296
+ }
278
297
  if ([override.allKeys containsObject:@"logLevelFilters"]) {
279
298
  model.logLevelFilters = [RCTConvert NSArray:override[@"logLevelFilters"]];
280
299
  }
281
300
  if ([override.allKeys containsObject:@"logEnableCustomLog"]) {
282
301
  model.logEnableCustomLog = @([RCTConvert double:override[@"logEnableCustomLog"]]);
283
302
  }
303
+ if ([override.allKeys containsObject:@"logEnableWebViewLog"] && override[@"logEnableWebViewLog"] != [NSNull null]) {
304
+ model.logEnableWebViewLog = @([RCTConvert BOOL:override[@"logEnableWebViewLog"]]);
305
+ }
284
306
 
285
307
  NSString *ruleId = [RCTConvert NSString:rule[@"id"]];
286
308
  [appliedRuleIds addObject:ruleId.length > 0 ? ruleId : [NSString stringWithFormat:@"rule_%lu", (unsigned long)idx]];
@@ -320,6 +342,12 @@ RCT_REMAP_METHOD(bindRUMUserData,
320
342
  [self bindRUMUserData:userId userName:userName userEmail:userName extra:extra resolve:resolve reject:reject];
321
343
  }
322
344
 
345
+ RCT_REMAP_METHOD(resetVisitorId,
346
+ resetVisitorIdResolver:(RCTPromiseResolveBlock)resolve
347
+ rejecter:(RCTPromiseRejectBlock)reject) {
348
+ [self resetVisitorId:resolve reject:reject];
349
+ }
350
+
323
351
  RCT_REMAP_METHOD(unbindRUMUserData,
324
352
  unbindRUMUserData_Resolver:(RCTPromiseResolveBlock)resolve
325
353
  rejecter:(RCTPromiseRejectBlock)reject
@@ -383,8 +411,7 @@ RCT_REMAP_METHOD(clearAllData,
383
411
  }else if(datakitUrl && datakitUrl.length>0){
384
412
  config = [[FTSDKConfig alloc]initWithDatakitUrl:datakitUrl];
385
413
  }else{
386
- resolve(nil);
387
- return;
414
+ config = [[FTSDKConfig alloc]init];
388
415
  }
389
416
  if ([context.allKeys containsObject:@"debug"]) {
390
417
  config.enableSDKDebugLog = [RCTConvert BOOL:context[@"debug"]];
@@ -428,6 +455,9 @@ RCT_REMAP_METHOD(clearAllData,
428
455
  if ([context.allKeys containsObject:@"dataFilters"]) {
429
456
  config.dataFilters = [RCTConvert NSDictionary:context[@"dataFilters"]];
430
457
  }
458
+ if ([context.allKeys containsObject:@"allowWebViewHost"]) {
459
+ config.allowWebViewHost = [RCTConvert NSArray:context[@"allowWebViewHost"]];
460
+ }
431
461
  if ([context.allKeys containsObject:@"globalContext"]) {
432
462
  config.globalContext = [RCTConvert NSDictionary:context[@"globalContext"]];
433
463
  }
@@ -528,6 +558,11 @@ RCT_REMAP_METHOD(clearAllData,
528
558
  [[FTMobileAgent sharedInstance] unbindUser];
529
559
  resolve(nil);
530
560
  }
561
+ - (void)resetVisitorId:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
562
+ [FTSDKAgent resetVisitorID];
563
+ resolve(nil);
564
+ }
565
+
531
566
  - (void)clearAllData:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
532
567
  [FTMobileAgent clearAllData];
533
568
  resolve(nil);
@@ -42,8 +42,13 @@ RCT_REMAP_METHOD(logWithStatusString,
42
42
 
43
43
  - (void)logConfig:(NSDictionary *)context resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
44
44
  FTLoggerConfig *logger = [[FTLoggerConfig alloc]init];
45
- if ([context.allKeys containsObject:@"sampleRate"]) {
46
- logger.sampleRate = [RCTConvert double:context[@"sampleRate"]] * 100;
45
+ if ([context.allKeys containsObject:@"logSamplingRate"]) {
46
+ logger.logSamplingRate = [RCTConvert double:context[@"logSamplingRate"]];
47
+ } else if ([context.allKeys containsObject:@"sampleRate"]) {
48
+ logger.logSamplingRate = [RCTConvert double:context[@"sampleRate"]] * 100;
49
+ }
50
+ if ([context.allKeys containsObject:@"enableWebViewLog"]) {
51
+ logger.enableWebViewLog = [RCTConvert BOOL:context[@"enableWebViewLog"]];
47
52
  }
48
53
  NSArray<NSNumber *>*filters = [RCTConvert NSNumberArray:context[@"logLevelFilters"]];
49
54
  if (filters) {