@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,317 @@
1
+ package io.billdog.reactnative
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
8
+ import com.facebook.react.bridge.ReactMethod
9
+ import com.facebook.react.bridge.ReadableMap
10
+ import com.facebook.react.bridge.WritableArray
11
+ import com.facebook.react.bridge.WritableMap
12
+ import com.facebook.react.modules.core.DeviceEventManagerModule
13
+ import io.billdog.core.BillDog
14
+ import io.billdog.core.BillDogEventBus
15
+ import io.billdog.abtest.ExperimentAssignment
16
+ import kotlinx.coroutines.CoroutineScope
17
+ import kotlinx.coroutines.Dispatchers
18
+ import kotlinx.coroutines.SupervisorJob
19
+ import kotlinx.coroutines.cancel
20
+ import kotlinx.coroutines.launch
21
+ import org.json.JSONArray
22
+ import org.json.JSONObject
23
+
24
+ /**
25
+ * BillDogABTest — React Native native module for Android
26
+ * (`NativeModules.BillDogABTest`).
27
+ *
28
+ * Thin bridge: every `@ReactMethod` here delegates to the concrete A/B-testing
29
+ * manager (`io.billdog.abtest.BillDogABTest`) resolved per-call from the live
30
+ * module registry (`BillDog.getModules()` — billdog-core BillDog.kt:427). The
31
+ * authoritative JS contract is `src/modules/ABTest.ts`; each
32
+ * `BillDogABTest.<method>(...)` call there maps to the identically-named method
33
+ * below. When the manager is not registered it resolves to `null` and calls are
34
+ * best-effort NO-OPs that resolve — matching the JS contract.
35
+ *
36
+ * `onExperimentAssignmentChanged` is emitted by subscribing to the SDK EventBus
37
+ * `AB_VARIANT_ASSIGNED` signal (billdog-core BillDogEventBus.kt:42) that the
38
+ * manager fires on every fetch (BillDogABTest.kt:258).
39
+ *
40
+ * ⚠️ VERIFICATION: this file cannot be compiled in this environment — there is
41
+ * no Android/Gradle toolchain present. It needs a Gradle/Android build to
42
+ * type-check against io.billdog:billdog-core + billdog-ab-test +
43
+ * kotlinx-coroutines. Every native symbol below is cited to file:line.
44
+ */
45
+ class BillDogABTestModule(private val reactContext: ReactApplicationContext) :
46
+ ReactContextBaseJavaModule(reactContext) {
47
+
48
+ private val TAG = "BillDogABTest"
49
+
50
+ /** suspend getVariant() is launched here; cancelled in invalidate(). */
51
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
52
+
53
+ /** EventBus subscription id for AB_VARIANT_ASSIGNED; unsubscribed in invalidate(). */
54
+ private var assignmentSubscription: String? = null
55
+
56
+ override fun getName(): String = "BillDogABTest"
57
+
58
+ init {
59
+ // Bridge the SDK EventBus AB_VARIANT_ASSIGNED signal → RN
60
+ // `onExperimentAssignmentChanged`. subscribe() — BillDogEventBus.kt:77.
61
+ assignmentSubscription = BillDogEventBus.subscribe(
62
+ BillDogEventBus.EventTypes.AB_VARIANT_ASSIGNED // BillDogEventBus.kt:42
63
+ ) { event ->
64
+ val experimentId = event.properties["experiment_id"]?.toString() ?: return@subscribe
65
+ // Resolve the full assignment for the RN payload; fall back to the
66
+ // thin {experiment_id, variant_id} the event itself carries.
67
+ val assignment = abtest()?.getVariantSync(experimentId) // BillDogABTest.kt:171
68
+ val payload = if (assignment != null) {
69
+ serializeAssignment(assignment)
70
+ } else {
71
+ toWritableMap(mapOf(
72
+ "experimentId" to experimentId,
73
+ "variantId" to (event.properties["variant_id"]?.toString() ?: "")
74
+ ))
75
+ }
76
+ emit("onExperimentAssignmentChanged", payload)
77
+ }
78
+ }
79
+
80
+ // ── Lifecycle ──────────────────────────────────────────────────────────
81
+
82
+ override fun invalidate() {
83
+ assignmentSubscription?.let { BillDogEventBus.unsubscribe(it) } // BillDogEventBus.kt:84
84
+ scope.cancel()
85
+ super.invalidate()
86
+ }
87
+
88
+ @Deprecated("Deprecated in RN; kept for older RN versions that call it instead of invalidate()")
89
+ override fun onCatalystInstanceDestroy() {
90
+ assignmentSubscription?.let { BillDogEventBus.unsubscribe(it) }
91
+ scope.cancel()
92
+ @Suppress("DEPRECATION")
93
+ super.onCatalystInstanceDestroy()
94
+ }
95
+
96
+ /** Resolve the concrete A/B manager from the live registry; null → callers NO-OP. */
97
+ private fun abtest(): io.billdog.abtest.BillDogABTest? =
98
+ BillDog.getModules() // billdog-core BillDog.kt:427
99
+ .firstOrNull { it is io.billdog.abtest.BillDogABTest }
100
+ as? io.billdog.abtest.BillDogABTest
101
+
102
+ private fun run(promise: Promise, block: suspend () -> Any?) {
103
+ scope.launch {
104
+ try {
105
+ promise.resolve(block())
106
+ } catch (e: Throwable) {
107
+ promise.reject("BILLDOG_ERROR", e.message, e)
108
+ }
109
+ }
110
+ }
111
+
112
+ // ── Init ────────────────────────────────────────────────────────────────
113
+
114
+ /**
115
+ * BillDogABTest.initialize() — the A/B manager is configured by the core
116
+ * `BillDog.configure(...)`; there is no per-module native init. Resolve null
117
+ * (documented no-op).
118
+ */
119
+ @ReactMethod
120
+ fun initialize(promise: Promise) {
121
+ Log.d(TAG, "initialize() — ABTest configured via BillDog.configure(); no-op")
122
+ promise.resolve(null)
123
+ }
124
+
125
+ /**
126
+ * BillDogABTest.getVariant(experimentId) → mgr.getVariant(experimentId)
127
+ * — BillDogABTest.kt:166 (suspend; joins the in-flight fetch). Returns the
128
+ * serialized ExperimentAssignment or null.
129
+ */
130
+ @ReactMethod
131
+ fun getVariant(experimentId: String, promise: Promise) {
132
+ run(promise) {
133
+ abtest()?.getVariant(experimentId)?.let { serializeAssignment(it) } // :166
134
+ }
135
+ }
136
+
137
+ /**
138
+ * BillDogABTest.trackConversion(experimentId, value, metadata) →
139
+ * mgr.trackConversion(experimentId, value, metadata) — BillDogABTest.kt:206.
140
+ * JS always passes value (default 1) and metadata (default {}).
141
+ */
142
+ @ReactMethod
143
+ @Suppress("UNCHECKED_CAST")
144
+ fun trackConversion(experimentId: String, value: Double, metadata: ReadableMap?, promise: Promise) {
145
+ run(promise) {
146
+ abtest()?.trackConversion(
147
+ experimentId,
148
+ value,
149
+ (metadata?.toHashMap() as? Map<String, Any>) ?: emptyMap()
150
+ ) // :206
151
+ null
152
+ }
153
+ }
154
+
155
+ /** BillDogABTest.trackImpression(experimentId) → mgr.trackImpression(experimentId) — BillDogABTest.kt:236. */
156
+ @ReactMethod
157
+ fun trackImpression(experimentId: String, promise: Promise) {
158
+ run(promise) { abtest()?.trackImpression(experimentId); null } // :236
159
+ }
160
+
161
+ /**
162
+ * BillDogABTest.setUserId(id) → mgr.setUserId(id) — BillDogABTest.kt:284
163
+ * (triggers an automatic experiment refresh internally).
164
+ */
165
+ @ReactMethod
166
+ fun setUserId(id: String, promise: Promise) {
167
+ run(promise) { abtest()?.setUserId(id); null } // :284
168
+ }
169
+
170
+ /** BillDogABTest.setAttributes(attributes) → mgr.setAttributes(attributes) — BillDogABTest.kt:290. */
171
+ @ReactMethod
172
+ @Suppress("UNCHECKED_CAST")
173
+ fun setAttributes(attributes: ReadableMap?, promise: Promise) {
174
+ run(promise) {
175
+ abtest()?.setAttributes((attributes?.toHashMap() as? Map<String, Any>) ?: emptyMap()) // :290
176
+ null
177
+ }
178
+ }
179
+
180
+ /**
181
+ * BillDogABTest.getFeatureFlag(key) → mgr.getFeatureFlag(key) —
182
+ * BillDogABTest.kt:179 (Boolean; unknown keys fail-closed to false). JS types
183
+ * the return as `any`; a boolean satisfies it.
184
+ */
185
+ @ReactMethod
186
+ fun getFeatureFlag(key: String, promise: Promise) {
187
+ run(promise) { abtest()?.getFeatureFlag(key) ?: false } // :179
188
+ }
189
+
190
+ /** BillDogABTest.refreshExperiments() → mgr.refreshExperiments() — BillDogABTest.kt:161. */
191
+ @ReactMethod
192
+ fun refreshExperiments(promise: Promise) {
193
+ run(promise) { abtest()?.refreshExperiments(); null } // :161
194
+ }
195
+
196
+ /** BillDogABTest.reset() → mgr.reset() — BillDogABTest.kt:265. */
197
+ @ReactMethod
198
+ fun reset(promise: Promise) {
199
+ run(promise) { abtest()?.reset(); null } // :265
200
+ }
201
+
202
+ // ── NativeEventEmitter plumbing (onExperimentAssignmentChanged) ──────────
203
+
204
+ @ReactMethod
205
+ fun addListener(eventName: String) { /* NativeEventEmitter bookkeeping — no-op */ }
206
+
207
+ @ReactMethod
208
+ fun removeListeners(count: Int) { /* NativeEventEmitter bookkeeping — no-op */ }
209
+
210
+ private fun emit(event: String, params: WritableMap?) {
211
+ reactContext
212
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
213
+ .emit(event, params)
214
+ }
215
+
216
+ // ── Serializers ──────────────────────────────────────────────────────────
217
+
218
+ /** io.billdog.abtest.ExperimentAssignment (BillDogABTest.kt:383) → JS ExperimentAssignment. */
219
+ private fun serializeAssignment(a: ExperimentAssignment): WritableMap = toWritableMap(mapOf(
220
+ "experimentId" to a.experimentId,
221
+ "variantId" to a.variantId,
222
+ "variantName" to a.variantName,
223
+ "config" to a.config,
224
+ "isControl" to a.isControl
225
+ ))
226
+
227
+ // ── Map<String, Any?> → WritableMap converter (private per-module copy) ───
228
+ // Handles org.json values because ExperimentAssignment.config stores raw
229
+ // JSONObject/JSONArray/primitives decoded from the experiment-config payload.
230
+
231
+ private fun toWritableMap(map: Map<String, Any?>): WritableMap {
232
+ val out = Arguments.createMap()
233
+ for ((key, value) in map) {
234
+ when (value) {
235
+ null, JSONObject.NULL -> out.putNull(key)
236
+ is Boolean -> out.putBoolean(key, value)
237
+ is Int -> out.putInt(key, value)
238
+ is Long -> out.putDouble(key, value.toDouble())
239
+ is Float -> out.putDouble(key, value.toDouble())
240
+ is Double -> out.putDouble(key, value)
241
+ is String -> out.putString(key, value)
242
+ is WritableMap -> out.putMap(key, value)
243
+ is WritableArray -> out.putArray(key, value)
244
+ is JSONObject -> out.putMap(key, jsonObjectToWritable(value))
245
+ is JSONArray -> out.putArray(key, jsonArrayToWritable(value))
246
+ is Map<*, *> -> out.putMap(key, toWritableMap(coerceStringKeys(value)))
247
+ is List<*> -> out.putArray(key, toWritableArray(value))
248
+ else -> out.putString(key, value.toString())
249
+ }
250
+ }
251
+ return out
252
+ }
253
+
254
+ private fun toWritableArray(list: List<*>): WritableArray {
255
+ val out = Arguments.createArray()
256
+ for (value in list) {
257
+ when (value) {
258
+ null, JSONObject.NULL -> out.pushNull()
259
+ is Boolean -> out.pushBoolean(value)
260
+ is Int -> out.pushInt(value)
261
+ is Long -> out.pushDouble(value.toDouble())
262
+ is Float -> out.pushDouble(value.toDouble())
263
+ is Double -> out.pushDouble(value)
264
+ is String -> out.pushString(value)
265
+ is WritableMap -> out.pushMap(value)
266
+ is WritableArray -> out.pushArray(value)
267
+ is JSONObject -> out.pushMap(jsonObjectToWritable(value))
268
+ is JSONArray -> out.pushArray(jsonArrayToWritable(value))
269
+ is Map<*, *> -> out.pushMap(toWritableMap(coerceStringKeys(value)))
270
+ is List<*> -> out.pushArray(toWritableArray(value))
271
+ else -> out.pushString(value.toString())
272
+ }
273
+ }
274
+ return out
275
+ }
276
+
277
+ private fun jsonObjectToWritable(obj: JSONObject): WritableMap {
278
+ val out = Arguments.createMap()
279
+ val keys = obj.keys()
280
+ while (keys.hasNext()) {
281
+ val key = keys.next()
282
+ when (val v = obj.get(key)) {
283
+ JSONObject.NULL -> out.putNull(key)
284
+ is Boolean -> out.putBoolean(key, v)
285
+ is Int -> out.putInt(key, v)
286
+ is Long -> out.putDouble(key, v.toDouble())
287
+ is Double -> out.putDouble(key, v)
288
+ is String -> out.putString(key, v)
289
+ is JSONObject -> out.putMap(key, jsonObjectToWritable(v))
290
+ is JSONArray -> out.putArray(key, jsonArrayToWritable(v))
291
+ else -> out.putString(key, v.toString())
292
+ }
293
+ }
294
+ return out
295
+ }
296
+
297
+ private fun jsonArrayToWritable(arr: JSONArray): WritableArray {
298
+ val out = Arguments.createArray()
299
+ for (i in 0 until arr.length()) {
300
+ when (val v = arr.get(i)) {
301
+ JSONObject.NULL -> out.pushNull()
302
+ is Boolean -> out.pushBoolean(v)
303
+ is Int -> out.pushInt(v)
304
+ is Long -> out.pushDouble(v.toDouble())
305
+ is Double -> out.pushDouble(v)
306
+ is String -> out.pushString(v)
307
+ is JSONObject -> out.pushMap(jsonObjectToWritable(v))
308
+ is JSONArray -> out.pushArray(jsonArrayToWritable(v))
309
+ else -> out.pushString(v.toString())
310
+ }
311
+ }
312
+ return out
313
+ }
314
+
315
+ private fun coerceStringKeys(map: Map<*, *>): Map<String, Any?> =
316
+ map.entries.associate { (k, v) -> k.toString() to v }
317
+ }