@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,41 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BillDogLiveActivities.m
|
|
3
|
+
// @billdog.io/react-native
|
|
4
|
+
//
|
|
5
|
+
// RCT_EXTERN bridge declarations for the BillDogLiveActivities Swift module.
|
|
6
|
+
// iOS-only, no events. ⚠️ Requires `pod install` + Xcode (iOS 16.2+) to
|
|
7
|
+
// compile/verify — no iOS toolchain in the authoring environment.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
#import <React/RCTBridgeModule.h>
|
|
11
|
+
#import <React/RCTEventEmitter.h>
|
|
12
|
+
|
|
13
|
+
@interface RCT_EXTERN_MODULE(BillDogLiveActivities, RCTEventEmitter)
|
|
14
|
+
|
|
15
|
+
RCT_EXTERN_METHOD(areActivitiesEnabled:(RCTPromiseResolveBlock)resolve
|
|
16
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
17
|
+
|
|
18
|
+
RCT_EXTERN_METHOD(startActivity:(NSString *)activityType
|
|
19
|
+
attributes:(NSDictionary *)attributes
|
|
20
|
+
contentState:(NSDictionary *)contentState
|
|
21
|
+
staleDate:(NSString *)staleDate
|
|
22
|
+
dismissalDate:(NSString *)dismissalDate
|
|
23
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
24
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
25
|
+
|
|
26
|
+
RCT_EXTERN_METHOD(updateActivity:(NSString *)activityId
|
|
27
|
+
contentState:(NSDictionary *)contentState
|
|
28
|
+
staleDate:(NSString *)staleDate
|
|
29
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
30
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
31
|
+
|
|
32
|
+
RCT_EXTERN_METHOD(endActivity:(NSString *)activityId
|
|
33
|
+
finalState:(NSDictionary *)finalState
|
|
34
|
+
dismissalPolicy:(NSString *)dismissalPolicy
|
|
35
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
36
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
37
|
+
|
|
38
|
+
RCT_EXTERN_METHOD(getActiveActivities:(RCTPromiseResolveBlock)resolve
|
|
39
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
40
|
+
|
|
41
|
+
@end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BillDogLiveActivities.swift
|
|
3
|
+
// @billdog.io/react-native
|
|
4
|
+
//
|
|
5
|
+
// React Native bridge → iOS Live Activities (ActivityKit). iOS-only, no events.
|
|
6
|
+
//
|
|
7
|
+
// ⚠️ VERIFICATION: there is no iOS/CocoaPods toolchain in the authoring
|
|
8
|
+
// environment. This file REQUIRES a `pod install` + Xcode build (with an
|
|
9
|
+
// ActivityKit-capable deployment target, iOS 16.2+) to compile/verify.
|
|
10
|
+
//
|
|
11
|
+
// DESIGN LIMITATION (documented, intentional no-ops):
|
|
12
|
+
// The native `LiveActivityManager` (LiveActivityManager.swift:25) exposes
|
|
13
|
+
// start/update/end/getActiveActivities as GENERICS over a concrete
|
|
14
|
+
// `ActivityAttributes` type — e.g. startActivity<Attributes: ActivityAttributes>
|
|
15
|
+
// (LiveActivityManager.swift:53). A React Native bridge only ever receives
|
|
16
|
+
// untyped JS dictionaries, so there is NO concrete ActivityAttributes /
|
|
17
|
+
// ContentState struct to instantiate at this layer. Those four methods are
|
|
18
|
+
// therefore best-effort NO-OPS (NSLog + resolve null/[]), and MUST be driven
|
|
19
|
+
// from a host-app native module that owns the concrete Attributes types.
|
|
20
|
+
// `areActivitiesEnabled` IS wired for real via ActivityKit's
|
|
21
|
+
// ActivityAuthorizationInfo (the same check at LiveActivityManager.swift:60).
|
|
22
|
+
//
|
|
23
|
+
|
|
24
|
+
import Foundation
|
|
25
|
+
import React
|
|
26
|
+
|
|
27
|
+
#if canImport(ActivityKit)
|
|
28
|
+
import ActivityKit
|
|
29
|
+
#endif
|
|
30
|
+
|
|
31
|
+
@objc(BillDogLiveActivities)
|
|
32
|
+
class BillDogLiveActivities: RCTEventEmitter {
|
|
33
|
+
|
|
34
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// No events for this module.
|
|
39
|
+
override func supportedEvents() -> [String]! {
|
|
40
|
+
return []
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// MARK: - Authorization
|
|
44
|
+
|
|
45
|
+
@objc
|
|
46
|
+
func areActivitiesEnabled(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
47
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
48
|
+
#if canImport(ActivityKit)
|
|
49
|
+
if #available(iOS 16.2, *) {
|
|
50
|
+
// Mirrors LiveActivityManager.swift:60 — real authorization state.
|
|
51
|
+
resolve(ActivityAuthorizationInfo().areActivitiesEnabled)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
#endif
|
|
55
|
+
resolve(false)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// MARK: - Lifecycle (documented no-ops — see file header)
|
|
59
|
+
|
|
60
|
+
@objc
|
|
61
|
+
func startActivity(_ activityType: String,
|
|
62
|
+
attributes: NSDictionary,
|
|
63
|
+
contentState: NSDictionary,
|
|
64
|
+
staleDate: String?,
|
|
65
|
+
dismissalDate: String?,
|
|
66
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
67
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
68
|
+
NSLog("[BillDogLiveActivities] startActivity is a no-op in the RN bridge: "
|
|
69
|
+
+ "the native LiveActivityManager requires a concrete ActivityAttributes "
|
|
70
|
+
+ "type that cannot be built from an untyped JS payload. Drive Live "
|
|
71
|
+
+ "Activities from a host-app native module. (activityType=\(activityType))")
|
|
72
|
+
resolve(nil)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@objc
|
|
76
|
+
func updateActivity(_ activityId: String,
|
|
77
|
+
contentState: NSDictionary,
|
|
78
|
+
staleDate: String?,
|
|
79
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
80
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
81
|
+
NSLog("[BillDogLiveActivities] updateActivity is a no-op in the RN bridge "
|
|
82
|
+
+ "(generic ActivityAttributes unavailable). activityId=\(activityId)")
|
|
83
|
+
resolve(nil)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@objc
|
|
87
|
+
func endActivity(_ activityId: String,
|
|
88
|
+
finalState: NSDictionary?,
|
|
89
|
+
dismissalPolicy: String,
|
|
90
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
91
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
92
|
+
NSLog("[BillDogLiveActivities] endActivity is a no-op in the RN bridge "
|
|
93
|
+
+ "(generic ActivityAttributes unavailable). activityId=\(activityId)")
|
|
94
|
+
resolve(nil)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@objc
|
|
98
|
+
func getActiveActivities(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
99
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
100
|
+
NSLog("[BillDogLiveActivities] getActiveActivities is a no-op in the RN "
|
|
101
|
+
+ "bridge (generic ActivityAttributes unavailable). Returning [].")
|
|
102
|
+
resolve([])
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BillDogPaywallViewManager.m
|
|
3
|
+
// @billdog.io/react-native
|
|
4
|
+
//
|
|
5
|
+
// RCT_EXTERN bridge declarations for the BillDogPaywallViewManager Swift view
|
|
6
|
+
// manager. Exposes the inline (embedded) native paywall to React Native's
|
|
7
|
+
// legacy (Paper) architecture as `BDPaywallView` — JS resolves it via
|
|
8
|
+
// `requireNativeComponent('BDPaywallView')`.
|
|
9
|
+
//
|
|
10
|
+
// ⚠️ Requires `pod install` + Xcode to compile/verify — no iOS toolchain in
|
|
11
|
+
// the authoring environment. Written to be compile-ready and internally
|
|
12
|
+
// consistent, but MUST be built against the example app before shipping.
|
|
13
|
+
//
|
|
14
|
+
|
|
15
|
+
#import <React/RCTViewManager.h>
|
|
16
|
+
#import <React/RCTComponent.h>
|
|
17
|
+
|
|
18
|
+
// Remap the JS module name so RN's view-name derivation (strip trailing
|
|
19
|
+
// "Manager") yields exactly `BDPaywallView` — the name JS resolves via
|
|
20
|
+
// `requireNativeComponent('BDPaywallView')` — while the Swift class stays
|
|
21
|
+
// `BillDogPaywallViewManager` (see @objc(BillDogPaywallViewManager)).
|
|
22
|
+
// A plain RCT_EXTERN_MODULE(BillDogPaywallViewManager, …) would instead export
|
|
23
|
+
// the component as `BillDogPaywallView`, which JS does NOT look up.
|
|
24
|
+
@interface RCT_EXTERN_REMAP_MODULE(BDPaywallViewManager, BillDogPaywallViewManager, RCTViewManager)
|
|
25
|
+
|
|
26
|
+
// The placement / paywall identifier to load. Setting a non-empty value loads
|
|
27
|
+
// and embeds the paywall (see BillDogPaywallView.paywallIdentifier didSet).
|
|
28
|
+
RCT_EXPORT_VIEW_PROPERTY(paywallIdentifier, NSString)
|
|
29
|
+
|
|
30
|
+
// native → JS event callbacks (RCTBubblingEventBlock).
|
|
31
|
+
RCT_EXPORT_VIEW_PROPERTY(onLoaded, RCTBubblingEventBlock)
|
|
32
|
+
RCT_EXPORT_VIEW_PROPERTY(onError, RCTBubblingEventBlock)
|
|
33
|
+
RCT_EXPORT_VIEW_PROPERTY(onPurchase, RCTBubblingEventBlock)
|
|
34
|
+
RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTBubblingEventBlock)
|
|
35
|
+
RCT_EXPORT_VIEW_PROPERTY(onRestore, RCTBubblingEventBlock)
|
|
36
|
+
RCT_EXPORT_VIEW_PROPERTY(onOpenUrl, RCTBubblingEventBlock)
|
|
37
|
+
RCT_EXPORT_VIEW_PROPERTY(onTrackEvent, RCTBubblingEventBlock)
|
|
38
|
+
|
|
39
|
+
@end
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BillDogPaywallViewManager.swift
|
|
3
|
+
// @billdog.io/react-native
|
|
4
|
+
//
|
|
5
|
+
// Inline (embedded) native paywall UIView for React Native's legacy (Paper /
|
|
6
|
+
// NON-Fabric) architecture. Exposed to JS as `BDPaywallView` via the paired
|
|
7
|
+
// RCT_EXTERN_MODULE in BillDogPaywallViewManager.m; JS resolves it with
|
|
8
|
+
// `requireNativeComponent('BDPaywallView')`.
|
|
9
|
+
//
|
|
10
|
+
// NOTE: This file has NOT been compiled in this environment (no iOS toolchain /
|
|
11
|
+
// CocoaPods here). It is written to be compile-ready and internally consistent,
|
|
12
|
+
// but MUST be verified with `pod install` + an Xcode build of the example app
|
|
13
|
+
// before shipping.
|
|
14
|
+
//
|
|
15
|
+
// `import BillDog` exposes every public SDK symbol (CocoaPods compiles all
|
|
16
|
+
// BillDog subspecs into a single Clang module named `BillDog`).
|
|
17
|
+
//
|
|
18
|
+
// CRITICAL — purchase routing (ported from the Flutter PlatformView,
|
|
19
|
+
// packages/flutter/ios/Classes/BillDogPaywallViewFactory.swift):
|
|
20
|
+
// A raw `getPaywall(...)` VC does NOT auto-route taps into the SDK (unlike the
|
|
21
|
+
// auto-present path, which installs `InternalPaywallDelegate`). So this view
|
|
22
|
+
// sets itself as the VC delegate and MIRRORS `InternalPaywallDelegate` —
|
|
23
|
+
// executing `BillDog.shared.purchase(...)` / `restore()` on `.purchase` /
|
|
24
|
+
// `.restore`, and forwarding results back to JS as bubbling events.
|
|
25
|
+
// Reference: BillDogPaywall/UI/InternalPaywallDelegate.swift:15 (class),
|
|
26
|
+
// :24 (didReceiveAction routing).
|
|
27
|
+
//
|
|
28
|
+
|
|
29
|
+
import UIKit
|
|
30
|
+
import React
|
|
31
|
+
import BillDogFull
|
|
32
|
+
|
|
33
|
+
// MARK: - View Manager
|
|
34
|
+
|
|
35
|
+
@objc(BillDogPaywallViewManager)
|
|
36
|
+
class BillDogPaywallViewManager: RCTViewManager {
|
|
37
|
+
|
|
38
|
+
override func view() -> UIView! {
|
|
39
|
+
return BillDogPaywallView()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// MARK: - View
|
|
48
|
+
|
|
49
|
+
// Conforms to `PaywallViewControllerDelegate`
|
|
50
|
+
// (protocol: BillDogPaywall/UI/PaywallViewController.swift:1182).
|
|
51
|
+
class BillDogPaywallView: UIView, PaywallViewControllerDelegate {
|
|
52
|
+
|
|
53
|
+
// MARK: Exported props (native → JS callbacks; RCTBubblingEventBlock)
|
|
54
|
+
|
|
55
|
+
@objc var onLoaded: RCTBubblingEventBlock?
|
|
56
|
+
@objc var onError: RCTBubblingEventBlock?
|
|
57
|
+
@objc var onPurchase: RCTBubblingEventBlock?
|
|
58
|
+
@objc var onDismiss: RCTBubblingEventBlock?
|
|
59
|
+
@objc var onRestore: RCTBubblingEventBlock?
|
|
60
|
+
@objc var onOpenUrl: RCTBubblingEventBlock?
|
|
61
|
+
@objc var onTrackEvent: RCTBubblingEventBlock?
|
|
62
|
+
|
|
63
|
+
/// The placement / paywall identifier. Loading is triggered the first time a
|
|
64
|
+
/// non-empty value is assigned (RN sets props after construction).
|
|
65
|
+
@objc var paywallIdentifier: String? {
|
|
66
|
+
didSet {
|
|
67
|
+
guard let id = paywallIdentifier, !id.isEmpty else { return }
|
|
68
|
+
guard !didStartLoad else { return }
|
|
69
|
+
didStartLoad = true
|
|
70
|
+
loadPaywall(identifier: id)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// MARK: State
|
|
75
|
+
|
|
76
|
+
/// The embedded paywall VC (retained so it — and its delegate wiring —
|
|
77
|
+
/// survives). PaywallViewController: BillDogPaywall/UI/PaywallViewController.swift:16.
|
|
78
|
+
private var paywallViewController: PaywallViewController?
|
|
79
|
+
|
|
80
|
+
/// Guards a double-load (RN can re-assign the same prop).
|
|
81
|
+
private var didStartLoad = false
|
|
82
|
+
|
|
83
|
+
/// Guards against emitting `onDismiss` twice (a `.close` action AND a
|
|
84
|
+
/// `didFinishWith(.dismissed)` lifecycle callback can both arrive).
|
|
85
|
+
private var didEmitDismiss = false
|
|
86
|
+
|
|
87
|
+
// MARK: - Loading / embedding
|
|
88
|
+
|
|
89
|
+
private func loadPaywall(identifier: String) {
|
|
90
|
+
Task { [weak self] in
|
|
91
|
+
guard let self = self else { return }
|
|
92
|
+
do {
|
|
93
|
+
// getPaywall(forPlacement:) → PaywallViewController.
|
|
94
|
+
// BillDog.swift:3243.
|
|
95
|
+
let vc = try await BillDog.shared.getPaywall(forPlacement: identifier)
|
|
96
|
+
await MainActor.run { [weak self] in
|
|
97
|
+
self?.embed(vc)
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
self.send(self.onError, ["message": error.localizedDescription])
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@MainActor
|
|
106
|
+
private func embed(_ vc: PaywallViewController) {
|
|
107
|
+
self.paywallViewController = vc
|
|
108
|
+
|
|
109
|
+
// Route paywall taps/lifecycle back through this object so we can execute
|
|
110
|
+
// purchases and forward events to JS (mirrors InternalPaywallDelegate).
|
|
111
|
+
vc.delegate = self
|
|
112
|
+
|
|
113
|
+
// Add the VC's view with edge-pinned constraints.
|
|
114
|
+
let child = vc.view!
|
|
115
|
+
child.translatesAutoresizingMaskIntoConstraints = false
|
|
116
|
+
addSubview(child)
|
|
117
|
+
NSLayoutConstraint.activate([
|
|
118
|
+
child.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
119
|
+
child.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
120
|
+
child.topAnchor.constraint(equalTo: topAnchor),
|
|
121
|
+
child.bottomAnchor.constraint(equalTo: bottomAnchor)
|
|
122
|
+
])
|
|
123
|
+
|
|
124
|
+
// Establish a proper view-controller containment relationship when a host
|
|
125
|
+
// VC is reachable (so appearance/lifecycle + `present(...)` from the
|
|
126
|
+
// paywall work). Prefer the RN-provided ancestor VC; fall back to the
|
|
127
|
+
// front-most presented VC of the key window.
|
|
128
|
+
if let parent = reactViewController() ?? Self.nearestHostViewController() {
|
|
129
|
+
parent.addChild(vc)
|
|
130
|
+
vc.didMove(toParent: parent)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
send(onLoaded, nil)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Best-effort walk to the front-most presented VC of the key window, used as
|
|
137
|
+
/// a fallback containment parent for the embedded paywall.
|
|
138
|
+
private static func nearestHostViewController() -> UIViewController? {
|
|
139
|
+
let window: UIWindow?
|
|
140
|
+
if #available(iOS 13.0, *) {
|
|
141
|
+
window = UIApplication.shared.connectedScenes
|
|
142
|
+
.compactMap { $0 as? UIWindowScene }
|
|
143
|
+
.flatMap { $0.windows }
|
|
144
|
+
.first(where: { $0.isKeyWindow }) ?? UIApplication.shared.windows.first(where: { $0.isKeyWindow })
|
|
145
|
+
} else {
|
|
146
|
+
window = UIApplication.shared.keyWindow
|
|
147
|
+
}
|
|
148
|
+
var top = window?.rootViewController
|
|
149
|
+
while let presented = top?.presentedViewController {
|
|
150
|
+
top = presented
|
|
151
|
+
}
|
|
152
|
+
return top
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// MARK: - PaywallViewControllerDelegate
|
|
156
|
+
|
|
157
|
+
// MIRROR of BillDogPaywall/UI/InternalPaywallDelegate.swift:24 — the SDK's
|
|
158
|
+
// default routing of PaywallAction → BillDog.shared. `value` carries the
|
|
159
|
+
// action payload (productId for `.purchase`, url for `.openUrl`, etc.).
|
|
160
|
+
// PaywallAction enum + cases: PaywallViewController.swift:1197
|
|
161
|
+
// .purchase :1198, .restore :1201, .close :1202, .openUrl :1203.
|
|
162
|
+
func paywallViewController(
|
|
163
|
+
_ controller: PaywallViewController,
|
|
164
|
+
didReceiveAction action: PaywallAction,
|
|
165
|
+
value: String
|
|
166
|
+
) {
|
|
167
|
+
switch action {
|
|
168
|
+
case .purchase:
|
|
169
|
+
// InternalPaywallDelegate.swift:26-39 → BillDog.shared.purchase(productId:)
|
|
170
|
+
// purchase(productId:) : BillDog.swift:1375 (→ PurchaseResult, PurchaseResult.swift:24)
|
|
171
|
+
let productId = value
|
|
172
|
+
Task { [weak self] in
|
|
173
|
+
guard let self = self else { return }
|
|
174
|
+
do {
|
|
175
|
+
let result = try await BillDog.shared.purchase(productId: productId)
|
|
176
|
+
self.send(self.onPurchase, [
|
|
177
|
+
"productId": Self.resolvedProductId(from: result, fallback: productId),
|
|
178
|
+
"data": Self.purchaseData(from: result) as Any
|
|
179
|
+
])
|
|
180
|
+
} catch {
|
|
181
|
+
self.send(self.onError, ["message": error.localizedDescription])
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
case .restore:
|
|
186
|
+
// InternalPaywallDelegate.swift:41-49 → BillDog.shared.restore()
|
|
187
|
+
// restore() : BillDog.swift:1544 (→ PurchaseResult)
|
|
188
|
+
Task { [weak self] in
|
|
189
|
+
guard let self = self else { return }
|
|
190
|
+
do {
|
|
191
|
+
_ = try await BillDog.shared.restore()
|
|
192
|
+
self.send(self.onRestore, nil)
|
|
193
|
+
} catch {
|
|
194
|
+
self.send(self.onError, ["message": error.localizedDescription])
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
case .close:
|
|
199
|
+
// InternalPaywallDelegate.swift:51-52 dismisses the VC. Here the VC is
|
|
200
|
+
// embedded (not modally presented), so we just notify JS to tear down
|
|
201
|
+
// the view; JS owns the container lifecycle.
|
|
202
|
+
emitDismiss()
|
|
203
|
+
|
|
204
|
+
case .openUrl:
|
|
205
|
+
// InternalPaywallDelegate.swift:54-55 opens SFSafari in-app. For the
|
|
206
|
+
// embedded case we hand the URL to JS so the host app decides.
|
|
207
|
+
send(onOpenUrl, ["url": value])
|
|
208
|
+
|
|
209
|
+
case .webCheckout:
|
|
210
|
+
send(onTrackEvent, ["event": "web_checkout", "data": ["value": value]])
|
|
211
|
+
|
|
212
|
+
case .customCheckout:
|
|
213
|
+
send(onTrackEvent, ["event": "custom_checkout", "data": ["value": value]])
|
|
214
|
+
|
|
215
|
+
case .oauth(let provider):
|
|
216
|
+
// OAuth is fully handled inside PaywallViewController.handleOAuthAction
|
|
217
|
+
// before reaching the delegate (InternalPaywallDelegate.swift:76-79) —
|
|
218
|
+
// observation only.
|
|
219
|
+
send(onTrackEvent, ["event": "oauth", "data": ["provider": provider, "value": value]])
|
|
220
|
+
|
|
221
|
+
case .custom(let name):
|
|
222
|
+
send(onTrackEvent, ["event": "custom", "data": ["name": name, "value": value]])
|
|
223
|
+
|
|
224
|
+
case .openTerms:
|
|
225
|
+
send(onTrackEvent, ["event": "open_terms", "data": ["value": value]])
|
|
226
|
+
|
|
227
|
+
case .openPrivacy:
|
|
228
|
+
send(onTrackEvent, ["event": "open_privacy", "data": ["value": value]])
|
|
229
|
+
|
|
230
|
+
case .manageSubscription:
|
|
231
|
+
send(onTrackEvent, ["event": "manage_subscription", "data": ["value": value]])
|
|
232
|
+
|
|
233
|
+
case .requestRefund(let productIdentifier):
|
|
234
|
+
send(onTrackEvent, ["event": "request_refund", "data": ["productIdentifier": productIdentifier as Any, "value": value]])
|
|
235
|
+
|
|
236
|
+
case .upgradeSubscription(let currentProductIdentifier, let newProductIdentifier):
|
|
237
|
+
send(onTrackEvent, ["event": "upgrade_subscription", "data": [
|
|
238
|
+
"currentProductIdentifier": currentProductIdentifier as Any,
|
|
239
|
+
"newProductIdentifier": newProductIdentifier,
|
|
240
|
+
"value": value
|
|
241
|
+
]])
|
|
242
|
+
|
|
243
|
+
case .downgradeSubscription(let currentProductIdentifier, let newProductIdentifier):
|
|
244
|
+
send(onTrackEvent, ["event": "downgrade_subscription", "data": [
|
|
245
|
+
"currentProductIdentifier": currentProductIdentifier as Any,
|
|
246
|
+
"newProductIdentifier": newProductIdentifier,
|
|
247
|
+
"value": value
|
|
248
|
+
]])
|
|
249
|
+
|
|
250
|
+
case .executeAction(let actionType, let componentId, let idempotencyKey):
|
|
251
|
+
send(onTrackEvent, ["event": "execute_action", "data": [
|
|
252
|
+
"actionType": actionType,
|
|
253
|
+
"componentId": componentId,
|
|
254
|
+
"idempotencyKey": idempotencyKey as Any,
|
|
255
|
+
"value": value
|
|
256
|
+
]])
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Lifecycle result (PaywallViewController.PaywallDismissResult:
|
|
261
|
+
// PaywallViewController.swift:118). Maps the terminal outcome to the matching
|
|
262
|
+
// JS event. `.purchased`/`.restored` are primarily driven by the action
|
|
263
|
+
// handler above (which carries richer SDK data); this path is the
|
|
264
|
+
// authoritative dismiss signal and a safety net.
|
|
265
|
+
func paywallViewController(
|
|
266
|
+
_ controller: PaywallViewController,
|
|
267
|
+
didFinishWith result: PaywallViewController.PaywallDismissResult,
|
|
268
|
+
shouldDismiss: Bool
|
|
269
|
+
) {
|
|
270
|
+
switch result {
|
|
271
|
+
case .purchased(let productId):
|
|
272
|
+
// PaywallDismissResult.purchased(productId:) : PaywallViewController.swift:119
|
|
273
|
+
send(onPurchase, ["productId": productId, "data": NSNull()])
|
|
274
|
+
case .restored:
|
|
275
|
+
// PaywallDismissResult.restored : PaywallViewController.swift:120
|
|
276
|
+
send(onRestore, nil)
|
|
277
|
+
case .dismissed:
|
|
278
|
+
// PaywallDismissResult.dismissed : PaywallViewController.swift:121
|
|
279
|
+
emitDismiss()
|
|
280
|
+
case .oauthSuccess(let provider, let userId, let email, let name, let idToken):
|
|
281
|
+
// PaywallDismissResult.oauthSuccess : PaywallViewController.swift:122
|
|
282
|
+
send(onTrackEvent, ["event": "oauth_success", "data": [
|
|
283
|
+
"provider": provider,
|
|
284
|
+
"userId": userId as Any,
|
|
285
|
+
"email": email as Any,
|
|
286
|
+
"name": name as Any,
|
|
287
|
+
"idToken": idToken as Any
|
|
288
|
+
]])
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if shouldDismiss {
|
|
292
|
+
emitDismiss()
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Loading-state callback (PaywallViewController.PaywallLoadingState:
|
|
297
|
+
// PaywallViewController.swift:83). Observation only — `onLoaded` is emitted
|
|
298
|
+
// from `embed(_:)`.
|
|
299
|
+
func paywallViewController(
|
|
300
|
+
_ controller: PaywallViewController,
|
|
301
|
+
loadingStateDidChange state: PaywallViewController.PaywallLoadingState
|
|
302
|
+
) { }
|
|
303
|
+
|
|
304
|
+
// MARK: - Helpers
|
|
305
|
+
|
|
306
|
+
private func emitDismiss() {
|
|
307
|
+
guard !didEmitDismiss else { return }
|
|
308
|
+
didEmitDismiss = true
|
|
309
|
+
send(onDismiss, nil)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/// Fire an RCTBubblingEventBlock on the main thread with a `[String: Any]`
|
|
313
|
+
/// payload. RN block calls must occur on the main thread.
|
|
314
|
+
private func send(_ block: RCTBubblingEventBlock?, _ payload: [String: Any]?) {
|
|
315
|
+
guard let block = block else { return }
|
|
316
|
+
let body = payload ?? [:]
|
|
317
|
+
if Thread.isMainThread {
|
|
318
|
+
block(body)
|
|
319
|
+
} else {
|
|
320
|
+
DispatchQueue.main.async {
|
|
321
|
+
block(body)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/// Resolve the productId from a PurchaseResult (PurchaseResult.swift:24),
|
|
327
|
+
/// falling back to the tapped product on non-success outcomes.
|
|
328
|
+
private static func resolvedProductId(from result: PurchaseResult, fallback: String) -> String {
|
|
329
|
+
switch result {
|
|
330
|
+
case .purchased(_, let productId, _): return productId
|
|
331
|
+
case .restored(_, let productId, _): return productId
|
|
332
|
+
default: return fallback
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/// Flatten a PurchaseResult into a `[String: Any]?` payload for JS.
|
|
337
|
+
private static func purchaseData(from result: PurchaseResult) -> [String: Any]? {
|
|
338
|
+
switch result {
|
|
339
|
+
case .purchased(let transactionId, let productId, let originalTransactionId):
|
|
340
|
+
return [
|
|
341
|
+
"status": "purchased",
|
|
342
|
+
"transactionId": transactionId,
|
|
343
|
+
"productId": productId,
|
|
344
|
+
"originalTransactionId": originalTransactionId as Any
|
|
345
|
+
]
|
|
346
|
+
case .restored(let transactionId, let productId, let originalTransactionId):
|
|
347
|
+
return [
|
|
348
|
+
"status": "restored",
|
|
349
|
+
"transactionId": transactionId,
|
|
350
|
+
"productId": productId,
|
|
351
|
+
"originalTransactionId": originalTransactionId as Any
|
|
352
|
+
]
|
|
353
|
+
case .cancelled:
|
|
354
|
+
return ["status": "cancelled"]
|
|
355
|
+
case .pending:
|
|
356
|
+
return ["status": "pending"]
|
|
357
|
+
case .failed(let error):
|
|
358
|
+
return ["status": "failed", "error": error.localizedDescription]
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BillDogSurvey.m
|
|
3
|
+
// @billdog.io/react-native
|
|
4
|
+
//
|
|
5
|
+
// RCT_EXTERN bridge declarations for the BillDogSurvey Swift module.
|
|
6
|
+
// ⚠️ Requires `pod install` + Xcode to compile/verify — no iOS toolchain in
|
|
7
|
+
// the authoring environment.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
#import <React/RCTBridgeModule.h>
|
|
11
|
+
#import <React/RCTEventEmitter.h>
|
|
12
|
+
|
|
13
|
+
@interface RCT_EXTERN_MODULE(BillDogSurvey, RCTEventEmitter)
|
|
14
|
+
|
|
15
|
+
RCT_EXTERN_METHOD(initialize:(RCTPromiseResolveBlock)resolve
|
|
16
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
17
|
+
|
|
18
|
+
RCT_EXTERN_METHOD(presentSurvey:(NSDictionary *)options
|
|
19
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
20
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
21
|
+
|
|
22
|
+
RCT_EXTERN_METHOD(fetchSurvey:(NSString *)surveyIdentifier
|
|
23
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
24
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
25
|
+
|
|
26
|
+
RCT_EXTERN_METHOD(submitResponse:(NSDictionary *)response
|
|
27
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
28
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
29
|
+
|
|
30
|
+
RCT_EXTERN_METHOD(canShowSurvey:(NSString *)surveyIdentifier
|
|
31
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
32
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
33
|
+
|
|
34
|
+
@end
|