@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.
- package/BillDogReactNative.podspec +30 -0
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/android/build.gradle +99 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogABTestModule.kt +317 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogAnalyticsModule.kt +389 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogModule.kt +817 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogPaywallViewManager.kt +383 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogSurveyModule.kt +427 -0
- package/android/src/main/kotlin/io/billdog/reactnative/BillDogVirtualCurrencyModule.kt +407 -0
- package/android/src/main/kotlin/io/billdog/virtualcurrency/BillDogVirtualCurrencyPackage.kt +44 -0
- package/dist/AdvancedPaywallCache.d.ts +70 -0
- package/dist/AdvancedPaywallCache.js +344 -0
- package/dist/BillDog.d.ts +255 -0
- package/dist/BillDog.js +918 -0
- package/dist/BillDogConfiguration.d.ts +145 -0
- package/dist/BillDogConfiguration.js +229 -0
- package/dist/ComponentRenderer.d.ts +17 -0
- package/dist/ComponentRenderer.js +886 -0
- package/dist/LayoutEngine.d.ts +101 -0
- package/dist/LayoutEngine.js +394 -0
- package/dist/LocalizationResolver.d.ts +99 -0
- package/dist/LocalizationResolver.js +266 -0
- package/dist/NativePaywallView.d.ts +36 -0
- package/dist/NativePaywallView.js +59 -0
- package/dist/PaywallCache.d.ts +74 -0
- package/dist/PaywallCache.js +293 -0
- package/dist/PaywallStateManager.d.ts +62 -0
- package/dist/PaywallStateManager.js +147 -0
- package/dist/PaywallView.d.ts +42 -0
- package/dist/PaywallView.js +275 -0
- package/dist/ProductVariableProvider.d.ts +60 -0
- package/dist/ProductVariableProvider.js +239 -0
- package/dist/VariableResolver.d.ts +120 -0
- package/dist/VariableResolver.js +379 -0
- package/dist/WebViewPaywall.d.ts +75 -0
- package/dist/WebViewPaywall.js +360 -0
- package/dist/animations/presets.d.ts +49 -0
- package/dist/animations/presets.js +367 -0
- package/dist/components/AccoladeCpsComponent.d.ts +13 -0
- package/dist/components/AccoladeCpsComponent.js +248 -0
- package/dist/components/AccordionComponent.d.ts +10 -0
- package/dist/components/AccordionComponent.js +187 -0
- package/dist/components/AnimatedWrapper.d.ts +35 -0
- package/dist/components/AnimatedWrapper.js +277 -0
- package/dist/components/CarouselComponent.d.ts +23 -0
- package/dist/components/CarouselComponent.js +484 -0
- package/dist/components/ChartCurveComponent.d.ts +13 -0
- package/dist/components/ChartCurveComponent.js +333 -0
- package/dist/components/CheckboxComponent.d.ts +9 -0
- package/dist/components/CheckboxComponent.js +172 -0
- package/dist/components/ConfettiComponent.d.ts +9 -0
- package/dist/components/ConfettiComponent.js +172 -0
- package/dist/components/CountdownComponent.d.ts +16 -0
- package/dist/components/CountdownComponent.js +414 -0
- package/dist/components/DelayedXComponent.d.ts +12 -0
- package/dist/components/DelayedXComponent.js +240 -0
- package/dist/components/DrawerModalComponent.d.ts +20 -0
- package/dist/components/DrawerModalComponent.js +286 -0
- package/dist/components/GradientView.d.ts +38 -0
- package/dist/components/GradientView.js +126 -0
- package/dist/components/HapticFeedbackComponent.d.ts +16 -0
- package/dist/components/HapticFeedbackComponent.js +64 -0
- package/dist/components/HorizontalProductsComponent.d.ts +17 -0
- package/dist/components/HorizontalProductsComponent.js +284 -0
- package/dist/components/MultiChoiceGridComponent.d.ts +13 -0
- package/dist/components/MultiChoiceGridComponent.js +234 -0
- package/dist/components/NPSScaleComponent.d.ts +16 -0
- package/dist/components/NPSScaleComponent.js +260 -0
- package/dist/components/NavigationComponent.d.ts +26 -0
- package/dist/components/NavigationComponent.js +302 -0
- package/dist/components/OAuthButtonComponent.d.ts +20 -0
- package/dist/components/OAuthButtonComponent.js +237 -0
- package/dist/components/ProgressLoaderComponent.d.ts +13 -0
- package/dist/components/ProgressLoaderComponent.js +308 -0
- package/dist/components/ProgressStepperComponent.d.ts +18 -0
- package/dist/components/ProgressStepperComponent.js +228 -0
- package/dist/components/SegmentedPickerComponent.d.ts +10 -0
- package/dist/components/SegmentedPickerComponent.js +221 -0
- package/dist/components/ShareButtonsComponent.d.ts +21 -0
- package/dist/components/ShareButtonsComponent.js +265 -0
- package/dist/components/ShareCodeComponent.d.ts +16 -0
- package/dist/components/ShareCodeComponent.js +274 -0
- package/dist/components/SingleChoiceGridComponent.d.ts +13 -0
- package/dist/components/SingleChoiceGridComponent.js +210 -0
- package/dist/components/SliderComponent.d.ts +9 -0
- package/dist/components/SliderComponent.js +263 -0
- package/dist/components/StarRatingInputComponent.d.ts +20 -0
- package/dist/components/StarRatingInputComponent.js +198 -0
- package/dist/components/StickerCompositeComponent.d.ts +13 -0
- package/dist/components/StickerCompositeComponent.js +136 -0
- package/dist/components/TabsComponent.d.ts +19 -0
- package/dist/components/TabsComponent.js +431 -0
- package/dist/components/TextInputComponent.d.ts +18 -0
- package/dist/components/TextInputComponent.js +227 -0
- package/dist/components/TimelineComponent.d.ts +19 -0
- package/dist/components/TimelineComponent.js +104 -0
- package/dist/components/ToggleComponent.d.ts +14 -0
- package/dist/components/ToggleComponent.js +132 -0
- package/dist/components/VerticalProductsComponent.d.ts +17 -0
- package/dist/components/VerticalProductsComponent.js +311 -0
- package/dist/dynamicValues.d.ts +36 -0
- package/dist/dynamicValues.js +142 -0
- package/dist/filters/dateFilters.d.ts +50 -0
- package/dist/filters/dateFilters.js +390 -0
- package/dist/filters/index.d.ts +18 -0
- package/dist/filters/index.js +253 -0
- package/dist/hooks/useOfferings.d.ts +76 -0
- package/dist/hooks/useOfferings.js +99 -0
- package/dist/hooks/usePaywall.d.ts +27 -0
- package/dist/hooks/usePaywall.js +114 -0
- package/dist/hooks/usePlacement.d.ts +34 -0
- package/dist/hooks/usePlacement.js +89 -0
- package/dist/hooks/useSubscription.d.ts +40 -0
- package/dist/hooks/useSubscription.js +97 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +198 -0
- package/dist/models/BillDogError.d.ts +40 -0
- package/dist/models/BillDogError.js +11 -0
- package/dist/models/CacheFetchPolicy.d.ts +8 -0
- package/dist/models/CacheFetchPolicy.js +4 -0
- package/dist/models/CustomProduct.d.ts +32 -0
- package/dist/models/CustomProduct.js +15 -0
- package/dist/models/CustomerInfo.d.ts +17 -0
- package/dist/models/CustomerInfo.js +11 -0
- package/dist/models/Delegate.d.ts +20 -0
- package/dist/models/Delegate.js +11 -0
- package/dist/models/EntitlementInfo.d.ts +19 -0
- package/dist/models/EntitlementInfo.js +2 -0
- package/dist/models/Placement.d.ts +54 -0
- package/dist/models/Placement.js +15 -0
- package/dist/models/PurchaseParams.d.ts +39 -0
- package/dist/models/PurchaseParams.js +60 -0
- package/dist/models/PurchaseResult.d.ts +30 -0
- package/dist/models/PurchaseResult.js +15 -0
- package/dist/models/RestoreResult.d.ts +30 -0
- package/dist/models/RestoreResult.js +22 -0
- package/dist/models/RestoredItem.d.ts +26 -0
- package/dist/models/RestoredItem.js +13 -0
- package/dist/modules/ABTest.d.ts +65 -0
- package/dist/modules/ABTest.js +113 -0
- package/dist/modules/Analytics.d.ts +91 -0
- package/dist/modules/Analytics.js +182 -0
- package/dist/modules/Survey.d.ts +65 -0
- package/dist/modules/Survey.js +82 -0
- package/dist/modules/VirtualCurrency.d.ts +63 -0
- package/dist/modules/VirtualCurrency.js +147 -0
- package/dist/murmur.d.ts +23 -0
- package/dist/murmur.js +104 -0
- package/dist/notifications/LiveActivitiesManager.d.ts +47 -0
- package/dist/notifications/LiveActivitiesManager.js +106 -0
- package/dist/notifications/NotificationScheduler.d.ts +55 -0
- package/dist/notifications/NotificationScheduler.js +199 -0
- package/dist/notifications/PurchaseNotificationHandler.d.ts +24 -0
- package/dist/notifications/PurchaseNotificationHandler.js +55 -0
- package/dist/notifications/useDeviceToken.d.ts +36 -0
- package/dist/notifications/useDeviceToken.js +97 -0
- package/dist/notifications/useLiveActivity.d.ts +39 -0
- package/dist/notifications/useLiveActivity.js +104 -0
- package/dist/review/ReviewAnalytics.d.ts +34 -0
- package/dist/review/ReviewAnalytics.js +73 -0
- package/dist/review/ReviewManager.d.ts +83 -0
- package/dist/review/ReviewManager.js +339 -0
- package/dist/review/ReviewTimingManager.d.ts +82 -0
- package/dist/review/ReviewTimingManager.js +224 -0
- package/dist/survey-core/types.d.ts +215 -0
- package/dist/survey-core/types.js +8 -0
- package/dist/types.d.ts +438 -0
- package/dist/types.js +158 -0
- package/dist/utils/ApiClient.d.ts +65 -0
- package/dist/utils/ApiClient.js +132 -0
- package/dist/utils/CSSConverter.d.ts +49 -0
- package/dist/utils/CSSConverter.js +470 -0
- package/dist/utils/animations.d.ts +41 -0
- package/dist/utils/animations.js +194 -0
- package/dist/utils/calendarIntegration.d.ts +36 -0
- package/dist/utils/calendarIntegration.js +89 -0
- package/dist/utils/configurationResolver.d.ts +23 -0
- package/dist/utils/configurationResolver.js +78 -0
- package/dist/utils/deviceDetection.d.ts +38 -0
- package/dist/utils/deviceDetection.js +59 -0
- package/dist/utils/hapticFeedback.d.ts +17 -0
- package/dist/utils/hapticFeedback.js +53 -0
- package/dist/utils/iconMapper.d.ts +26 -0
- package/dist/utils/iconMapper.js +113 -0
- package/dist/utils/notificationPermissions.d.ts +13 -0
- package/dist/utils/notificationPermissions.js +51 -0
- package/dist/utils/priceFormatting.d.ts +19 -0
- package/dist/utils/priceFormatting.js +99 -0
- package/dist/utils/shareSheet.d.ts +17 -0
- package/dist/utils/shareSheet.js +42 -0
- package/dist/utils/subscriptionUpgrade.d.ts +84 -0
- package/dist/utils/subscriptionUpgrade.js +100 -0
- package/dist/valueUtils.d.ts +48 -0
- package/dist/valueUtils.js +129 -0
- package/dist/xFlowTags.d.ts +18 -0
- package/dist/xFlowTags.js +385 -0
- package/ios/BillDogABTest.m +50 -0
- package/ios/BillDogABTest.swift +188 -0
- package/ios/BillDogAnalytics.m +105 -0
- package/ios/BillDogAnalytics.swift +324 -0
- package/ios/BillDogBridge.m +222 -0
- package/ios/BillDogBridge.swift +715 -0
- package/ios/BillDogLiveActivities.m +41 -0
- package/ios/BillDogLiveActivities.swift +104 -0
- package/ios/BillDogPaywallViewManager.m +39 -0
- package/ios/BillDogPaywallViewManager.swift +361 -0
- package/ios/BillDogSurvey.m +34 -0
- package/ios/BillDogSurvey.swift +257 -0
- package/ios/BillDogVirtualCurrencyBridge.m +63 -0
- package/ios/BillDogVirtualCurrencyBridge.swift +511 -0
- package/junit.xml +25 -0
- package/package.json +61 -0
- package/react-native.config.js +13 -0
- package/src/AdvancedPaywallCache.ts +433 -0
- package/src/BillDog.ts +983 -0
- package/src/BillDogConfiguration.ts +318 -0
- package/src/ComponentRenderer.tsx +1325 -0
- package/src/LayoutEngine.ts +459 -0
- package/src/LocalizationResolver.ts +311 -0
- package/src/NativePaywallView.tsx +132 -0
- package/src/PaywallCache.ts +358 -0
- package/src/PaywallStateManager.ts +154 -0
- package/src/PaywallView.tsx +328 -0
- package/src/ProductVariableProvider.ts +282 -0
- package/src/VariableResolver.ts +516 -0
- package/src/WebViewPaywall.tsx +454 -0
- package/src/animations/presets.ts +421 -0
- package/src/components/AccoladeCpsComponent.tsx +259 -0
- package/src/components/AccordionComponent.tsx +213 -0
- package/src/components/AnimatedWrapper.tsx +316 -0
- package/src/components/CarouselComponent.tsx +615 -0
- package/src/components/ChartCurveComponent.tsx +452 -0
- package/src/components/CheckboxComponent.tsx +185 -0
- package/src/components/ConfettiComponent.tsx +178 -0
- package/src/components/CountdownComponent.tsx +454 -0
- package/src/components/DelayedXComponent.tsx +266 -0
- package/src/components/DrawerModalComponent.tsx +324 -0
- package/src/components/GradientView.tsx +129 -0
- package/src/components/HapticFeedbackComponent.tsx +39 -0
- package/src/components/HorizontalProductsComponent.tsx +329 -0
- package/src/components/MultiChoiceGridComponent.tsx +265 -0
- package/src/components/NPSScaleComponent.tsx +294 -0
- package/src/components/NavigationComponent.tsx +329 -0
- package/src/components/OAuthButtonComponent.tsx +266 -0
- package/src/components/ProgressLoaderComponent.tsx +366 -0
- package/src/components/ProgressStepperComponent.tsx +250 -0
- package/src/components/SegmentedPickerComponent.tsx +245 -0
- package/src/components/ShareButtonsComponent.tsx +299 -0
- package/src/components/ShareCodeComponent.tsx +303 -0
- package/src/components/SingleChoiceGridComponent.tsx +229 -0
- package/src/components/SliderComponent.tsx +280 -0
- package/src/components/StarRatingInputComponent.tsx +223 -0
- package/src/components/StickerCompositeComponent.tsx +168 -0
- package/src/components/TabsComponent.tsx +496 -0
- package/src/components/TextInputComponent.tsx +258 -0
- package/src/components/TimelineComponent.tsx +129 -0
- package/src/components/ToggleComponent.tsx +142 -0
- package/src/components/VerticalProductsComponent.tsx +357 -0
- package/src/dynamicValues.ts +180 -0
- package/src/filters/dateFilters.ts +411 -0
- package/src/filters/index.ts +281 -0
- package/src/hooks/useOfferings.ts +192 -0
- package/src/hooks/usePaywall.ts +154 -0
- package/src/hooks/usePlacement.ts +143 -0
- package/src/hooks/useSubscription.ts +154 -0
- package/src/index.ts +245 -0
- package/src/models/BillDogError.ts +24 -0
- package/src/models/CacheFetchPolicy.ts +13 -0
- package/src/models/CustomProduct.ts +33 -0
- package/src/models/CustomerInfo.ts +26 -0
- package/src/models/Delegate.ts +22 -0
- package/src/models/EntitlementInfo.ts +27 -0
- package/src/models/Placement.ts +50 -0
- package/src/models/PurchaseParams.ts +81 -0
- package/src/models/PurchaseResult.ts +32 -0
- package/src/models/RestoreResult.ts +27 -0
- package/src/models/RestoredItem.ts +25 -0
- package/src/modules/ABTest.ts +131 -0
- package/src/modules/Analytics.ts +227 -0
- package/src/modules/Survey.ts +126 -0
- package/src/modules/VirtualCurrency.ts +223 -0
- package/src/murmur.ts +102 -0
- package/src/notifications/LiveActivitiesManager.ts +140 -0
- package/src/notifications/NotificationScheduler.ts +292 -0
- package/src/notifications/PurchaseNotificationHandler.ts +67 -0
- package/src/notifications/useDeviceToken.ts +116 -0
- package/src/notifications/useLiveActivity.ts +125 -0
- package/src/review/ReviewAnalytics.ts +89 -0
- package/src/review/ReviewManager.ts +355 -0
- package/src/review/ReviewTimingManager.ts +265 -0
- package/src/survey-core/types.ts +244 -0
- package/src/types/ambient.d.ts +9 -0
- package/src/types.ts +680 -0
- package/src/utils/ApiClient.ts +163 -0
- package/src/utils/CSSConverter.ts +552 -0
- package/src/utils/animations.ts +232 -0
- package/src/utils/calendarIntegration.ts +113 -0
- package/src/utils/configurationResolver.ts +106 -0
- package/src/utils/deviceDetection.ts +83 -0
- package/src/utils/hapticFeedback.ts +55 -0
- package/src/utils/iconMapper.ts +102 -0
- package/src/utils/notificationPermissions.ts +48 -0
- package/src/utils/priceFormatting.ts +119 -0
- package/src/utils/shareSheet.ts +55 -0
- package/src/utils/subscriptionUpgrade.ts +183 -0
- package/src/valueUtils.ts +108 -0
- 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
|