react-native-netmera 2.0.1 → 2.1.0-alpha02

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 (139) hide show
  1. package/RNNetmera.podspec +1 -1
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/netmera/reactnativesdk/RNNetmera.kt +4 -0
  4. package/android/src/main/java/com/netmera/reactnativesdk/RNNetmeraConfigHandler.kt +33 -0
  5. package/android/src/main/java/com/netmera/reactnativesdk/RNNetmeraModule.kt +23 -1
  6. package/ios/RNNetmera.mm +7 -0
  7. package/ios/RNNetmera.swift +21 -0
  8. package/ios/RNNetmeraConfigHandler.swift +28 -0
  9. package/ios/RNNetmeraRCTEventEmitter.swift +7 -0
  10. package/lib/commonjs/Netmera.js +277 -0
  11. package/lib/commonjs/Netmera.js.map +1 -0
  12. package/lib/commonjs/autotrack/NetmeraAutoTrackProvider.js +406 -0
  13. package/lib/commonjs/autotrack/NetmeraAutoTrackProvider.js.map +1 -0
  14. package/lib/commonjs/events/NetmeraEvent.js +13 -0
  15. package/lib/commonjs/events/NetmeraEvent.js.map +1 -0
  16. package/lib/commonjs/events/NetmeraEventBannerOpen.js +19 -0
  17. package/lib/commonjs/events/NetmeraEventBannerOpen.js.map +1 -0
  18. package/lib/commonjs/events/NetmeraEventBatteryLevel.js +16 -0
  19. package/lib/commonjs/events/NetmeraEventBatteryLevel.js.map +1 -0
  20. package/lib/commonjs/events/NetmeraEventCategoryView.js +19 -0
  21. package/lib/commonjs/events/NetmeraEventCategoryView.js.map +1 -0
  22. package/lib/commonjs/events/NetmeraEventInAppPurchase.js +34 -0
  23. package/lib/commonjs/events/NetmeraEventInAppPurchase.js.map +1 -0
  24. package/lib/commonjs/events/NetmeraEventLogin.js +16 -0
  25. package/lib/commonjs/events/NetmeraEventLogin.js.map +1 -0
  26. package/lib/commonjs/events/NetmeraEventRegister.js +16 -0
  27. package/lib/commonjs/events/NetmeraEventRegister.js.map +1 -0
  28. package/lib/commonjs/events/NetmeraEventScreenView.js +28 -0
  29. package/lib/commonjs/events/NetmeraEventScreenView.js.map +1 -0
  30. package/lib/commonjs/events/NetmeraEventSearch.js +19 -0
  31. package/lib/commonjs/events/NetmeraEventSearch.js.map +1 -0
  32. package/lib/commonjs/events/NetmeraEventShare.js +19 -0
  33. package/lib/commonjs/events/NetmeraEventShare.js.map +1 -0
  34. package/lib/commonjs/events/commerce/NetmeraEventCartAddProduct.js +19 -0
  35. package/lib/commonjs/events/commerce/NetmeraEventCartAddProduct.js.map +1 -0
  36. package/lib/commonjs/events/commerce/NetmeraEventCartRemoveProduct.js +16 -0
  37. package/lib/commonjs/events/commerce/NetmeraEventCartRemoveProduct.js.map +1 -0
  38. package/lib/commonjs/events/commerce/NetmeraEventCartView.js +19 -0
  39. package/lib/commonjs/events/commerce/NetmeraEventCartView.js.map +1 -0
  40. package/lib/commonjs/events/commerce/NetmeraEventOrderCancel.js +25 -0
  41. package/lib/commonjs/events/commerce/NetmeraEventOrderCancel.js.map +1 -0
  42. package/lib/commonjs/events/commerce/NetmeraEventProductComment.js +13 -0
  43. package/lib/commonjs/events/commerce/NetmeraEventProductComment.js.map +1 -0
  44. package/lib/commonjs/events/commerce/NetmeraEventProductRate.js +16 -0
  45. package/lib/commonjs/events/commerce/NetmeraEventProductRate.js.map +1 -0
  46. package/lib/commonjs/events/commerce/NetmeraEventProductView.js +13 -0
  47. package/lib/commonjs/events/commerce/NetmeraEventProductView.js.map +1 -0
  48. package/lib/commonjs/events/commerce/NetmeraEventPurchase.js +40 -0
  49. package/lib/commonjs/events/commerce/NetmeraEventPurchase.js.map +1 -0
  50. package/lib/commonjs/events/commerce/NetmeraEventWishList.js +13 -0
  51. package/lib/commonjs/events/commerce/NetmeraEventWishList.js.map +1 -0
  52. package/lib/commonjs/events/commerce/NetmeraLineItem.js +14 -0
  53. package/lib/commonjs/events/commerce/NetmeraLineItem.js.map +1 -0
  54. package/lib/commonjs/events/commerce/NetmeraProduct.js +51 -0
  55. package/lib/commonjs/events/commerce/NetmeraProduct.js.map +1 -0
  56. package/lib/commonjs/events/media/NetmeraEventContent.js +48 -0
  57. package/lib/commonjs/events/media/NetmeraEventContent.js.map +1 -0
  58. package/lib/commonjs/events/media/NetmeraEventContentComment.js +12 -0
  59. package/lib/commonjs/events/media/NetmeraEventContentComment.js.map +1 -0
  60. package/lib/commonjs/events/media/NetmeraEventContentRate.js +15 -0
  61. package/lib/commonjs/events/media/NetmeraEventContentRate.js.map +1 -0
  62. package/lib/commonjs/events/media/NetmeraEventContentView.js +12 -0
  63. package/lib/commonjs/events/media/NetmeraEventContentView.js.map +1 -0
  64. package/lib/commonjs/index.js +352 -0
  65. package/lib/commonjs/index.js.map +1 -0
  66. package/lib/commonjs/models/NMCategoryPreference.js +13 -0
  67. package/lib/commonjs/models/NMCategoryPreference.js.map +1 -0
  68. package/lib/commonjs/models/NMInboxStatus.js +15 -0
  69. package/lib/commonjs/models/NMInboxStatus.js.map +1 -0
  70. package/lib/commonjs/models/NMInboxStatusCountFilter.js +17 -0
  71. package/lib/commonjs/models/NMInboxStatusCountFilter.js.map +1 -0
  72. package/lib/commonjs/models/NetmeraCarouselObject.js +24 -0
  73. package/lib/commonjs/models/NetmeraCarouselObject.js.map +1 -0
  74. package/lib/commonjs/models/NetmeraCategory.js +13 -0
  75. package/lib/commonjs/models/NetmeraCategory.js.map +1 -0
  76. package/lib/commonjs/models/NetmeraCategoryFilter.js +13 -0
  77. package/lib/commonjs/models/NetmeraCategoryFilter.js.map +1 -0
  78. package/lib/commonjs/models/NetmeraCouponObject.js +13 -0
  79. package/lib/commonjs/models/NetmeraCouponObject.js.map +1 -0
  80. package/lib/commonjs/models/NetmeraInboxFilter.js +13 -0
  81. package/lib/commonjs/models/NetmeraInboxFilter.js.map +1 -0
  82. package/lib/commonjs/models/NetmeraInteractiveAction.js +24 -0
  83. package/lib/commonjs/models/NetmeraInteractiveAction.js.map +1 -0
  84. package/lib/commonjs/models/NetmeraProfileAttribute.js +80 -0
  85. package/lib/commonjs/models/NetmeraProfileAttribute.js.map +1 -0
  86. package/lib/commonjs/models/NetmeraPushAction.js +31 -0
  87. package/lib/commonjs/models/NetmeraPushAction.js.map +1 -0
  88. package/lib/commonjs/models/NetmeraPushObject.js +63 -0
  89. package/lib/commonjs/models/NetmeraPushObject.js.map +1 -0
  90. package/lib/commonjs/models/NetmeraUser.js +93 -0
  91. package/lib/commonjs/models/NetmeraUser.js.map +1 -0
  92. package/lib/commonjs/models/NetmeraUserProfile.js +63 -0
  93. package/lib/commonjs/models/NetmeraUserProfile.js.map +1 -0
  94. package/lib/commonjs/models/NotificationPermissionStatus.js +14 -0
  95. package/lib/commonjs/models/NotificationPermissionStatus.js.map +1 -0
  96. package/lib/commonjs/navigation/NetmeraNavigationState.js +31 -0
  97. package/lib/commonjs/navigation/NetmeraNavigationState.js.map +1 -0
  98. package/lib/commonjs/navigation/NetmeraNavigationTracker.js +70 -0
  99. package/lib/commonjs/navigation/NetmeraNavigationTracker.js.map +1 -0
  100. package/lib/commonjs/navigation/useNetmeraNavigation.js +83 -0
  101. package/lib/commonjs/navigation/useNetmeraNavigation.js.map +1 -0
  102. package/lib/commonjs/package.json +1 -0
  103. package/lib/commonjs/utils/DeviceUtils.js +16 -0
  104. package/lib/commonjs/utils/DeviceUtils.js.map +1 -0
  105. package/lib/commonjs/utils/Optional.js +37 -0
  106. package/lib/commonjs/utils/Optional.js.map +1 -0
  107. package/lib/commonjs/utils/RNNetmera.js +19 -0
  108. package/lib/commonjs/utils/RNNetmera.js.map +1 -0
  109. package/lib/module/Netmera.js +16 -0
  110. package/lib/module/Netmera.js.map +1 -1
  111. package/lib/module/NetmeraAnalyticProvider.js +68 -0
  112. package/lib/module/NetmeraAnalyticProvider.js.map +1 -0
  113. package/lib/module/autotracking/NetmeraNavigationState.js +14 -0
  114. package/lib/module/autotracking/NetmeraNavigationState.js.map +1 -0
  115. package/lib/module/autotracking/useNavigationTracking.js +104 -0
  116. package/lib/module/autotracking/useNavigationTracking.js.map +1 -0
  117. package/lib/module/autotracking/useTapTracking.js +249 -0
  118. package/lib/module/autotracking/useTapTracking.js.map +1 -0
  119. package/lib/module/index.js +4 -1
  120. package/lib/module/index.js.map +1 -1
  121. package/lib/typescript/src/Netmera.d.ts +10 -0
  122. package/lib/typescript/src/Netmera.d.ts.map +1 -1
  123. package/lib/typescript/src/NetmeraAnalyticProvider.d.ts +26 -0
  124. package/lib/typescript/src/NetmeraAnalyticProvider.d.ts.map +1 -0
  125. package/lib/typescript/src/autotracking/NetmeraNavigationState.d.ts +3 -0
  126. package/lib/typescript/src/autotracking/NetmeraNavigationState.d.ts.map +1 -0
  127. package/lib/typescript/src/autotracking/useNavigationTracking.d.ts +13 -0
  128. package/lib/typescript/src/autotracking/useNavigationTracking.d.ts.map +1 -0
  129. package/lib/typescript/src/autotracking/useTapTracking.d.ts +6 -0
  130. package/lib/typescript/src/autotracking/useTapTracking.d.ts.map +1 -0
  131. package/lib/typescript/src/index.d.ts +3 -1
  132. package/lib/typescript/src/index.d.ts.map +1 -1
  133. package/package.json +1 -1
  134. package/src/Netmera.ts +16 -0
  135. package/src/NetmeraAnalyticProvider.tsx +124 -0
  136. package/src/autotracking/NetmeraNavigationState.ts +13 -0
  137. package/src/autotracking/useNavigationTracking.ts +130 -0
  138. package/src/autotracking/useTapTracking.ts +300 -0
  139. package/src/index.ts +6 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NetmeraAnalyticProvider.d.ts","sourceRoot":"","sources":["../../../src/NetmeraAnalyticProvider.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAU3D,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;CACtC;AAQD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,aAAa,GACd,EAAE,4BAA4B,2CAwE9B"}
@@ -0,0 +1,3 @@
1
+ export declare function getCurrentScreen(): string | null;
2
+ export declare function setCurrentScreen(name: string): void;
3
+ //# sourceMappingURL=NetmeraNavigationState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NetmeraNavigationState.d.ts","sourceRoot":"","sources":["../../../../src/autotracking/NetmeraNavigationState.ts"],"names":[],"mappings":"AAMA,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEnD"}
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ /**
3
+ * Subscribes to React Navigation screen changes and keeps NetmeraNavigationState
4
+ * up to date. Resolves the navigation container ref via two paths:
5
+ *
6
+ * - New Arch (Fabric): auto-detected through the fiber tree via viewRef.
7
+ * - Old Arch (Paper): caller passes navigationRef prop explicitly.
8
+ *
9
+ * Silently no-ops if neither path yields a valid ref.
10
+ * Respects the isScreenFlowEnabled flag — screen changes are ignored when disabled.
11
+ */
12
+ export declare function useNavigationTracking(viewRef: React.RefObject<any>, navigationRef: React.RefObject<any> | undefined, enabled: boolean, screenLogEnabled: boolean): void;
13
+ //# sourceMappingURL=useNavigationTracking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNavigationTracking.d.ts","sourceRoot":"","sources":["../../../../src/autotracking/useNavigationTracking.ts"],"names":[],"mappings":"AAIA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAkCjD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAC7B,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,EAC/C,OAAO,EAAE,OAAO,EAChB,gBAAgB,EAAE,OAAO,GACxB,IAAI,CA4EN"}
@@ -0,0 +1,6 @@
1
+ import type { GestureResponderEvent } from 'react-native';
2
+ export declare function useTapTracking(enabled: boolean, shouldCollectValues: boolean): {
3
+ onTouchStart: (e: GestureResponderEvent) => void;
4
+ onTouchEnd: (e: GestureResponderEvent) => void;
5
+ };
6
+ //# sourceMappingURL=useTapTracking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTapTracking.d.ts","sourceRoot":"","sources":["../../../../src/autotracking/useTapTracking.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AA6J1D,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO;sBA6BtC,qBAAqB;oBAoBvB,qBAAqB;EAwFzD"}
@@ -38,5 +38,7 @@ import { NetmeraProduct } from './events/commerce/NetmeraProduct';
38
38
  import { NetmeraEventContentComment } from './events/media/NetmeraEventContentComment';
39
39
  import { NetmeraEventContentRate } from './events/media/NetmeraEventContentRate';
40
40
  import { NetmeraEventContentView } from './events/media/NetmeraEventContentView';
41
- export { Netmera, NetmeraCouponObject, NetmeraUser, NetmeraPushObject, NetmeraPushType, NetmeraPushAction, NetmeraPushActionType, NetmeraCarouselObject, NetmeraInteractiveAction, NetmeraInboxFilter, NMInboxStatus, NMInboxStatusCountFilter, NetmeraCategory, NetmeraCategoryFilter, NMCategoryPreference, NotificationPermissionStatus, NetmeraLineItem, NetmeraProduct, NetmeraUserProfile, NetmeraProfileAttribute, NetmeraProfileAttributeCollection, Gender, MaritalStatus, NetmeraEvent, NetmeraEventBannerOpen, NetmeraEventBatteryLevel, NetmeraEventCategoryView, NetmeraEventInAppPurchase, NetmeraEventLogin, NetmeraEventRegister, NetmeraEventScreenView, NetmeraEventSearch, NetmeraEventShare, NetmeraEventCartAddProduct, NetmeraEventCartRemoveProduct, NetmeraEventCartView, NetmeraEventOrderCancel, NetmeraEventProductComment, NetmeraEventProductRate, NetmeraEventProductView, NetmeraEventPurchase, NetmeraEventWishList, NetmeraEventContentComment, NetmeraEventContentRate, NetmeraEventContentView, };
41
+ import { NetmeraAnalyticProvider } from './NetmeraAnalyticProvider';
42
+ import type { NetmeraAnalyticProviderProps } from './NetmeraAnalyticProvider';
43
+ export { Netmera, NetmeraCouponObject, NetmeraUser, NetmeraPushObject, NetmeraPushType, NetmeraPushAction, NetmeraPushActionType, NetmeraCarouselObject, NetmeraInteractiveAction, NetmeraInboxFilter, NMInboxStatus, NMInboxStatusCountFilter, NetmeraCategory, NetmeraCategoryFilter, NMCategoryPreference, NotificationPermissionStatus, NetmeraLineItem, NetmeraProduct, NetmeraUserProfile, NetmeraProfileAttribute, NetmeraProfileAttributeCollection, Gender, MaritalStatus, NetmeraEvent, NetmeraEventBannerOpen, NetmeraEventBatteryLevel, NetmeraEventCategoryView, NetmeraEventInAppPurchase, NetmeraEventLogin, NetmeraEventRegister, NetmeraEventScreenView, NetmeraEventSearch, NetmeraEventShare, NetmeraEventCartAddProduct, NetmeraEventCartRemoveProduct, NetmeraEventCartView, NetmeraEventOrderCancel, NetmeraEventProductComment, NetmeraEventProductRate, NetmeraEventProductView, NetmeraEventPurchase, NetmeraEventWishList, NetmeraEventContentComment, NetmeraEventContentRate, NetmeraEventContentView, NetmeraAnalyticProvider, type NetmeraAnalyticProviderProps, };
42
44
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-netmera",
3
- "version": "2.0.1",
3
+ "version": "2.1.0-alpha02",
4
4
  "description": "Netmera React Native SDK",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",
package/src/Netmera.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { AppRegistry, DeviceEventEmitter } from 'react-native';
6
+ import { getCurrentScreen } from './autotracking/NetmeraNavigationState';
6
7
  import NetmeraEvent from './events/NetmeraEvent';
7
8
  import NetmeraUser from './models/NetmeraUser';
8
9
  import NetmeraInboxFilter from './models/NetmeraInboxFilter';
@@ -391,4 +392,19 @@ export default class Netmera {
391
392
  static async getWhatsAppPermission(): Promise<boolean> {
392
393
  return RNNetmera.getWhatsAppPermission();
393
394
  }
395
+
396
+ /**
397
+ * Manually log a user action for components that don't fire standard
398
+ * onPress/onValueChange events (e.g. third-party dropdowns, custom pickers).
399
+ *
400
+ * Netmera.trackAction('category-selector|Electronics')
401
+ *
402
+ * @param path Identifier or structured path describing the action.
403
+ * Use `|` to separate segments (e.g. `component|value`).
404
+ */
405
+ static trackAction(path: string): void {
406
+ const screen = getCurrentScreen();
407
+ const entry = screen ? `${screen}|${path}` : path;
408
+ RNNetmera.trackAction(entry);
409
+ }
394
410
  }
@@ -0,0 +1,124 @@
1
+ /*
2
+ * Copyright (c) 2026 Netmera Research.
3
+ */
4
+
5
+ import React, { useEffect, useRef, useState } from 'react';
6
+ import {
7
+ NativeEventEmitter,
8
+ NativeModules,
9
+ StyleSheet,
10
+ View,
11
+ } from 'react-native';
12
+ import { useNavigationTracking } from './autotracking/useNavigationTracking';
13
+ import { useTapTracking } from './autotracking/useTapTracking';
14
+
15
+ export interface NetmeraAnalyticProviderProps {
16
+ children: React.ReactNode;
17
+ /**
18
+ * Required on Old Architecture (newArchEnabled=false). Pass your NavigationContainer
19
+ * ref so the provider can subscribe to screen changes.
20
+ *
21
+ * On New Architecture the provider detects the navigation context automatically
22
+ * through the fiber tree — this prop is not needed.
23
+ *
24
+ * const navRef = useRef(null);
25
+ * <NetmeraAnalyticProvider navigationRef={navRef}>
26
+ * <NavigationContainer ref={navRef}>…</NavigationContainer>
27
+ * </NetmeraAnalyticProvider>
28
+ */
29
+ navigationRef?: React.RefObject<any>;
30
+ }
31
+
32
+ type AutoTrackConfig = {
33
+ isScreenFlowEnabled: boolean;
34
+ isInputActionEnabled: boolean;
35
+ shouldCollectValues: boolean;
36
+ };
37
+
38
+ /**
39
+ * Wraps your app to enable automatic screen tracking and tap tracking.
40
+ *
41
+ * <NetmeraAnalyticProvider>
42
+ * <NavigationContainer>…</NavigationContainer>
43
+ * </NetmeraAnalyticProvider>
44
+ */
45
+ export function NetmeraAnalyticProvider({
46
+ children,
47
+ navigationRef,
48
+ }: NetmeraAnalyticProviderProps) {
49
+ const viewRef = useRef<any>(null);
50
+ const [config, setConfig] = useState<AutoTrackConfig | null>(null);
51
+ const appliedConfigRef = useRef<AutoTrackConfig | null>(null);
52
+
53
+ const applyConfig = (cfg: AutoTrackConfig) => {
54
+ const prev = appliedConfigRef.current;
55
+ if (
56
+ prev?.isScreenFlowEnabled === cfg.isScreenFlowEnabled &&
57
+ prev?.isInputActionEnabled === cfg.isInputActionEnabled &&
58
+ prev?.shouldCollectValues === cfg.shouldCollectValues
59
+ )
60
+ return;
61
+ appliedConfigRef.current = cfg;
62
+ setConfig(cfg);
63
+ console.log(
64
+ '[NMAutotrack] screen tracking ',
65
+ cfg.isScreenFlowEnabled ? 'enabled' : 'disabled'
66
+ );
67
+ console.log(
68
+ '[NMAutotrack] input action tracking ',
69
+ cfg.isInputActionEnabled ? 'enabled' : 'disabled'
70
+ );
71
+ };
72
+
73
+ useEffect(() => {
74
+ NativeModules.RNNetmera?.getAutoTrackConfig?.()
75
+ ?.then((cfg: AutoTrackConfig | null) => {
76
+ if (cfg) applyConfig(cfg);
77
+ })
78
+ ?.catch((_err: unknown) => {});
79
+ }, []);
80
+
81
+ useEffect(() => {
82
+ const emitter = new NativeEventEmitter(
83
+ NativeModules.RNNetmeraRCTEventEmitter
84
+ );
85
+ const subscription = emitter.addListener(
86
+ 'onAutoTrackConfigUpdate',
87
+ (cfg: AutoTrackConfig) => {
88
+ if (cfg) applyConfig(cfg);
89
+ }
90
+ );
91
+ return () => subscription.remove();
92
+ }, []);
93
+
94
+ const screenFlowEnabled = config?.isScreenFlowEnabled ?? false;
95
+ const inputActionEnabled = config?.isInputActionEnabled ?? false;
96
+ const shouldCollectValues = config?.shouldCollectValues ?? false;
97
+
98
+ const navigationEnabled = screenFlowEnabled || inputActionEnabled;
99
+ useNavigationTracking(
100
+ viewRef,
101
+ navigationRef,
102
+ navigationEnabled,
103
+ screenFlowEnabled
104
+ );
105
+ const { onTouchStart, onTouchEnd } = useTapTracking(
106
+ inputActionEnabled,
107
+ shouldCollectValues
108
+ );
109
+
110
+ return (
111
+ <View
112
+ ref={navigationEnabled ? viewRef : undefined}
113
+ style={styles.container}
114
+ onTouchStart={onTouchStart}
115
+ onTouchEnd={onTouchEnd}
116
+ >
117
+ {children}
118
+ </View>
119
+ );
120
+ }
121
+
122
+ const styles = StyleSheet.create({
123
+ container: { flex: 1 },
124
+ });
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Copyright (c) 2026 Netmera Research.
3
+ */
4
+
5
+ let currentScreen: string | null = null;
6
+
7
+ export function getCurrentScreen(): string | null {
8
+ return currentScreen;
9
+ }
10
+
11
+ export function setCurrentScreen(name: string): void {
12
+ currentScreen = name;
13
+ }
@@ -0,0 +1,130 @@
1
+ /*
2
+ * Copyright (c) 2026 Netmera Research.
3
+ */
4
+
5
+ import React, { useEffect, useRef } from 'react';
6
+ import { setCurrentScreen } from './NetmeraNavigationState';
7
+ import { RNNetmera } from '../utils/RNNetmera';
8
+
9
+ // ContextProvider fiber tag — stable React internal constant since React 16.
10
+ const CONTEXT_PROVIDER_TAG = 10;
11
+ const NAV_SEARCH_DEPTH = 25;
12
+
13
+ // Duck-type check: React Navigation v5/v6/v7 all expose this surface on the container ref.
14
+ function isNavContainerRef(value: any): boolean {
15
+ return (
16
+ value != null &&
17
+ typeof value.addListener === 'function' &&
18
+ typeof value.getCurrentRoute === 'function' &&
19
+ typeof value.isReady === 'function'
20
+ );
21
+ }
22
+
23
+ // DFS through child fibers to find NavigationContainerRefContext.Provider.
24
+ // Siblings walk at the same depth — they sit at the same level in the tree.
25
+ function findNavContainerRef(fiber: any, depth = 0): any {
26
+ if (!fiber || depth > NAV_SEARCH_DEPTH) return null;
27
+ if (
28
+ fiber.tag === CONTEXT_PROVIDER_TAG &&
29
+ isNavContainerRef(fiber.memoizedProps?.value)
30
+ ) {
31
+ return fiber.memoizedProps.value;
32
+ }
33
+ return (
34
+ findNavContainerRef(fiber.child, depth + 1) ??
35
+ findNavContainerRef(fiber.sibling, depth)
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Subscribes to React Navigation screen changes and keeps NetmeraNavigationState
41
+ * up to date. Resolves the navigation container ref via two paths:
42
+ *
43
+ * - New Arch (Fabric): auto-detected through the fiber tree via viewRef.
44
+ * - Old Arch (Paper): caller passes navigationRef prop explicitly.
45
+ *
46
+ * Silently no-ops if neither path yields a valid ref.
47
+ * Respects the isScreenFlowEnabled flag — screen changes are ignored when disabled.
48
+ */
49
+ export function useNavigationTracking(
50
+ viewRef: React.RefObject<any>,
51
+ navigationRef: React.RefObject<any> | undefined,
52
+ enabled: boolean,
53
+ screenLogEnabled: boolean
54
+ ): void {
55
+ // React 18: capture own fiber during render via ReactCurrentOwner.
56
+ // React 19 removed ReactCurrentOwner — this silently yields null, which is fine
57
+ // because the Fabric path (viewRef.__internalInstanceHandle) covers React 19.
58
+ const screenLogEnabledRef = useRef(screenLogEnabled);
59
+
60
+ useEffect(() => {
61
+ screenLogEnabledRef.current = screenLogEnabled;
62
+ }, [screenLogEnabled]);
63
+
64
+ const selfFiberRef = useRef<any>(null);
65
+ if (selfFiberRef.current === null) {
66
+ try {
67
+ const R = React as any;
68
+ selfFiberRef.current =
69
+ R.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?.ReactCurrentOwner
70
+ ?.current ?? null;
71
+ } catch (_) {}
72
+ }
73
+
74
+ useEffect(() => {
75
+ if (!enabled) return;
76
+ let navRef: any = null;
77
+
78
+ // Path 1 — New Arch: ReactNativeElement exposes its fiber via __internalInstanceHandle.
79
+ // Guard with 'child' in candidate to confirm it's a fiber, not another host object.
80
+ const candidate = viewRef.current?.__internalInstanceHandle;
81
+ const viewFiber: any =
82
+ candidate != null && 'child' in candidate
83
+ ? candidate
84
+ : (selfFiberRef.current?.child ?? null);
85
+
86
+ if (viewFiber) {
87
+ navRef = findNavContainerRef(viewFiber);
88
+ }
89
+
90
+ // Path 2 — Old Arch: use the ref passed explicitly via prop.
91
+ if (!navRef && isNavContainerRef(navigationRef?.current)) {
92
+ navRef = navigationRef!.current;
93
+ }
94
+
95
+ if (!navRef) return;
96
+
97
+ const handleChange = () => {
98
+ try {
99
+ const route = navRef.getCurrentRoute();
100
+ if (route?.name) {
101
+ setCurrentScreen(route.name);
102
+ if (screenLogEnabledRef.current) {
103
+ RNNetmera.trackView(route.name);
104
+ }
105
+ }
106
+ } catch (_) {}
107
+ };
108
+
109
+ const unsubState = navRef.addListener('state', handleChange);
110
+
111
+ // NavigationContainer's effects run before ours (child-first), so 'ready' has
112
+ // already fired. isReady() captures the initial screen synchronously; the 'ready'
113
+ // listener is a safety net for async startup (deferred auth, deep links, etc.).
114
+ let unsubReady: (() => void) | undefined;
115
+ if (navRef.isReady()) {
116
+ handleChange();
117
+ } else {
118
+ unsubReady = navRef.addListener('ready', () => {
119
+ handleChange();
120
+ unsubReady?.();
121
+ unsubReady = undefined;
122
+ });
123
+ }
124
+
125
+ return () => {
126
+ unsubState?.();
127
+ unsubReady?.();
128
+ };
129
+ }, [enabled]); // eslint-disable-line react-hooks/exhaustive-deps
130
+ }
@@ -0,0 +1,300 @@
1
+ /*
2
+ * Copyright (c) 2026 Netmera Research.
3
+ */
4
+
5
+ import { useCallback, useEffect, useRef } from 'react';
6
+ import type { GestureResponderEvent } from 'react-native';
7
+ import { getCurrentScreen } from './NetmeraNavigationState';
8
+ import { RNNetmera } from '../utils/RNNetmera';
9
+
10
+ const MAX_FIBER_DEPTH = 20;
11
+ const MAX_TEXT_DEPTH = 5;
12
+ const TAP_THRESHOLD = 10; // px — above this distance the gesture is a scroll, not a tap
13
+
14
+ // ── Fiber predicates ──────────────────────────────────────────────────────────
15
+
16
+ // Skip onStartShouldSetResponder — it matches ScrollViews, which are not interactive targets.
17
+ function isInteractive(props: any): boolean {
18
+ return !!(props?.onPress || props?.onClick || props?.onValueChange);
19
+ }
20
+
21
+ // Switch has onValueChange + boolean value, but no onPress/onClick.
22
+ function isSwitch(props: any): boolean {
23
+ return (
24
+ !!props?.onValueChange &&
25
+ typeof props?.value === 'boolean' &&
26
+ !(props?.onPress || props?.onClick)
27
+ );
28
+ }
29
+
30
+ // SelectDropdown and similar pickers expose onSelect on an ancestor fiber.
31
+ // Tapping them only opens the picker UI — the real select event fires separately.
32
+ function isInsideSelectionControl(fiber: any): boolean {
33
+ let node: any = fiber?.return;
34
+ for (let d = 0; d < 5 && node; d++, node = node.return) {
35
+ if (node?.memoizedProps?.onSelect != null) return true;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ // ── Identifier resolution ─────────────────────────────────────────────────────
41
+
42
+ function textFromChildren(children: any, depth = 0): string | undefined {
43
+ if (depth > MAX_TEXT_DEPTH) return undefined;
44
+ if (typeof children === 'string') {
45
+ const t = children.trim();
46
+ return t.length > 0 ? t : undefined;
47
+ }
48
+ if (typeof children === 'number') return String(children);
49
+ if (Array.isArray(children)) {
50
+ for (const child of children) {
51
+ const t = textFromChildren(child, depth + 1);
52
+ if (t) return t;
53
+ }
54
+ return undefined;
55
+ }
56
+ if (children != null && typeof children === 'object' && 'props' in children) {
57
+ return textFromChildren((children as any).props?.children, depth + 1);
58
+ }
59
+ return undefined;
60
+ }
61
+
62
+ // Priority: accessibilityLabel → testID → placeholder (TextInput) → first child text.
63
+ function resolveIdentifier(props: any): string | undefined {
64
+ if (!props) return undefined;
65
+ if (
66
+ typeof props.accessibilityLabel === 'string' &&
67
+ props.accessibilityLabel.trim()
68
+ ) {
69
+ return props.accessibilityLabel.trim();
70
+ }
71
+ if (typeof props.testID === 'string' && props.testID.trim()) {
72
+ return props.testID.trim();
73
+ }
74
+ if (typeof props.placeholder === 'string' && props.placeholder.trim()) {
75
+ return props.placeholder.trim();
76
+ }
77
+ return textFromChildren(props.children);
78
+ }
79
+
80
+ // ── List index resolution ─────────────────────────────────────────────────────
81
+
82
+ type ListIndex =
83
+ | { type: 'flatlist'; index: number }
84
+ | { type: 'sectionlist'; sectionIndex: number; itemIndex: number };
85
+
86
+ function resolveListIndex(startFiber: any): ListIndex | undefined {
87
+ let fiber: any = startFiber;
88
+ for (let d = 0; d < MAX_FIBER_DEPTH && fiber; d++, fiber = fiber.return) {
89
+ const p = fiber.memoizedProps;
90
+ if (
91
+ typeof p?.index !== 'number' ||
92
+ typeof p?.cellKey !== 'string' ||
93
+ typeof p?.onUnmount !== 'function'
94
+ )
95
+ continue;
96
+
97
+ const cellKey: string = p.cellKey;
98
+
99
+ // Prefer stateNode.props.index over memoizedProps.index: both current and alternate
100
+ // fibers share the same class instance, and React writes nextProps to instance.props
101
+ // during reconciliation — so stateNode.props is always current even when _targetInst
102
+ // resolves to the stale alternate fiber (common on iOS after a commit).
103
+ const flatIndex: number =
104
+ typeof fiber.stateNode?.props?.index === 'number'
105
+ ? fiber.stateNode.props.index
106
+ : p.index;
107
+
108
+ // `sections` Array prop is only present on SectionList; VirtualizedList uses `data`.
109
+ let sections: any[] | undefined;
110
+ let up: any = fiber.return;
111
+ for (let i = 0; i < 15 && up; i++, up = up.return) {
112
+ const upProps = up.stateNode?.props ?? up.memoizedProps;
113
+ if (Array.isArray(upProps?.sections)) {
114
+ sections = upProps.sections;
115
+ break;
116
+ }
117
+ }
118
+
119
+ if (sections) {
120
+ if (cellKey.endsWith(':header') || cellKey.endsWith(':footer'))
121
+ return undefined;
122
+
123
+ // Mirrors VirtualizedSectionList._getItem offset arithmetic:
124
+ // flatIndex 0 → section 0 header
125
+ // flatIndex 1..n → section 0 items (itemIndex = flatIndex - 1)
126
+ // flatIndex n+1 → section 0 footer
127
+ // flatIndex n+2 → section 1 header …
128
+ let idx = flatIndex - 1;
129
+ for (let si = 0; si < sections.length; si++) {
130
+ const count = sections[si].data?.length ?? 0;
131
+ if (idx === -1 || idx === count) return undefined;
132
+ if (idx < count)
133
+ return { type: 'sectionlist', sectionIndex: si, itemIndex: idx };
134
+ idx -= count + 2;
135
+ }
136
+ return undefined;
137
+ }
138
+
139
+ return { type: 'flatlist', index: flatIndex };
140
+ }
141
+ return undefined;
142
+ }
143
+
144
+ // ── Log path ──────────────────────────────────────────────────────────────────
145
+
146
+ function buildPath(
147
+ screen: string | null,
148
+ listType: string | null,
149
+ identifier: string,
150
+ indexSuffix: string,
151
+ value?: boolean
152
+ ): string {
153
+ const base = listType
154
+ ? `${screen}|${listType}|${identifier}`
155
+ : `${screen}|${identifier}`;
156
+ return value !== undefined
157
+ ? `${base}|${value}${indexSuffix}`
158
+ : `${base}${indexSuffix}`;
159
+ }
160
+
161
+ // ── Hook ──────────────────────────────────────────────────────────────────────
162
+
163
+ export function useTapTracking(enabled: boolean, shouldCollectValues: boolean) {
164
+ const enabledRef = useRef(enabled);
165
+ const shouldCollectValuesRef = useRef(shouldCollectValues);
166
+ const touchStartRef = useRef<{
167
+ x: number;
168
+ y: number;
169
+ switchValue?: boolean;
170
+ } | null>(null);
171
+ const lastScreenRef = useRef<string | null | undefined>(undefined);
172
+
173
+ // Tracks the last logged value per Switch identifier so we can compute the new
174
+ // state as !lastLogged without re-reading the fiber. memoizedProps.value is
175
+ // unreliable across taps because React's current/alternate swap can expose a
176
+ // stale alternate fiber via _targetInst after any commit.
177
+ const switchLastLoggedRef = useRef<Map<string, boolean>>(new Map());
178
+
179
+ useEffect(() => {
180
+ enabledRef.current = enabled;
181
+ if (!enabled) {
182
+ touchStartRef.current = null;
183
+ lastScreenRef.current = undefined;
184
+ switchLastLoggedRef.current.clear();
185
+ }
186
+ }, [enabled]);
187
+
188
+ useEffect(() => {
189
+ shouldCollectValuesRef.current = shouldCollectValues;
190
+ }, [shouldCollectValues]);
191
+
192
+ const onTouchStart = useCallback((e: GestureResponderEvent) => {
193
+ if (!enabledRef.current) return;
194
+ let switchValue: boolean | undefined;
195
+ try {
196
+ let fiber: any = (e as any)._targetInst;
197
+ for (let d = 0; d < MAX_FIBER_DEPTH && fiber; d++, fiber = fiber.return) {
198
+ if (isSwitch(fiber.memoizedProps)) {
199
+ // Capture the committed value before any event fires — reliable first-tap baseline.
200
+ switchValue = fiber.memoizedProps.value;
201
+ break;
202
+ }
203
+ }
204
+ } catch (_) {}
205
+ touchStartRef.current = {
206
+ x: e.nativeEvent.pageX,
207
+ y: e.nativeEvent.pageY,
208
+ switchValue,
209
+ };
210
+ }, []);
211
+
212
+ const onTouchEnd = useCallback((e: GestureResponderEvent) => {
213
+ if (!enabledRef.current) return;
214
+ const start = touchStartRef.current;
215
+ if (start) {
216
+ const dx = Math.abs(e.nativeEvent.pageX - start.x);
217
+ const dy = Math.abs(e.nativeEvent.pageY - start.y);
218
+ if (dx > TAP_THRESHOLD || dy > TAP_THRESHOLD) return;
219
+ }
220
+
221
+ try {
222
+ let node: any = (e as any)._targetInst;
223
+ if (!node) return;
224
+
225
+ // Pass 1: walk up to find the first interactive ancestor.
226
+ // e.g. tapping a <Text> inside a <Pressable> yields the Pressable.
227
+ let interactiveNode: any = null;
228
+ let depth = 0;
229
+ while (node && depth < MAX_FIBER_DEPTH) {
230
+ if (isInteractive(node.memoizedProps)) {
231
+ interactiveNode = node;
232
+ break;
233
+ }
234
+ node = node.return;
235
+ depth++;
236
+ }
237
+ if (!interactiveNode) return;
238
+
239
+ // Pass 2: resolve identifier from the interactive fiber and up to 2 ancestors.
240
+ // The look-ahead handles TouchableOpacity, where the inner Animated.View fiber
241
+ // (where Pass 1 stops) may carry the accessibilityLabel from the parent TO.
242
+ let identifierNode: any = interactiveNode;
243
+ let identifier: string | undefined;
244
+ for (let i = 0; i < 3 && identifierNode; i++) {
245
+ identifier = resolveIdentifier(identifierNode.memoizedProps);
246
+ if (identifier) break;
247
+ identifierNode = identifierNode.return;
248
+ }
249
+
250
+ if (!identifier) return;
251
+
252
+ const isSwitchNode = isSwitch(interactiveNode.memoizedProps);
253
+ if (!isSwitchNode && isInsideSelectionControl(interactiveNode)) return;
254
+
255
+ const screen = getCurrentScreen();
256
+ if (screen !== lastScreenRef.current) {
257
+ switchLastLoggedRef.current.clear();
258
+ lastScreenRef.current = screen;
259
+ }
260
+
261
+ const listIndex = shouldCollectValuesRef.current
262
+ ? resolveListIndex(interactiveNode)
263
+ : undefined;
264
+ const listType = listIndex
265
+ ? listIndex.type === 'sectionlist'
266
+ ? 'SectionList'
267
+ : 'FlatList'
268
+ : null;
269
+ const indexSuffix = listIndex
270
+ ? listIndex.type === 'sectionlist'
271
+ ? `|section:${listIndex.sectionIndex},index:${listIndex.itemIndex}`
272
+ : `|index:${listIndex.index}`
273
+ : '';
274
+
275
+ if (isSwitchNode) {
276
+ const lastLogged = switchLastLoggedRef.current.get(identifier);
277
+ let newValue: boolean;
278
+ if (lastLogged !== undefined) {
279
+ newValue = !lastLogged;
280
+ } else {
281
+ const preTapValue = touchStartRef.current?.switchValue;
282
+ newValue =
283
+ preTapValue !== undefined
284
+ ? !preTapValue
285
+ : !interactiveNode.memoizedProps.value;
286
+ }
287
+ switchLastLoggedRef.current.set(identifier, newValue);
288
+ const switchPath = buildPath(screen, listType, identifier, indexSuffix, newValue);
289
+ RNNetmera.trackAction(switchPath);
290
+ } else {
291
+ const tapPath = buildPath(screen, listType, identifier, indexSuffix);
292
+ RNNetmera.trackAction(tapPath);
293
+ }
294
+ } catch (_) {
295
+ // Fiber tree is a private React API — fail silently.
296
+ }
297
+ }, []);
298
+
299
+ return { onTouchStart, onTouchEnd };
300
+ }