@billdog.io/react-native 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/BillDogReactNative.podspec +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +330 -0
  4. package/android/build.gradle +99 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/kotlin/io/billdog/reactnative/BillDogABTestModule.kt +317 -0
  7. package/android/src/main/kotlin/io/billdog/reactnative/BillDogAnalyticsModule.kt +389 -0
  8. package/android/src/main/kotlin/io/billdog/reactnative/BillDogModule.kt +817 -0
  9. package/android/src/main/kotlin/io/billdog/reactnative/BillDogPaywallViewManager.kt +383 -0
  10. package/android/src/main/kotlin/io/billdog/reactnative/BillDogSurveyModule.kt +427 -0
  11. package/android/src/main/kotlin/io/billdog/reactnative/BillDogVirtualCurrencyModule.kt +407 -0
  12. package/android/src/main/kotlin/io/billdog/virtualcurrency/BillDogVirtualCurrencyPackage.kt +44 -0
  13. package/dist/AdvancedPaywallCache.d.ts +70 -0
  14. package/dist/AdvancedPaywallCache.js +344 -0
  15. package/dist/BillDog.d.ts +255 -0
  16. package/dist/BillDog.js +918 -0
  17. package/dist/BillDogConfiguration.d.ts +145 -0
  18. package/dist/BillDogConfiguration.js +229 -0
  19. package/dist/ComponentRenderer.d.ts +17 -0
  20. package/dist/ComponentRenderer.js +886 -0
  21. package/dist/LayoutEngine.d.ts +101 -0
  22. package/dist/LayoutEngine.js +394 -0
  23. package/dist/LocalizationResolver.d.ts +99 -0
  24. package/dist/LocalizationResolver.js +266 -0
  25. package/dist/NativePaywallView.d.ts +36 -0
  26. package/dist/NativePaywallView.js +59 -0
  27. package/dist/PaywallCache.d.ts +74 -0
  28. package/dist/PaywallCache.js +293 -0
  29. package/dist/PaywallStateManager.d.ts +62 -0
  30. package/dist/PaywallStateManager.js +147 -0
  31. package/dist/PaywallView.d.ts +42 -0
  32. package/dist/PaywallView.js +275 -0
  33. package/dist/ProductVariableProvider.d.ts +60 -0
  34. package/dist/ProductVariableProvider.js +239 -0
  35. package/dist/VariableResolver.d.ts +120 -0
  36. package/dist/VariableResolver.js +379 -0
  37. package/dist/WebViewPaywall.d.ts +75 -0
  38. package/dist/WebViewPaywall.js +360 -0
  39. package/dist/animations/presets.d.ts +49 -0
  40. package/dist/animations/presets.js +367 -0
  41. package/dist/components/AccoladeCpsComponent.d.ts +13 -0
  42. package/dist/components/AccoladeCpsComponent.js +248 -0
  43. package/dist/components/AccordionComponent.d.ts +10 -0
  44. package/dist/components/AccordionComponent.js +187 -0
  45. package/dist/components/AnimatedWrapper.d.ts +35 -0
  46. package/dist/components/AnimatedWrapper.js +277 -0
  47. package/dist/components/CarouselComponent.d.ts +23 -0
  48. package/dist/components/CarouselComponent.js +484 -0
  49. package/dist/components/ChartCurveComponent.d.ts +13 -0
  50. package/dist/components/ChartCurveComponent.js +333 -0
  51. package/dist/components/CheckboxComponent.d.ts +9 -0
  52. package/dist/components/CheckboxComponent.js +172 -0
  53. package/dist/components/ConfettiComponent.d.ts +9 -0
  54. package/dist/components/ConfettiComponent.js +172 -0
  55. package/dist/components/CountdownComponent.d.ts +16 -0
  56. package/dist/components/CountdownComponent.js +414 -0
  57. package/dist/components/DelayedXComponent.d.ts +12 -0
  58. package/dist/components/DelayedXComponent.js +240 -0
  59. package/dist/components/DrawerModalComponent.d.ts +20 -0
  60. package/dist/components/DrawerModalComponent.js +286 -0
  61. package/dist/components/GradientView.d.ts +38 -0
  62. package/dist/components/GradientView.js +126 -0
  63. package/dist/components/HapticFeedbackComponent.d.ts +16 -0
  64. package/dist/components/HapticFeedbackComponent.js +64 -0
  65. package/dist/components/HorizontalProductsComponent.d.ts +17 -0
  66. package/dist/components/HorizontalProductsComponent.js +284 -0
  67. package/dist/components/MultiChoiceGridComponent.d.ts +13 -0
  68. package/dist/components/MultiChoiceGridComponent.js +234 -0
  69. package/dist/components/NPSScaleComponent.d.ts +16 -0
  70. package/dist/components/NPSScaleComponent.js +260 -0
  71. package/dist/components/NavigationComponent.d.ts +26 -0
  72. package/dist/components/NavigationComponent.js +302 -0
  73. package/dist/components/OAuthButtonComponent.d.ts +20 -0
  74. package/dist/components/OAuthButtonComponent.js +237 -0
  75. package/dist/components/ProgressLoaderComponent.d.ts +13 -0
  76. package/dist/components/ProgressLoaderComponent.js +308 -0
  77. package/dist/components/ProgressStepperComponent.d.ts +18 -0
  78. package/dist/components/ProgressStepperComponent.js +228 -0
  79. package/dist/components/SegmentedPickerComponent.d.ts +10 -0
  80. package/dist/components/SegmentedPickerComponent.js +221 -0
  81. package/dist/components/ShareButtonsComponent.d.ts +21 -0
  82. package/dist/components/ShareButtonsComponent.js +265 -0
  83. package/dist/components/ShareCodeComponent.d.ts +16 -0
  84. package/dist/components/ShareCodeComponent.js +274 -0
  85. package/dist/components/SingleChoiceGridComponent.d.ts +13 -0
  86. package/dist/components/SingleChoiceGridComponent.js +210 -0
  87. package/dist/components/SliderComponent.d.ts +9 -0
  88. package/dist/components/SliderComponent.js +263 -0
  89. package/dist/components/StarRatingInputComponent.d.ts +20 -0
  90. package/dist/components/StarRatingInputComponent.js +198 -0
  91. package/dist/components/StickerCompositeComponent.d.ts +13 -0
  92. package/dist/components/StickerCompositeComponent.js +136 -0
  93. package/dist/components/TabsComponent.d.ts +19 -0
  94. package/dist/components/TabsComponent.js +431 -0
  95. package/dist/components/TextInputComponent.d.ts +18 -0
  96. package/dist/components/TextInputComponent.js +227 -0
  97. package/dist/components/TimelineComponent.d.ts +19 -0
  98. package/dist/components/TimelineComponent.js +104 -0
  99. package/dist/components/ToggleComponent.d.ts +14 -0
  100. package/dist/components/ToggleComponent.js +132 -0
  101. package/dist/components/VerticalProductsComponent.d.ts +17 -0
  102. package/dist/components/VerticalProductsComponent.js +311 -0
  103. package/dist/dynamicValues.d.ts +36 -0
  104. package/dist/dynamicValues.js +142 -0
  105. package/dist/filters/dateFilters.d.ts +50 -0
  106. package/dist/filters/dateFilters.js +390 -0
  107. package/dist/filters/index.d.ts +18 -0
  108. package/dist/filters/index.js +253 -0
  109. package/dist/hooks/useOfferings.d.ts +76 -0
  110. package/dist/hooks/useOfferings.js +99 -0
  111. package/dist/hooks/usePaywall.d.ts +27 -0
  112. package/dist/hooks/usePaywall.js +114 -0
  113. package/dist/hooks/usePlacement.d.ts +34 -0
  114. package/dist/hooks/usePlacement.js +89 -0
  115. package/dist/hooks/useSubscription.d.ts +40 -0
  116. package/dist/hooks/useSubscription.js +97 -0
  117. package/dist/index.d.ts +98 -0
  118. package/dist/index.js +198 -0
  119. package/dist/models/BillDogError.d.ts +40 -0
  120. package/dist/models/BillDogError.js +11 -0
  121. package/dist/models/CacheFetchPolicy.d.ts +8 -0
  122. package/dist/models/CacheFetchPolicy.js +4 -0
  123. package/dist/models/CustomProduct.d.ts +32 -0
  124. package/dist/models/CustomProduct.js +15 -0
  125. package/dist/models/CustomerInfo.d.ts +17 -0
  126. package/dist/models/CustomerInfo.js +11 -0
  127. package/dist/models/Delegate.d.ts +20 -0
  128. package/dist/models/Delegate.js +11 -0
  129. package/dist/models/EntitlementInfo.d.ts +19 -0
  130. package/dist/models/EntitlementInfo.js +2 -0
  131. package/dist/models/Placement.d.ts +54 -0
  132. package/dist/models/Placement.js +15 -0
  133. package/dist/models/PurchaseParams.d.ts +39 -0
  134. package/dist/models/PurchaseParams.js +60 -0
  135. package/dist/models/PurchaseResult.d.ts +30 -0
  136. package/dist/models/PurchaseResult.js +15 -0
  137. package/dist/models/RestoreResult.d.ts +30 -0
  138. package/dist/models/RestoreResult.js +22 -0
  139. package/dist/models/RestoredItem.d.ts +26 -0
  140. package/dist/models/RestoredItem.js +13 -0
  141. package/dist/modules/ABTest.d.ts +65 -0
  142. package/dist/modules/ABTest.js +113 -0
  143. package/dist/modules/Analytics.d.ts +91 -0
  144. package/dist/modules/Analytics.js +182 -0
  145. package/dist/modules/Survey.d.ts +65 -0
  146. package/dist/modules/Survey.js +82 -0
  147. package/dist/modules/VirtualCurrency.d.ts +63 -0
  148. package/dist/modules/VirtualCurrency.js +147 -0
  149. package/dist/murmur.d.ts +23 -0
  150. package/dist/murmur.js +104 -0
  151. package/dist/notifications/LiveActivitiesManager.d.ts +47 -0
  152. package/dist/notifications/LiveActivitiesManager.js +106 -0
  153. package/dist/notifications/NotificationScheduler.d.ts +55 -0
  154. package/dist/notifications/NotificationScheduler.js +199 -0
  155. package/dist/notifications/PurchaseNotificationHandler.d.ts +24 -0
  156. package/dist/notifications/PurchaseNotificationHandler.js +55 -0
  157. package/dist/notifications/useDeviceToken.d.ts +36 -0
  158. package/dist/notifications/useDeviceToken.js +97 -0
  159. package/dist/notifications/useLiveActivity.d.ts +39 -0
  160. package/dist/notifications/useLiveActivity.js +104 -0
  161. package/dist/review/ReviewAnalytics.d.ts +34 -0
  162. package/dist/review/ReviewAnalytics.js +73 -0
  163. package/dist/review/ReviewManager.d.ts +83 -0
  164. package/dist/review/ReviewManager.js +339 -0
  165. package/dist/review/ReviewTimingManager.d.ts +82 -0
  166. package/dist/review/ReviewTimingManager.js +224 -0
  167. package/dist/survey-core/types.d.ts +215 -0
  168. package/dist/survey-core/types.js +8 -0
  169. package/dist/types.d.ts +438 -0
  170. package/dist/types.js +158 -0
  171. package/dist/utils/ApiClient.d.ts +65 -0
  172. package/dist/utils/ApiClient.js +132 -0
  173. package/dist/utils/CSSConverter.d.ts +49 -0
  174. package/dist/utils/CSSConverter.js +470 -0
  175. package/dist/utils/animations.d.ts +41 -0
  176. package/dist/utils/animations.js +194 -0
  177. package/dist/utils/calendarIntegration.d.ts +36 -0
  178. package/dist/utils/calendarIntegration.js +89 -0
  179. package/dist/utils/configurationResolver.d.ts +23 -0
  180. package/dist/utils/configurationResolver.js +78 -0
  181. package/dist/utils/deviceDetection.d.ts +38 -0
  182. package/dist/utils/deviceDetection.js +59 -0
  183. package/dist/utils/hapticFeedback.d.ts +17 -0
  184. package/dist/utils/hapticFeedback.js +53 -0
  185. package/dist/utils/iconMapper.d.ts +26 -0
  186. package/dist/utils/iconMapper.js +113 -0
  187. package/dist/utils/notificationPermissions.d.ts +13 -0
  188. package/dist/utils/notificationPermissions.js +51 -0
  189. package/dist/utils/priceFormatting.d.ts +19 -0
  190. package/dist/utils/priceFormatting.js +99 -0
  191. package/dist/utils/shareSheet.d.ts +17 -0
  192. package/dist/utils/shareSheet.js +42 -0
  193. package/dist/utils/subscriptionUpgrade.d.ts +84 -0
  194. package/dist/utils/subscriptionUpgrade.js +100 -0
  195. package/dist/valueUtils.d.ts +48 -0
  196. package/dist/valueUtils.js +129 -0
  197. package/dist/xFlowTags.d.ts +18 -0
  198. package/dist/xFlowTags.js +385 -0
  199. package/ios/BillDogABTest.m +50 -0
  200. package/ios/BillDogABTest.swift +188 -0
  201. package/ios/BillDogAnalytics.m +105 -0
  202. package/ios/BillDogAnalytics.swift +324 -0
  203. package/ios/BillDogBridge.m +222 -0
  204. package/ios/BillDogBridge.swift +715 -0
  205. package/ios/BillDogLiveActivities.m +41 -0
  206. package/ios/BillDogLiveActivities.swift +104 -0
  207. package/ios/BillDogPaywallViewManager.m +39 -0
  208. package/ios/BillDogPaywallViewManager.swift +361 -0
  209. package/ios/BillDogSurvey.m +34 -0
  210. package/ios/BillDogSurvey.swift +257 -0
  211. package/ios/BillDogVirtualCurrencyBridge.m +63 -0
  212. package/ios/BillDogVirtualCurrencyBridge.swift +511 -0
  213. package/junit.xml +25 -0
  214. package/package.json +61 -0
  215. package/react-native.config.js +13 -0
  216. package/src/AdvancedPaywallCache.ts +433 -0
  217. package/src/BillDog.ts +983 -0
  218. package/src/BillDogConfiguration.ts +318 -0
  219. package/src/ComponentRenderer.tsx +1325 -0
  220. package/src/LayoutEngine.ts +459 -0
  221. package/src/LocalizationResolver.ts +311 -0
  222. package/src/NativePaywallView.tsx +132 -0
  223. package/src/PaywallCache.ts +358 -0
  224. package/src/PaywallStateManager.ts +154 -0
  225. package/src/PaywallView.tsx +328 -0
  226. package/src/ProductVariableProvider.ts +282 -0
  227. package/src/VariableResolver.ts +516 -0
  228. package/src/WebViewPaywall.tsx +454 -0
  229. package/src/animations/presets.ts +421 -0
  230. package/src/components/AccoladeCpsComponent.tsx +259 -0
  231. package/src/components/AccordionComponent.tsx +213 -0
  232. package/src/components/AnimatedWrapper.tsx +316 -0
  233. package/src/components/CarouselComponent.tsx +615 -0
  234. package/src/components/ChartCurveComponent.tsx +452 -0
  235. package/src/components/CheckboxComponent.tsx +185 -0
  236. package/src/components/ConfettiComponent.tsx +178 -0
  237. package/src/components/CountdownComponent.tsx +454 -0
  238. package/src/components/DelayedXComponent.tsx +266 -0
  239. package/src/components/DrawerModalComponent.tsx +324 -0
  240. package/src/components/GradientView.tsx +129 -0
  241. package/src/components/HapticFeedbackComponent.tsx +39 -0
  242. package/src/components/HorizontalProductsComponent.tsx +329 -0
  243. package/src/components/MultiChoiceGridComponent.tsx +265 -0
  244. package/src/components/NPSScaleComponent.tsx +294 -0
  245. package/src/components/NavigationComponent.tsx +329 -0
  246. package/src/components/OAuthButtonComponent.tsx +266 -0
  247. package/src/components/ProgressLoaderComponent.tsx +366 -0
  248. package/src/components/ProgressStepperComponent.tsx +250 -0
  249. package/src/components/SegmentedPickerComponent.tsx +245 -0
  250. package/src/components/ShareButtonsComponent.tsx +299 -0
  251. package/src/components/ShareCodeComponent.tsx +303 -0
  252. package/src/components/SingleChoiceGridComponent.tsx +229 -0
  253. package/src/components/SliderComponent.tsx +280 -0
  254. package/src/components/StarRatingInputComponent.tsx +223 -0
  255. package/src/components/StickerCompositeComponent.tsx +168 -0
  256. package/src/components/TabsComponent.tsx +496 -0
  257. package/src/components/TextInputComponent.tsx +258 -0
  258. package/src/components/TimelineComponent.tsx +129 -0
  259. package/src/components/ToggleComponent.tsx +142 -0
  260. package/src/components/VerticalProductsComponent.tsx +357 -0
  261. package/src/dynamicValues.ts +180 -0
  262. package/src/filters/dateFilters.ts +411 -0
  263. package/src/filters/index.ts +281 -0
  264. package/src/hooks/useOfferings.ts +192 -0
  265. package/src/hooks/usePaywall.ts +154 -0
  266. package/src/hooks/usePlacement.ts +143 -0
  267. package/src/hooks/useSubscription.ts +154 -0
  268. package/src/index.ts +245 -0
  269. package/src/models/BillDogError.ts +24 -0
  270. package/src/models/CacheFetchPolicy.ts +13 -0
  271. package/src/models/CustomProduct.ts +33 -0
  272. package/src/models/CustomerInfo.ts +26 -0
  273. package/src/models/Delegate.ts +22 -0
  274. package/src/models/EntitlementInfo.ts +27 -0
  275. package/src/models/Placement.ts +50 -0
  276. package/src/models/PurchaseParams.ts +81 -0
  277. package/src/models/PurchaseResult.ts +32 -0
  278. package/src/models/RestoreResult.ts +27 -0
  279. package/src/models/RestoredItem.ts +25 -0
  280. package/src/modules/ABTest.ts +131 -0
  281. package/src/modules/Analytics.ts +227 -0
  282. package/src/modules/Survey.ts +126 -0
  283. package/src/modules/VirtualCurrency.ts +223 -0
  284. package/src/murmur.ts +102 -0
  285. package/src/notifications/LiveActivitiesManager.ts +140 -0
  286. package/src/notifications/NotificationScheduler.ts +292 -0
  287. package/src/notifications/PurchaseNotificationHandler.ts +67 -0
  288. package/src/notifications/useDeviceToken.ts +116 -0
  289. package/src/notifications/useLiveActivity.ts +125 -0
  290. package/src/review/ReviewAnalytics.ts +89 -0
  291. package/src/review/ReviewManager.ts +355 -0
  292. package/src/review/ReviewTimingManager.ts +265 -0
  293. package/src/survey-core/types.ts +244 -0
  294. package/src/types/ambient.d.ts +9 -0
  295. package/src/types.ts +680 -0
  296. package/src/utils/ApiClient.ts +163 -0
  297. package/src/utils/CSSConverter.ts +552 -0
  298. package/src/utils/animations.ts +232 -0
  299. package/src/utils/calendarIntegration.ts +113 -0
  300. package/src/utils/configurationResolver.ts +106 -0
  301. package/src/utils/deviceDetection.ts +83 -0
  302. package/src/utils/hapticFeedback.ts +55 -0
  303. package/src/utils/iconMapper.ts +102 -0
  304. package/src/utils/notificationPermissions.ts +48 -0
  305. package/src/utils/priceFormatting.ts +119 -0
  306. package/src/utils/shareSheet.ts +55 -0
  307. package/src/utils/subscriptionUpgrade.ts +183 -0
  308. package/src/valueUtils.ts +108 -0
  309. package/src/xFlowTags.ts +463 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @billdog.io/react-native — A/B Testing Module
3
+ *
4
+ * Native bridge to BillDogABTest (iOS) / billdog-abtest (Android).
5
+ * Provides experiment assignment, variant retrieval, and conversion tracking.
6
+ *
7
+ * Types and methods align 1:1 with the native SDK surface.
8
+ */
9
+
10
+ import { NativeModules, NativeEventEmitter } from 'react-native';
11
+
12
+ const { BillDogABTest } = NativeModules;
13
+ const eventEmitter = BillDogABTest ? new NativeEventEmitter(BillDogABTest) : null;
14
+
15
+ // MARK: - Types (matching native SDK contracts)
16
+
17
+ export interface ExperimentAssignment {
18
+ experimentId: string;
19
+ variantId: string;
20
+ variantName: string;
21
+ config: Record<string, any>;
22
+ isControl: boolean;
23
+ }
24
+
25
+ // MARK: - A/B Test Manager
26
+
27
+ export class ABTestManager {
28
+ private static instance: ABTestManager | null = null;
29
+ private initialized = false;
30
+
31
+ private constructor() {}
32
+
33
+ static getInstance(): ABTestManager {
34
+ if (!ABTestManager.instance) {
35
+ ABTestManager.instance = new ABTestManager();
36
+ }
37
+ return ABTestManager.instance;
38
+ }
39
+
40
+ /**
41
+ * Initialize the A/B Testing module.
42
+ */
43
+ async initialize(): Promise<void> {
44
+ if (!BillDogABTest) {
45
+ console.warn('[BillDogABTest] Native module not linked. A/B Testing will be no-op.');
46
+ return;
47
+ }
48
+ await BillDogABTest.initialize();
49
+ this.initialized = true;
50
+ }
51
+
52
+ /**
53
+ * Get the variant assignment for an experiment.
54
+ * Returns the cached assignment if available, otherwise fetches from the server.
55
+ */
56
+ async getVariant(experimentId: string): Promise<ExperimentAssignment | null> {
57
+ if (!this.initialized || !BillDogABTest) return null;
58
+ return await BillDogABTest.getVariant(experimentId);
59
+ }
60
+
61
+ /**
62
+ * Track a conversion event for an experiment.
63
+ */
64
+ async trackConversion(
65
+ experimentId: string,
66
+ value?: number,
67
+ metadata?: Record<string, any>
68
+ ): Promise<void> {
69
+ if (!this.initialized || !BillDogABTest) return;
70
+ await BillDogABTest.trackConversion(experimentId, value ?? 1, metadata ?? {});
71
+ }
72
+
73
+ /**
74
+ * Track an impression event for an experiment.
75
+ */
76
+ async trackImpression(experimentId: string): Promise<void> {
77
+ if (!this.initialized || !BillDogABTest) return;
78
+ await BillDogABTest.trackImpression(experimentId);
79
+ }
80
+
81
+ /**
82
+ * Set the user ID for server-side experiment assignment.
83
+ * Triggers automatic experiment refresh (matching native behavior).
84
+ */
85
+ async setUserId(id: string): Promise<void> {
86
+ if (!BillDogABTest) return;
87
+ await BillDogABTest.setUserId(id);
88
+ }
89
+
90
+ /**
91
+ * Set user attributes for experiment targeting.
92
+ */
93
+ async setAttributes(attributes: Record<string, any>): Promise<void> {
94
+ if (!BillDogABTest) return;
95
+ await BillDogABTest.setAttributes(attributes);
96
+ }
97
+
98
+ /**
99
+ * Get a feature flag value.
100
+ */
101
+ async getFeatureFlag(key: string): Promise<any> {
102
+ if (!this.initialized || !BillDogABTest) return null;
103
+ return await BillDogABTest.getFeatureFlag(key);
104
+ }
105
+
106
+ /**
107
+ * Refresh experiment assignments from the server.
108
+ */
109
+ async refreshExperiments(): Promise<void> {
110
+ if (!this.initialized || !BillDogABTest) return;
111
+ await BillDogABTest.refreshExperiments();
112
+ }
113
+
114
+ /**
115
+ * Reset all experiment state and cached assignments.
116
+ */
117
+ async reset(): Promise<void> {
118
+ if (!BillDogABTest) return;
119
+ await BillDogABTest.reset();
120
+ this.initialized = false;
121
+ }
122
+
123
+ /**
124
+ * Listen to assignment changes (e.g., after refresh).
125
+ */
126
+ onAssignmentChanged(callback: (assignment: ExperimentAssignment) => void) {
127
+ return eventEmitter?.addListener('onExperimentAssignmentChanged', callback);
128
+ }
129
+ }
130
+
131
+ export default ABTestManager.getInstance();
@@ -0,0 +1,227 @@
1
+ /**
2
+ * @billdog.io/react-native — Analytics Module
3
+ *
4
+ * Native bridge to BillDogAnalytics (iOS) / billdog-analytics (Android).
5
+ * The native SDKs handle server-driven config (batch size, flush interval, sampling, etc.)
6
+ * fetched from the `sdk-config` edge function at init time.
7
+ * Config priority: client override > server config > SDK defaults (20 batch, 30s flush).
8
+ */
9
+
10
+ import { NativeModules, NativeEventEmitter } from 'react-native';
11
+
12
+ const { BillDogAnalytics } = NativeModules;
13
+ const eventEmitter = BillDogAnalytics ? new NativeEventEmitter(BillDogAnalytics) : null;
14
+
15
+ // MARK: - Types
16
+
17
+ export interface AnalyticsEvent {
18
+ name: string;
19
+ properties?: Record<string, any>;
20
+ timestamp?: number;
21
+ }
22
+
23
+ export interface RevenueEvent {
24
+ /** Revenue amount (in the currency's base unit, e.g., dollars not cents) */
25
+ amount: number;
26
+ /** ISO 4217 currency code */
27
+ currency?: string;
28
+ /** Product identifier */
29
+ productId?: string;
30
+ /** Additional properties */
31
+ properties?: Record<string, any>;
32
+ }
33
+
34
+ export interface UserProperties {
35
+ [key: string]: string | number | boolean | null;
36
+ }
37
+
38
+ export type UserPropertyOperation = '$set' | '$set_once' | '$add' | '$append' | '$remove' | '$unset';
39
+
40
+ // MARK: - Analytics Manager
41
+
42
+ export class AnalyticsManager {
43
+ private static instance: AnalyticsManager | null = null;
44
+ private initialized = false;
45
+
46
+ private constructor() {}
47
+
48
+ static getInstance(): AnalyticsManager {
49
+ if (!AnalyticsManager.instance) {
50
+ AnalyticsManager.instance = new AnalyticsManager();
51
+ }
52
+ return AnalyticsManager.instance;
53
+ }
54
+
55
+ /**
56
+ * Initialize the Analytics module.
57
+ * Called automatically when `BillDogConfiguration.configure()` includes `apiKey`.
58
+ */
59
+ async initialize(): Promise<void> {
60
+ if (!BillDogAnalytics) {
61
+ console.warn('[BillDogAnalytics] Native module not linked. Analytics will be no-op.');
62
+ return;
63
+ }
64
+ await BillDogAnalytics.initialize();
65
+ this.initialized = true;
66
+ }
67
+
68
+ /**
69
+ * Track a custom event.
70
+ */
71
+ async track(event: string | AnalyticsEvent, properties?: Record<string, any>): Promise<void> {
72
+ if (!this.initialized || !BillDogAnalytics) return;
73
+
74
+ const name = typeof event === 'string' ? event : event.name;
75
+ const props = typeof event === 'string' ? properties : event.properties;
76
+
77
+ await BillDogAnalytics.track(name, props || {});
78
+ }
79
+
80
+ /**
81
+ * Track a revenue event (standardized `$revenue` event).
82
+ */
83
+ async trackRevenue(revenue: RevenueEvent): Promise<void> {
84
+ if (!this.initialized || !BillDogAnalytics) return;
85
+
86
+ await BillDogAnalytics.trackRevenue(
87
+ revenue.amount,
88
+ revenue.currency || 'USD',
89
+ revenue.productId || null,
90
+ revenue.properties || {}
91
+ );
92
+ }
93
+
94
+ /**
95
+ * Identify a user.
96
+ */
97
+ async identify(userId: string, properties?: UserProperties): Promise<void> {
98
+ if (!this.initialized || !BillDogAnalytics) return;
99
+ await BillDogAnalytics.identify(userId, properties || {});
100
+ }
101
+
102
+ /**
103
+ * Set super properties (persisted, merged into every event).
104
+ */
105
+ async setSuperProperties(properties: Record<string, any>): Promise<void> {
106
+ if (!this.initialized || !BillDogAnalytics) return;
107
+ await BillDogAnalytics.setSuperProperties(properties);
108
+ }
109
+
110
+ /**
111
+ * Update user properties with a specific operation.
112
+ */
113
+ async updateUserProperties(
114
+ operation: UserPropertyOperation,
115
+ properties: Record<string, any>
116
+ ): Promise<void> {
117
+ if (!this.initialized || !BillDogAnalytics) return;
118
+ await BillDogAnalytics.updateUserProperties(operation, properties);
119
+ }
120
+
121
+ /**
122
+ * Flush pending events immediately.
123
+ */
124
+ async flush(): Promise<void> {
125
+ if (!this.initialized || !BillDogAnalytics) return;
126
+ await BillDogAnalytics.flush();
127
+ }
128
+
129
+ /** Opt out of analytics tracking (GDPR). */
130
+ async optOut(): Promise<void> {
131
+ if (!BillDogAnalytics) return;
132
+ await BillDogAnalytics.optOut();
133
+ }
134
+
135
+ /** Opt back in to analytics tracking. */
136
+ async optIn(): Promise<void> {
137
+ if (!BillDogAnalytics) return;
138
+ await BillDogAnalytics.optIn();
139
+ }
140
+
141
+ /** Reset analytics state (clears identity + super properties). */
142
+ async reset(): Promise<void> {
143
+ if (!BillDogAnalytics) return;
144
+ await BillDogAnalytics.reset();
145
+ this.initialized = false;
146
+ }
147
+
148
+ // ── Parity surface (Sev 2.3 fix) ─────────────────────────────────────
149
+ // Native (iOS/Android) BillDogAnalytics already implements all of these;
150
+ // the RN bridge simply needs to expose them.
151
+
152
+ /** Alias an anonymous user to a known identity. */
153
+ async alias(newId: string, originalId?: string): Promise<void> {
154
+ if (!this.initialized || !BillDogAnalytics) return;
155
+ await BillDogAnalytics.alias(newId, originalId ?? null);
156
+ }
157
+
158
+ /** Start timing an event — duration emitted on next track() of same name. */
159
+ async timeEvent(eventName: string): Promise<void> {
160
+ if (!this.initialized || !BillDogAnalytics) return;
161
+ await BillDogAnalytics.timeEvent(eventName);
162
+ }
163
+
164
+ /** Associate this user with a group (B2B analytics). */
165
+ async setGroup(groupType: string, groupId: string): Promise<void> {
166
+ if (!this.initialized || !BillDogAnalytics) return;
167
+ await BillDogAnalytics.setGroup(groupType, groupId);
168
+ }
169
+
170
+ async setGroupProperties(
171
+ groupType: string,
172
+ groupId: string,
173
+ properties: Record<string, any>,
174
+ ): Promise<void> {
175
+ if (!this.initialized || !BillDogAnalytics) return;
176
+ await BillDogAnalytics.setGroupProperties(groupType, groupId, properties);
177
+ }
178
+
179
+ /** Track a screen view manually. */
180
+ async trackScreen(screenName: string, properties?: Record<string, any>): Promise<void> {
181
+ if (!this.initialized || !BillDogAnalytics) return;
182
+ await BillDogAnalytics.trackScreen(screenName, properties ?? {});
183
+ }
184
+
185
+ /** Track a deep-link URL. */
186
+ async trackDeepLink(url: string): Promise<void> {
187
+ if (!this.initialized || !BillDogAnalytics) return;
188
+ await BillDogAnalytics.trackDeepLink(url);
189
+ }
190
+
191
+ async getDeviceId(): Promise<string | null> {
192
+ if (!BillDogAnalytics) return null;
193
+ return BillDogAnalytics.getDeviceId();
194
+ }
195
+
196
+ async getDistinctId(): Promise<string | null> {
197
+ if (!BillDogAnalytics) return null;
198
+ return BillDogAnalytics.getDistinctId();
199
+ }
200
+
201
+ async getSessionId(): Promise<string | null> {
202
+ if (!BillDogAnalytics) return null;
203
+ return BillDogAnalytics.getSessionId();
204
+ }
205
+
206
+ async registerSuperPropertiesOnce(properties: Record<string, any>): Promise<void> {
207
+ if (!this.initialized || !BillDogAnalytics) return;
208
+ await BillDogAnalytics.registerSuperPropertiesOnce(properties);
209
+ }
210
+
211
+ async unregisterSuperProperty(key: string): Promise<void> {
212
+ if (!this.initialized || !BillDogAnalytics) return;
213
+ await BillDogAnalytics.unregisterSuperProperty(key);
214
+ }
215
+
216
+ async clearSuperProperties(): Promise<void> {
217
+ if (!this.initialized || !BillDogAnalytics) return;
218
+ await BillDogAnalytics.clearSuperProperties();
219
+ }
220
+
221
+ /** Listen to flush events (debugging). */
222
+ onFlush(callback: (data: { eventCount: number }) => void) {
223
+ return eventEmitter?.addListener('onAnalyticsFlush', callback);
224
+ }
225
+ }
226
+
227
+ export default AnalyticsManager.getInstance();
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @billdog.io/react-native — Survey Module
3
+ *
4
+ * Native bridge to BDSurvey (iOS) / billdog-survey (Android).
5
+ * Provides survey presentation, response collection, and template loading.
6
+ */
7
+
8
+ import { NativeModules, NativeEventEmitter } from 'react-native';
9
+
10
+ const { BillDogSurvey } = NativeModules;
11
+ const eventEmitter = BillDogSurvey ? new NativeEventEmitter(BillDogSurvey) : null;
12
+
13
+ // MARK: - Canonical Types (re-exported from survey-core)
14
+
15
+ import type {
16
+ SurveyConfiguration,
17
+ SurveyPage,
18
+ SurveyQuestion,
19
+ SurveyChoice,
20
+ SurveyAnswer,
21
+ SurveyMobileDisplayConfig,
22
+ // Local copy of packages/survey-core/src/types (see src/survey-core/types.ts).
23
+ // Copied because tsc rootDir cannot span files outside this package. These are
24
+ // type-only imports (no runtime), so a local copy preserves exact type fidelity.
25
+ } from '../survey-core/types';
26
+
27
+ export type { SurveyConfiguration, SurveyPage, SurveyQuestion, SurveyChoice, SurveyAnswer, SurveyMobileDisplayConfig };
28
+
29
+ /** Alias for backward compatibility */
30
+ export type Survey = SurveyConfiguration;
31
+
32
+ // MARK: - RN-Specific Types (bridge contracts, not canonical data)
33
+
34
+ export interface SurveyResponse {
35
+ survey_id: string;
36
+ answers: SurveyAnswer[];
37
+ completed_at?: string;
38
+ duration_ms?: number;
39
+ }
40
+
41
+ export interface SurveyPresentationOptions {
42
+ /** Survey identifier to display */
43
+ surveyIdentifier: string;
44
+ /** Optional customer ID for response attribution */
45
+ customerId?: string;
46
+ /** Optional context data passed to the survey */
47
+ context?: Record<string, any>;
48
+ /** Whether the survey can be dismissed */
49
+ isDismissible?: boolean;
50
+ }
51
+
52
+ // MARK: - Survey Manager
53
+
54
+ export class SurveyManager {
55
+ private static instance: SurveyManager | null = null;
56
+ private initialized = false;
57
+
58
+ private constructor() {}
59
+
60
+ static getInstance(): SurveyManager {
61
+ if (!SurveyManager.instance) {
62
+ SurveyManager.instance = new SurveyManager();
63
+ }
64
+ return SurveyManager.instance;
65
+ }
66
+
67
+ /**
68
+ * Initialize the Survey module.
69
+ */
70
+ async initialize(): Promise<void> {
71
+ if (!BillDogSurvey) {
72
+ console.warn('[BillDogSurvey] Native module not linked. Surveys will be no-op.');
73
+ return;
74
+ }
75
+ await BillDogSurvey.initialize();
76
+ this.initialized = true;
77
+ }
78
+
79
+ /**
80
+ * Present a survey to the user.
81
+ */
82
+ async presentSurvey(options: SurveyPresentationOptions): Promise<SurveyResponse | null> {
83
+ if (!this.initialized || !BillDogSurvey) return null;
84
+ return await BillDogSurvey.presentSurvey(options);
85
+ }
86
+
87
+ /**
88
+ * Fetch a survey configuration without presenting it.
89
+ */
90
+ async fetchSurvey(surveyIdentifier: string): Promise<Survey | null> {
91
+ if (!this.initialized || !BillDogSurvey) return null;
92
+ return await BillDogSurvey.fetchSurvey(surveyIdentifier);
93
+ }
94
+
95
+ /**
96
+ * Submit a survey response programmatically.
97
+ */
98
+ async submitResponse(response: SurveyResponse): Promise<boolean> {
99
+ if (!this.initialized || !BillDogSurvey) return false;
100
+ return await BillDogSurvey.submitResponse(response);
101
+ }
102
+
103
+ /**
104
+ * Check if a survey is eligible to be shown to the current user.
105
+ */
106
+ async canShowSurvey(surveyIdentifier: string): Promise<boolean> {
107
+ if (!this.initialized || !BillDogSurvey) return false;
108
+ return await BillDogSurvey.canShowSurvey(surveyIdentifier);
109
+ }
110
+
111
+ /**
112
+ * Listen to survey completion events.
113
+ */
114
+ onSurveyCompleted(callback: (response: SurveyResponse) => void) {
115
+ return eventEmitter?.addListener('onSurveyCompleted', callback);
116
+ }
117
+
118
+ /**
119
+ * Listen to survey dismissal events.
120
+ */
121
+ onSurveyDismissed(callback: (data: { surveyId: string; pageIndex: number }) => void) {
122
+ return eventEmitter?.addListener('onSurveyDismissed', callback);
123
+ }
124
+ }
125
+
126
+ export default SurveyManager.getInstance();
@@ -0,0 +1,223 @@
1
+ /**
2
+ * @billdog.io/react-native — Virtual Currency Module
3
+ * Version: 1.0.0
4
+ */
5
+
6
+ import { NativeModules, NativeEventEmitter } from 'react-native';
7
+ import { BillDogConfiguration } from '../BillDogConfiguration';
8
+
9
+ const { BillDogVirtualCurrency } = NativeModules;
10
+ const eventEmitter = BillDogVirtualCurrency ? new NativeEventEmitter(BillDogVirtualCurrency) : null;
11
+
12
+ /**
13
+ * Fail loud with a clear, actionable message when the native module is not linked
14
+ * (e.g. Expo Go, a JS-only Metro run, or a host app that hasn't rebuilt after
15
+ * adding the SDK) instead of a cryptic `Cannot read property '…' of undefined`.
16
+ * The native module is registered by `BillDogVirtualCurrencyPackage` on Android
17
+ * and `BillDogVirtualCurrencyBridge` on iOS — both require a native rebuild.
18
+ */
19
+ function requireNative(): any {
20
+ if (!BillDogVirtualCurrency) {
21
+ throw new Error(
22
+ '[BillDogVirtualCurrency] Native module not linked. Rebuild the app with the ' +
23
+ 'native SDK (npx expo prebuild + a dev-client/EAS build, or pod install + ' +
24
+ 'gradle sync) — the Virtual Currency bridge is not available in Expo Go.',
25
+ );
26
+ }
27
+ return BillDogVirtualCurrency;
28
+ }
29
+
30
+ /**
31
+ * Normalize a native transaction row into the declared `VirtualCurrencyTransaction`
32
+ * shape. The native bridges resolve the backend row as-is, which is snake_case
33
+ * (`currency_id`, `created_at`); this maps both snake_case and camelCase so the
34
+ * public type is honest on iOS and Android alike.
35
+ */
36
+ function normalizeTransaction(raw: any): VirtualCurrencyTransaction {
37
+ const ts =
38
+ typeof raw?.timestamp === 'number'
39
+ ? raw.timestamp
40
+ : raw?.created_at
41
+ ? Date.parse(raw.created_at) || 0
42
+ : 0;
43
+ return {
44
+ id: String(raw?.id ?? ''),
45
+ currencyId: raw?.currencyId ?? raw?.currency_id ?? '',
46
+ amount: Number(raw?.amount ?? 0),
47
+ type: (raw?.type ?? 'earn') as VirtualCurrencyTransaction['type'],
48
+ reason: raw?.reason ?? undefined,
49
+ timestamp: ts,
50
+ metadata: raw?.metadata ?? undefined,
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Normalize a native currency row into the declared `VirtualCurrency` shape.
56
+ * The all-balances backend route returns `{ currency: {id,code,name,icon}, balance }`
57
+ * (nested; the display symbol lives in `icon`), so map that shape as well as a
58
+ * flat one. See supabase/functions/virtual-currency/index.ts (handleGetAllBalances).
59
+ */
60
+ function normalizeCurrency(raw: any): VirtualCurrency {
61
+ const c = raw?.currency ?? raw;
62
+ return {
63
+ id: String(c?.id ?? ''),
64
+ name: c?.name ?? '',
65
+ code: c?.code ?? '',
66
+ symbol: c?.symbol ?? c?.icon ?? '',
67
+ balance: Number(raw?.balance ?? c?.balance ?? 0),
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Normalize a native exchange result into the declared `ExchangeResult` shape.
73
+ * The iOS bridge resolves the raw backend payload (snake_case: `to_amount`,
74
+ * `exchange_rate`, `new_from_balance`); the Android bridge already emits
75
+ * camelCase. Accept both so the public type is honest on either platform.
76
+ */
77
+ function normalizeExchange(
78
+ raw: any,
79
+ fromCurrencyId: string,
80
+ toCurrencyId: string,
81
+ fromAmount: number,
82
+ ): ExchangeResult {
83
+ const num = (a: any, b: any, d = 0): number => Number(a ?? b ?? d);
84
+ return {
85
+ success: raw?.success ?? !raw?.error,
86
+ fromCurrencyId: raw?.fromCurrencyId ?? raw?.from_currency_id ?? fromCurrencyId,
87
+ toCurrencyId: raw?.toCurrencyId ?? raw?.to_currency_id ?? toCurrencyId,
88
+ fromAmount: num(raw?.fromAmount, raw?.from_amount, fromAmount),
89
+ toAmount: num(raw?.toAmount, raw?.to_amount),
90
+ exchangeRate: num(raw?.exchangeRate, raw?.exchange_rate),
91
+ newFromBalance: num(raw?.newFromBalance, raw?.new_from_balance),
92
+ newToBalance: num(raw?.newToBalance, raw?.new_to_balance),
93
+ error: raw?.error ?? undefined,
94
+ };
95
+ }
96
+
97
+ export interface VirtualCurrency {
98
+ id: string;
99
+ name: string;
100
+ code: string;
101
+ symbol: string;
102
+ balance: number;
103
+ }
104
+
105
+ export interface VirtualCurrencyTransaction {
106
+ id: string;
107
+ currencyId: string;
108
+ amount: number;
109
+ type: 'earn' | 'spend' | 'grant' | 'exchange';
110
+ reason?: string;
111
+ timestamp: number;
112
+ metadata?: Record<string, any>;
113
+ }
114
+
115
+ export interface ExchangeResult {
116
+ success: boolean;
117
+ fromCurrencyId: string;
118
+ toCurrencyId: string;
119
+ fromAmount: number;
120
+ toAmount: number;
121
+ exchangeRate: number;
122
+ newFromBalance: number;
123
+ newToBalance: number;
124
+ error?: string;
125
+ }
126
+
127
+ export interface VirtualCurrencyConfig {
128
+ apiKey: string;
129
+ customerId: string;
130
+ enableLogging?: boolean;
131
+ }
132
+
133
+ export class VirtualCurrencyManager {
134
+ private static instance: VirtualCurrencyManager | null = null;
135
+ private config: VirtualCurrencyConfig | null = null;
136
+
137
+ private constructor() {}
138
+
139
+ static getInstance(): VirtualCurrencyManager {
140
+ if (!VirtualCurrencyManager.instance) {
141
+ VirtualCurrencyManager.instance = new VirtualCurrencyManager();
142
+ }
143
+ return VirtualCurrencyManager.instance;
144
+ }
145
+
146
+ async initialize(config: VirtualCurrencyConfig): Promise<void> {
147
+ this.config = config;
148
+ // baseUrl is the single branded host (api.billdog.io/v1), not caller-supplied.
149
+ await requireNative().initialize({
150
+ ...config,
151
+ baseUrl: BillDogConfiguration.getApiBaseUrl(),
152
+ });
153
+ }
154
+
155
+ /** True when the native Virtual Currency bridge is linked into the host app. */
156
+ isAvailable(): boolean {
157
+ return !!BillDogVirtualCurrency;
158
+ }
159
+
160
+ async getBalance(currencyId: string): Promise<number> {
161
+ return await requireNative().getBalance(currencyId);
162
+ }
163
+
164
+ async addCurrency(
165
+ currencyId: string,
166
+ amount: number,
167
+ reason?: string,
168
+ metadata?: Record<string, any>
169
+ ): Promise<number> {
170
+ return await requireNative().addCurrency(currencyId, amount, reason, metadata);
171
+ }
172
+
173
+ async deductCurrency(
174
+ currencyId: string,
175
+ amount: number,
176
+ reason?: string,
177
+ metadata?: Record<string, any>
178
+ ): Promise<number> {
179
+ return await requireNative().deductCurrency(currencyId, amount, reason, metadata);
180
+ }
181
+
182
+ async grantCurrency(
183
+ currencyId: string,
184
+ amount: number,
185
+ reason?: string
186
+ ): Promise<number> {
187
+ return await requireNative().grantCurrency(currencyId, amount, reason);
188
+ }
189
+
190
+ async getTransactionHistory(
191
+ currencyId?: string,
192
+ limit: number = 50
193
+ ): Promise<VirtualCurrencyTransaction[]> {
194
+ const raw = await requireNative().getTransactionHistory(currencyId, limit);
195
+ return Array.isArray(raw) ? raw.map(normalizeTransaction) : [];
196
+ }
197
+
198
+ async exchangeCurrency(
199
+ fromCurrencyId: string,
200
+ toCurrencyId: string,
201
+ fromAmount: number
202
+ ): Promise<ExchangeResult> {
203
+ const raw = await requireNative().exchangeCurrency(fromCurrencyId, toCurrencyId, fromAmount);
204
+ return normalizeExchange(raw, fromCurrencyId, toCurrencyId, fromAmount);
205
+ }
206
+
207
+ async getAllCurrencies(): Promise<VirtualCurrency[]> {
208
+ const raw = await requireNative().getAllCurrencies();
209
+ return Array.isArray(raw) ? raw.map(normalizeCurrency) : [];
210
+ }
211
+
212
+ onBalanceChanged(callback: (data: { currencyId: string; newBalance: number }) => void) {
213
+ if (!eventEmitter) return { remove() {} };
214
+ return eventEmitter.addListener('onBalanceChanged', callback);
215
+ }
216
+
217
+ onTransaction(callback: (transaction: VirtualCurrencyTransaction) => void) {
218
+ if (!eventEmitter) return { remove() {} };
219
+ return eventEmitter.addListener('onTransaction', callback);
220
+ }
221
+ }
222
+
223
+ export default VirtualCurrencyManager.getInstance();