@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,257 @@
1
+ //
2
+ // BillDogSurvey.swift
3
+ // @billdog.io/react-native
4
+ //
5
+ // React Native bridge → native `BillDogSurveyPresenter` / `BillDogSurveyManager`.
6
+ //
7
+ // ⚠️ VERIFICATION: there is no iOS/CocoaPods toolchain in the authoring
8
+ // environment. This file is written to be internally consistent and
9
+ // compile-ready against the verified native signatures (cited inline as
10
+ // `BillDog.swift:<line>` / `BillDogSurveyManager.swift:<line>` /
11
+ // `BillDogSurveyPresenter.swift:<line>`), but it REQUIRES a `pod install` +
12
+ // Xcode build to actually compile and run.
13
+ //
14
+ // Survey APIs are @available(iOS 15.0, *) — every call is guarded; on older
15
+ // OSes each method is a best-effort no-op (resolve nil/false), matching the JS
16
+ // side which no-ops when the native module is unavailable.
17
+ //
18
+
19
+ import Foundation
20
+ import React
21
+ import BillDogFull
22
+
23
+ @objc(BillDogSurvey)
24
+ class BillDogSurvey: RCTEventEmitter {
25
+
26
+ private var hasListeners = false
27
+ private var completedSubId: String?
28
+ private var dismissedSubId: String?
29
+
30
+ override static func requiresMainQueueSetup() -> Bool {
31
+ return true // presentSurvey touches UIKit — set up on the main queue.
32
+ }
33
+
34
+ override func supportedEvents() -> [String]! {
35
+ return ["onSurveyCompleted", "onSurveyDismissed"]
36
+ }
37
+
38
+ override func startObserving() {
39
+ hasListeners = true
40
+ // Native surveys emit on the in-process EventBus for EVERY present path
41
+ // (RN, paywall-triggered, event-triggered). Subscribe there so the RN
42
+ // listeners fire regardless of who presented the survey.
43
+ // survey_completed carries {survey_id, respondent_id}; survey_dismissed
44
+ // carries {survey_id}. (BillDogEventBus.swift:40-41,
45
+ // BillDogSurveyPresenter.swift:59 / :55)
46
+ completedSubId = BillDogEventBus.shared.subscribe(
47
+ BillDogEventBus.EventTypes.surveyCompleted
48
+ ) { [weak self] event in
49
+ guard let self = self, self.hasListeners else { return }
50
+ // PARTIAL SurveyResponse — the bus does not carry the answer list.
51
+ self.sendEvent(withName: "onSurveyCompleted", body: [
52
+ "survey_id": event.properties["survey_id"] as? String ?? "",
53
+ "respondent_id": event.properties["respondent_id"] as? String ?? ""
54
+ ])
55
+ }
56
+ dismissedSubId = BillDogEventBus.shared.subscribe(
57
+ BillDogEventBus.EventTypes.surveyDismissed
58
+ ) { [weak self] event in
59
+ guard let self = self, self.hasListeners else { return }
60
+ // `pageIndex` has no native equivalent on the dismiss event — reported as 0.
61
+ self.sendEvent(withName: "onSurveyDismissed", body: [
62
+ "surveyId": event.properties["survey_id"] as? String ?? "",
63
+ "pageIndex": 0
64
+ ])
65
+ }
66
+ }
67
+
68
+ override func stopObserving() {
69
+ hasListeners = false
70
+ if let id = completedSubId { BillDogEventBus.shared.unsubscribe(id); completedSubId = nil }
71
+ if let id = dismissedSubId { BillDogEventBus.shared.unsubscribe(id); dismissedSubId = nil }
72
+ }
73
+
74
+ // MARK: - Lifecycle
75
+
76
+ // No native `initialize()` — the presenter is materialized lazily by the
77
+ // SDK (BillDog.swift:73). Resolve as a best-effort no-op.
78
+ @objc
79
+ func initialize(_ resolve: @escaping RCTPromiseResolveBlock,
80
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
81
+ resolve(nil)
82
+ }
83
+
84
+ // MARK: - Presentation
85
+
86
+ @objc
87
+ func presentSurvey(_ options: NSDictionary,
88
+ resolver resolve: @escaping RCTPromiseResolveBlock,
89
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
90
+ guard #available(iOS 15.0, *) else { resolve(nil); return }
91
+ // BillDog.getSurveyPresenter() -> BillDogSurveyPresenter? (BillDog.swift:315)
92
+ guard let presenter = BillDog.getSurveyPresenter() else { resolve(nil); return }
93
+
94
+ let surveyId = options["surveyIdentifier"] as? String ?? ""
95
+ let customerId = options["customerId"] as? String
96
+ // NOTE: `context` and `isDismissible` from SurveyPresentationOptions have
97
+ // no counterpart on presenter.present(...) — they are not forwarded.
98
+
99
+ DispatchQueue.main.async {
100
+ guard let root = self.topViewController() else { resolve(nil); return }
101
+
102
+ // Resolve the promise exactly once (complete -> response, dismiss -> nil).
103
+ var settled = false
104
+ let settleOnce: (Any?) -> Void = { value in
105
+ if settled { return }
106
+ settled = true
107
+ resolve(value)
108
+ }
109
+
110
+ // presenter.present(surveyId:from:handler:customerId:...) (BillDogSurveyPresenter.swift:39)
111
+ // The handler type (SurveyPresentationHandler, SurveyModels.swift:269)
112
+ // is inferred — closures return SurveySubmitResult (SurveyModels.swift:243).
113
+ let handler = SurveyPresentationHandler(
114
+ onComplete: { _, result in
115
+ let answers = (result.answers ?? []).map { self.serializeAnswer($0) }
116
+ settleOnce([
117
+ "survey_id": result.survey_id,
118
+ "answers": answers,
119
+ "completed_at": ISO8601DateFormatter().string(from: Date())
120
+ ])
121
+ },
122
+ onDismiss: { _ in
123
+ settleOnce(nil)
124
+ },
125
+ onError: { error in
126
+ if settled { return }
127
+ settled = true
128
+ reject("BILLDOG_ERROR", error.localizedDescription, error)
129
+ }
130
+ )
131
+
132
+ presenter.present(
133
+ surveyId: surveyId,
134
+ from: root,
135
+ handler: handler,
136
+ customerId: customerId
137
+ )
138
+ }
139
+ }
140
+
141
+ // MARK: - Fetch
142
+
143
+ @objc
144
+ func fetchSurvey(_ surveyIdentifier: String,
145
+ resolver resolve: @escaping RCTPromiseResolveBlock,
146
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
147
+ guard #available(iOS 15.0, *) else { resolve(nil); return }
148
+ guard let presenter = BillDog.getSurveyPresenter() else { resolve(nil); return }
149
+ let manager = presenter.surveyManager // BillDogSurveyPresenter.swift:10
150
+ Task {
151
+ do {
152
+ // manager.fetchSurvey(surveyId:...) async throws -> SurveyConfig
153
+ // (BillDogSurveyManager.swift:49)
154
+ let config = try await manager.fetchSurvey(surveyId: surveyIdentifier)
155
+ resolve(self.serializeCodable(config))
156
+ } catch {
157
+ reject("BILLDOG_ERROR", error.localizedDescription, error)
158
+ }
159
+ }
160
+ }
161
+
162
+ // MARK: - Submit
163
+
164
+ @objc
165
+ func submitResponse(_ response: NSDictionary,
166
+ resolver resolve: @escaping RCTPromiseResolveBlock,
167
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
168
+ guard #available(iOS 15.0, *) else { resolve(false); return }
169
+ guard let presenter = BillDog.getSurveyPresenter() else { resolve(false); return }
170
+ let manager = presenter.surveyManager
171
+
172
+ let surveyId = response["survey_id"] as? String ?? ""
173
+ let rawAnswers = response["answers"] as? [[String: Any]] ?? []
174
+ let answers = rawAnswers.map { self.deserializeAnswer($0) }
175
+
176
+ Task {
177
+ do {
178
+ // manager.submitSurvey(surveyId:answers:...) async throws -> SurveySubmitResult
179
+ // (BillDogSurveyManager.swift:53)
180
+ _ = try await manager.submitSurvey(surveyId: surveyId, answers: answers)
181
+ resolve(true)
182
+ } catch {
183
+ reject("BILLDOG_ERROR", error.localizedDescription, error)
184
+ }
185
+ }
186
+ }
187
+
188
+ // MARK: - Eligibility
189
+
190
+ @objc
191
+ func canShowSurvey(_ surveyIdentifier: String,
192
+ resolver resolve: @escaping RCTPromiseResolveBlock,
193
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
194
+ guard #available(iOS 15.0, *) else { resolve(false); return }
195
+ guard let presenter = BillDog.getSurveyPresenter() else { resolve(false); return }
196
+ // No native "canShow" eligibility check is exposed. The closest reachable
197
+ // signal is hasCompletedSurvey(surveyId:) (BillDogSurveyPresenter.swift:122):
198
+ // a survey the user already completed cannot be shown again. This is an
199
+ // APPROXIMATION — server-side display_conditions are NOT evaluated here.
200
+ resolve(!presenter.hasCompletedSurvey(surveyId: surveyIdentifier))
201
+ }
202
+
203
+ // MARK: - Helpers
204
+
205
+ /// Topmost presented view controller from the active key window (main thread).
206
+ private func topViewController() -> UIViewController? {
207
+ let keyWindow = UIApplication.shared.connectedScenes
208
+ .compactMap { $0 as? UIWindowScene }
209
+ .flatMap { $0.windows }
210
+ .first { $0.isKeyWindow }
211
+ var vc = keyWindow?.rootViewController
212
+ while let presented = vc?.presentedViewController {
213
+ vc = presented
214
+ }
215
+ return vc
216
+ }
217
+
218
+ /// SurveyAnswer (BillDogCore/Models/SurveyAnswer.swift:4) -> JS dict.
219
+ private func serializeAnswer(_ answer: SurveyAnswer) -> [String: Any] {
220
+ var dict: [String: Any] = ["question_id": answer.question_id]
221
+ if let choiceId = answer.choice_id { dict["choice_id"] = choiceId }
222
+ if let text = answer.answer_text { dict["answer_text"] = text }
223
+ if let number = answer.answer_number { dict["answer_number"] = number }
224
+ if let json = answer.answer_json { dict["answer_json"] = json.mapValues { $0.value } }
225
+ return dict
226
+ }
227
+
228
+ /// JS dict -> SurveyAnswer (accepts snake_case or camelCase keys).
229
+ ///
230
+ /// NOTE: `answer_json` is intentionally NOT forwarded. Its native type is
231
+ /// `[String: AnyCodable]`, and `AnyCodable` is declared in more than one
232
+ /// BillDog target (Core + ABTest), so spelling the type in the umbrella
233
+ /// `import BillDog` is ambiguous. Scalar answers (choice/text/number) — the
234
+ /// common survey answer shapes — are forwarded fully.
235
+ private func deserializeAnswer(_ dict: [String: Any]) -> SurveyAnswer {
236
+ let questionId = (dict["question_id"] as? String) ?? (dict["questionId"] as? String) ?? ""
237
+ let choiceId = (dict["choice_id"] as? String) ?? (dict["choiceId"] as? String)
238
+ let answerText = (dict["answer_text"] as? String) ?? (dict["answerText"] as? String)
239
+ let answerNumber = (dict["answer_number"] as? NSNumber)?.doubleValue
240
+ ?? (dict["answerNumber"] as? NSNumber)?.doubleValue
241
+ return SurveyAnswer(
242
+ questionId: questionId,
243
+ choiceId: choiceId,
244
+ answerText: answerText,
245
+ answerNumber: answerNumber
246
+ )
247
+ }
248
+
249
+ /// Encode a Codable native model into a JSON-object graph for the RN bridge.
250
+ private func serializeCodable<T: Encodable>(_ value: T) -> Any? {
251
+ guard let data = try? JSONEncoder().encode(value),
252
+ let object = try? JSONSerialization.jsonObject(with: data) else {
253
+ return nil
254
+ }
255
+ return object
256
+ }
257
+ }
@@ -0,0 +1,63 @@
1
+ //
2
+ // BillDogVirtualCurrencyBridge.m
3
+ // @billdog.io/react-native
4
+ //
5
+ // React Native bridge for BillDog Virtual Currency module
6
+ //
7
+
8
+ #import <React/RCTBridgeModule.h>
9
+ #import <React/RCTEventEmitter.h>
10
+
11
+ @interface RCT_EXTERN_MODULE(BillDogVirtualCurrency, RCTEventEmitter)
12
+
13
+ // Initialize the Virtual Currency module
14
+ RCT_EXTERN_METHOD(initialize:(NSDictionary *)config
15
+ resolver:(RCTPromiseResolveBlock)resolve
16
+ rejecter:(RCTPromiseRejectBlock)reject)
17
+
18
+ // Get balance for a specific currency
19
+ RCT_EXTERN_METHOD(getBalance:(NSString *)currencyId
20
+ resolver:(RCTPromiseResolveBlock)resolve
21
+ rejecter:(RCTPromiseRejectBlock)reject)
22
+
23
+ // Add currency to balance
24
+ RCT_EXTERN_METHOD(addCurrency:(NSString *)currencyId
25
+ amount:(NSNumber *)amount
26
+ reason:(NSString *)reason
27
+ metadata:(NSDictionary *)metadata
28
+ resolver:(RCTPromiseResolveBlock)resolve
29
+ rejecter:(RCTPromiseRejectBlock)reject)
30
+
31
+ // Deduct currency from balance
32
+ RCT_EXTERN_METHOD(deductCurrency:(NSString *)currencyId
33
+ amount:(NSNumber *)amount
34
+ reason:(NSString *)reason
35
+ metadata:(NSDictionary *)metadata
36
+ resolver:(RCTPromiseResolveBlock)resolve
37
+ rejecter:(RCTPromiseRejectBlock)reject)
38
+
39
+ // Grant currency (admin/test operation)
40
+ RCT_EXTERN_METHOD(grantCurrency:(NSString *)currencyId
41
+ amount:(NSNumber *)amount
42
+ reason:(NSString *)reason
43
+ resolver:(RCTPromiseResolveBlock)resolve
44
+ rejecter:(RCTPromiseRejectBlock)reject)
45
+
46
+ // Get transaction history
47
+ RCT_EXTERN_METHOD(getTransactionHistory:(NSString *)currencyId
48
+ limit:(NSNumber *)limit
49
+ resolver:(RCTPromiseResolveBlock)resolve
50
+ rejecter:(RCTPromiseRejectBlock)reject)
51
+
52
+ // Exchange one currency for another
53
+ RCT_EXTERN_METHOD(exchangeCurrency:(NSString *)fromCurrencyId
54
+ toCurrencyId:(NSString *)toCurrencyId
55
+ fromAmount:(NSNumber *)fromAmount
56
+ resolver:(RCTPromiseResolveBlock)resolve
57
+ rejecter:(RCTPromiseRejectBlock)reject)
58
+
59
+ // Get all currencies with balances
60
+ RCT_EXTERN_METHOD(getAllCurrencies:(RCTPromiseResolveBlock)resolve
61
+ rejecter:(RCTPromiseRejectBlock)reject)
62
+
63
+ @end