@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,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side variable resolver for React Native
|
|
3
|
+
* Resolves ${variable} and {{variable}} placeholders at runtime
|
|
4
|
+
* Supports localization integration for localized variables
|
|
5
|
+
*/
|
|
6
|
+
import { LocalizationResolver } from './LocalizationResolver';
|
|
7
|
+
import { PaywallConfiguration, PaywallComponent } from './types';
|
|
8
|
+
import { PackageInput, TrialEligibilityInput } from './ProductVariableProvider';
|
|
9
|
+
export interface DeviceInfo {
|
|
10
|
+
osName?: string;
|
|
11
|
+
osVersion?: string;
|
|
12
|
+
platform?: string;
|
|
13
|
+
deviceModel?: string;
|
|
14
|
+
screenWidth?: number;
|
|
15
|
+
screenHeight?: number;
|
|
16
|
+
locale?: string;
|
|
17
|
+
timezone?: string;
|
|
18
|
+
interfaceStyle?: 'light' | 'dark' | 'unspecified';
|
|
19
|
+
batteryLevel?: number;
|
|
20
|
+
isLowPowerMode?: boolean;
|
|
21
|
+
networkType?: string;
|
|
22
|
+
carrierName?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare class VariableResolver {
|
|
25
|
+
private allVariables;
|
|
26
|
+
private localizationResolver?;
|
|
27
|
+
private variableLocalizations?;
|
|
28
|
+
private currentLocale?;
|
|
29
|
+
constructor(configVariables?: Record<string, string> | null, runtimeVariables?: Record<string, any>, localizationResolver?: LocalizationResolver, variableLocalizations?: Record<string, Record<string, string>>, currentLocale?: string);
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a single string with variables
|
|
32
|
+
*/
|
|
33
|
+
resolve(text?: string | null): string;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a variable path to its raw (un-stringified) value.
|
|
36
|
+
* Flat-key-first (grammar §4): a literal key matching the whole path wins.
|
|
37
|
+
* Returns undefined when unresolved.
|
|
38
|
+
*/
|
|
39
|
+
private resolveRawValue;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve nested variable paths like "offering.monthly"
|
|
42
|
+
*/
|
|
43
|
+
private resolveVariablePath;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve all text properties in a component
|
|
46
|
+
* Applies localization for LOCALIZABLE_PROPERTIES (matching Android/iOS parity)
|
|
47
|
+
*/
|
|
48
|
+
resolveComponent(component: PaywallComponent): PaywallComponent;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve all components in a configuration
|
|
51
|
+
*/
|
|
52
|
+
resolveConfiguration(config: PaywallConfiguration): PaywallConfiguration;
|
|
53
|
+
/**
|
|
54
|
+
* Build device variable map from DeviceInfo — single source of truth.
|
|
55
|
+
* Used by withDeviceInfo(), withFullContext(), withProducts().
|
|
56
|
+
* Maps RN DeviceInfo field names to canonical SDK key names.
|
|
57
|
+
*/
|
|
58
|
+
static buildDeviceVariables(deviceInfo: DeviceInfo): Record<string, any>;
|
|
59
|
+
/**
|
|
60
|
+
* Create resolver with offering variables injected
|
|
61
|
+
*/
|
|
62
|
+
static withOfferings(config: PaywallConfiguration, offerings: Record<string, string>): VariableResolver;
|
|
63
|
+
/**
|
|
64
|
+
* Create resolver with device info variables
|
|
65
|
+
* Maps device info to namespaced variables (device.*)
|
|
66
|
+
*/
|
|
67
|
+
static withDeviceInfo(config: PaywallConfiguration, deviceInfo: DeviceInfo): VariableResolver;
|
|
68
|
+
/**
|
|
69
|
+
* Create resolver with localization support
|
|
70
|
+
*/
|
|
71
|
+
static withLocalization(config: PaywallConfiguration, locale?: string): VariableResolver;
|
|
72
|
+
/**
|
|
73
|
+
* Create resolver with full context (offerings, device info, localization)
|
|
74
|
+
*/
|
|
75
|
+
static withFullContext(config: PaywallConfiguration, offerings: Record<string, string>, deviceInfo: DeviceInfo): VariableResolver;
|
|
76
|
+
/**
|
|
77
|
+
* Create resolver with user eligibility variables
|
|
78
|
+
* Enables {{user.isEligibleForIntro}}, {{user.hasUsedTrial}}, etc.
|
|
79
|
+
*/
|
|
80
|
+
static withEligibility(config: PaywallConfiguration, isEligibleForIntro?: boolean, hasUsedTrial?: boolean, isEligibleForPromo?: boolean): VariableResolver;
|
|
81
|
+
/**
|
|
82
|
+
* Create resolver with product/package variables
|
|
83
|
+
* Enables {{product.price}}, {{selectedProduct.*}}, {{products.N.*}},
|
|
84
|
+
* {{primary.*}}, {{monthly.*}}, etc. (40+ variable keys per package)
|
|
85
|
+
*
|
|
86
|
+
* Delegates to ProductVariableProvider for full parity with Android.
|
|
87
|
+
*/
|
|
88
|
+
static withProducts(config: PaywallConfiguration, packages: PackageInput[], selectedIndex?: number, trialEligibility?: Record<string, TrialEligibilityInput>, deviceInfo?: DeviceInfo): VariableResolver;
|
|
89
|
+
/**
|
|
90
|
+
* Create resolver with an Offering's packages
|
|
91
|
+
* Convenience wrapper around withProducts
|
|
92
|
+
*/
|
|
93
|
+
static withOffering(config: PaywallConfiguration, offering: {
|
|
94
|
+
packages: PackageInput[];
|
|
95
|
+
}, selectedIndex?: number, trialEligibility?: Record<string, TrialEligibilityInput>, deviceInfo?: DeviceInfo): VariableResolver;
|
|
96
|
+
/**
|
|
97
|
+
* Build the FULL render-time variable bag (device + product/offer/user) — the
|
|
98
|
+
* React-Native analog of iOS/Android `VariableResolver.withOffering(...).variables`.
|
|
99
|
+
* Fed to `processFullComponent` so Dynamic Values conditions on
|
|
100
|
+
* `products.*` / `selectedProduct.*` / `offer.*` / `user.*` actually resolve on
|
|
101
|
+
* RN (previously the render bag was device + `config.variables` only, so every
|
|
102
|
+
* product/user DV condition silently rendered `default`).
|
|
103
|
+
*
|
|
104
|
+
* `selectedIndex` is 0 for now — parity with the native Phase-2 render pass,
|
|
105
|
+
* which is not yet selection-reactive (Phase 2b). `appUserId`/`customerInfo` are
|
|
106
|
+
* passed in by the caller (`BillDog.getAppUserId()` + `customerInfoEmitter.latest`,
|
|
107
|
+
* both sync) to avoid a VariableResolver→BillDog import cycle.
|
|
108
|
+
*/
|
|
109
|
+
static buildRenderVariables(config: PaywallConfiguration, deviceInfo: DeviceInfo, opts?: {
|
|
110
|
+
selectedIndex?: number;
|
|
111
|
+
appUserId?: string | null;
|
|
112
|
+
customerInfo?: {
|
|
113
|
+
entitlements?: Array<{
|
|
114
|
+
identifier: string;
|
|
115
|
+
isActive: boolean;
|
|
116
|
+
}>;
|
|
117
|
+
activeSubscriptions?: string[];
|
|
118
|
+
} | null;
|
|
119
|
+
}): Record<string, any>;
|
|
120
|
+
}
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Client-side variable resolver for React Native
|
|
4
|
+
* Resolves ${variable} and {{variable}} placeholders at runtime
|
|
5
|
+
* Supports localization integration for localized variables
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.VariableResolver = void 0;
|
|
9
|
+
const LocalizationResolver_1 = require("./LocalizationResolver");
|
|
10
|
+
const xFlowTags_1 = require("./xFlowTags");
|
|
11
|
+
const filters_1 = require("./filters");
|
|
12
|
+
const ProductVariableProvider_1 = require("./ProductVariableProvider");
|
|
13
|
+
const murmur_1 = require("./murmur");
|
|
14
|
+
/**
|
|
15
|
+
* Get device locale with safe fallback for React Native
|
|
16
|
+
*/
|
|
17
|
+
function deviceLocale() {
|
|
18
|
+
try {
|
|
19
|
+
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return 'en';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Properties eligible for localization (matches Android/iOS LOCALIZABLE_PROPERTIES) */
|
|
26
|
+
const LOCALIZABLE_PROPERTIES = new Set([
|
|
27
|
+
'text', 'title', 'subtitle', 'label', 'placeholder',
|
|
28
|
+
'buttonText', 'ctaText', 'description', 'content',
|
|
29
|
+
'headerText', 'footerText', 'disclaimer',
|
|
30
|
+
]);
|
|
31
|
+
class VariableResolver {
|
|
32
|
+
constructor(configVariables, runtimeVariables = {}, localizationResolver, variableLocalizations, currentLocale) {
|
|
33
|
+
this.allVariables = {
|
|
34
|
+
...configVariables,
|
|
35
|
+
...runtimeVariables,
|
|
36
|
+
};
|
|
37
|
+
this.localizationResolver = localizationResolver;
|
|
38
|
+
this.variableLocalizations = variableLocalizations;
|
|
39
|
+
this.currentLocale = currentLocale;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a single string with variables
|
|
43
|
+
*/
|
|
44
|
+
resolve(text) {
|
|
45
|
+
if (!text)
|
|
46
|
+
return text !== null && text !== void 0 ? text : '';
|
|
47
|
+
let result = text;
|
|
48
|
+
// Local copy — xFlow assigns must not leak across resolve() calls
|
|
49
|
+
let resolveVars = { ...this.allVariables };
|
|
50
|
+
// Process xFlow {% %} tags before {{ }} interpolation
|
|
51
|
+
if (result.includes('{%')) {
|
|
52
|
+
const xFlowResult = (0, xFlowTags_1.processXFlowTags)(result, resolveVars);
|
|
53
|
+
result = xFlowResult.text;
|
|
54
|
+
// Merge assigned variables into local context only
|
|
55
|
+
resolveVars = { ...resolveVars, ...xFlowResult.variables };
|
|
56
|
+
}
|
|
57
|
+
// Resolve ${variable} syntax
|
|
58
|
+
result = result.replace(/\$\{([\w.]+)\}/g, (_, varName) => {
|
|
59
|
+
return this.resolveVariablePath(varName, resolveVars);
|
|
60
|
+
});
|
|
61
|
+
// Resolve {{ variable }} and {{ variable | filter: arg }} syntax
|
|
62
|
+
result = result.replace(/\{\{\s*([\w.]+)(?:\s*\|\s*(.+?))?\s*\}\}/g, (match, varName, filterChain) => {
|
|
63
|
+
const name = varName.trim();
|
|
64
|
+
if (!filterChain) {
|
|
65
|
+
return this.resolveVariablePath(name, resolveVars);
|
|
66
|
+
}
|
|
67
|
+
// Filter chains operate on the raw value, bypassing localization.
|
|
68
|
+
const raw = this.resolveRawValue(name, resolveVars);
|
|
69
|
+
if (raw === undefined)
|
|
70
|
+
return match;
|
|
71
|
+
let value = raw;
|
|
72
|
+
try {
|
|
73
|
+
value = (0, filters_1.applyFilterChain)(raw, filterChain.trim());
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.warn('[VariableResolver] Filter chain failed:', error);
|
|
77
|
+
}
|
|
78
|
+
return value !== undefined && value !== null ? String(value) : match;
|
|
79
|
+
});
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Resolve a variable path to its raw (un-stringified) value.
|
|
84
|
+
* Flat-key-first (grammar §4): a literal key matching the whole path wins.
|
|
85
|
+
* Returns undefined when unresolved.
|
|
86
|
+
*/
|
|
87
|
+
resolveRawValue(path, from) {
|
|
88
|
+
if (path in from)
|
|
89
|
+
return from[path];
|
|
90
|
+
const parts = path.split('.');
|
|
91
|
+
let current = from[parts[0]];
|
|
92
|
+
for (let i = 1; i < parts.length; i++) {
|
|
93
|
+
if (current && typeof current === 'object') {
|
|
94
|
+
current = current[parts[i]];
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return current;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Resolve nested variable paths like "offering.monthly"
|
|
104
|
+
*/
|
|
105
|
+
resolveVariablePath(path, from) {
|
|
106
|
+
var _a, _b;
|
|
107
|
+
const vars = from !== null && from !== void 0 ? from : this.allVariables;
|
|
108
|
+
// Priority 1: Check variableLocalizations[path][locale]
|
|
109
|
+
if (this.variableLocalizations && this.currentLocale) {
|
|
110
|
+
const localeMap = this.variableLocalizations[path];
|
|
111
|
+
if (localeMap) {
|
|
112
|
+
const localizedValue = (_b = (_a = localeMap[this.currentLocale]) !== null && _a !== void 0 ? _a : localeMap[this.currentLocale.split('-')[0]]) !== null && _b !== void 0 ? _b : localeMap[this.currentLocale.split('_')[0]];
|
|
113
|
+
if (localizedValue !== undefined) {
|
|
114
|
+
return localizedValue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Priority 2: Direct lookup
|
|
119
|
+
if (path in vars) {
|
|
120
|
+
return String(vars[path]);
|
|
121
|
+
}
|
|
122
|
+
// Priority 3: Try nested path resolution
|
|
123
|
+
const parts = path.split('.');
|
|
124
|
+
let current = undefined;
|
|
125
|
+
for (const part of parts) {
|
|
126
|
+
if (current === undefined) {
|
|
127
|
+
current = vars[part];
|
|
128
|
+
}
|
|
129
|
+
else if (typeof current === 'object' && current !== null) {
|
|
130
|
+
current = current[part];
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
current = undefined;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (current !== undefined) {
|
|
138
|
+
return String(current);
|
|
139
|
+
}
|
|
140
|
+
// Return empty string for unresolved variables to match the unified parity
|
|
141
|
+
// contract: iOS VariableResolver.swift:177 and Android
|
|
142
|
+
// VariableResolver.kt:147 both substitute "". A `${path}` placeholder is
|
|
143
|
+
// editor-mode behavior and must not leak into runtime — an unresolved
|
|
144
|
+
// variable would otherwise render as literal `${user.name}` on-device.
|
|
145
|
+
//
|
|
146
|
+
// This fix existed in the legacy packages/react-native resolver and was
|
|
147
|
+
// lost when the thin bridge replaced it (ba6a68d86); the conformance
|
|
148
|
+
// corpus caught the regression. Warn rather than log so unresolved
|
|
149
|
+
// variables stay visible in production, matching both native SDKs.
|
|
150
|
+
console.warn(`[VariableResolver] Variable not resolved: ${path} — substituting empty string`);
|
|
151
|
+
return '';
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Resolve all text properties in a component
|
|
155
|
+
* Applies localization for LOCALIZABLE_PROPERTIES (matching Android/iOS parity)
|
|
156
|
+
*/
|
|
157
|
+
resolveComponent(component) {
|
|
158
|
+
if (!component)
|
|
159
|
+
return component;
|
|
160
|
+
const resolvedComponent = { ...component };
|
|
161
|
+
// Resolve properties
|
|
162
|
+
if (component.properties) {
|
|
163
|
+
const resolvedProperties = {};
|
|
164
|
+
for (const [key, value] of Object.entries(component.properties)) {
|
|
165
|
+
if (typeof value === 'string') {
|
|
166
|
+
const variableResolved = this.resolve(value);
|
|
167
|
+
// Apply localization for localizable properties (matching Android/iOS)
|
|
168
|
+
if (this.localizationResolver
|
|
169
|
+
&& LOCALIZABLE_PROPERTIES.has(key)
|
|
170
|
+
&& !this.localizationResolver.isBaseLocale()) {
|
|
171
|
+
resolvedProperties[key] = this.localizationResolver.resolve(`${component.id}.${key}`, variableResolved);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
resolvedProperties[key] = variableResolved;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
resolvedProperties[key] = value;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
resolvedComponent.properties = resolvedProperties;
|
|
182
|
+
}
|
|
183
|
+
// Recursively resolve children
|
|
184
|
+
if (component.children && Array.isArray(component.children)) {
|
|
185
|
+
resolvedComponent.children = component.children.map((child) => this.resolveComponent(child));
|
|
186
|
+
}
|
|
187
|
+
return resolvedComponent;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Resolve all components in a configuration
|
|
191
|
+
*/
|
|
192
|
+
resolveConfiguration(config) {
|
|
193
|
+
if (!config)
|
|
194
|
+
return config;
|
|
195
|
+
const resolvedConfig = { ...config };
|
|
196
|
+
if (config.components && Array.isArray(config.components)) {
|
|
197
|
+
resolvedConfig.components = config.components.map((comp) => this.resolveComponent(comp));
|
|
198
|
+
}
|
|
199
|
+
return resolvedConfig;
|
|
200
|
+
}
|
|
201
|
+
// ─── Device Variable Builder (single source of truth) ──────────────────────
|
|
202
|
+
/**
|
|
203
|
+
* Build device variable map from DeviceInfo — single source of truth.
|
|
204
|
+
* Used by withDeviceInfo(), withFullContext(), withProducts().
|
|
205
|
+
* Maps RN DeviceInfo field names to canonical SDK key names.
|
|
206
|
+
*/
|
|
207
|
+
static buildDeviceVariables(deviceInfo) {
|
|
208
|
+
const vars = {};
|
|
209
|
+
// Flat keys for direct access (canonical names matching Android/iOS)
|
|
210
|
+
if (deviceInfo.osName)
|
|
211
|
+
vars['device.osName'] = deviceInfo.osName;
|
|
212
|
+
if (deviceInfo.osVersion)
|
|
213
|
+
vars['device.osVersion'] = deviceInfo.osVersion;
|
|
214
|
+
if (deviceInfo.platform)
|
|
215
|
+
vars['device.platform'] = deviceInfo.platform;
|
|
216
|
+
if (deviceInfo.deviceModel)
|
|
217
|
+
vars['device.deviceModel'] = deviceInfo.deviceModel;
|
|
218
|
+
if (deviceInfo.screenWidth !== undefined)
|
|
219
|
+
vars['device.screenWidth'] = deviceInfo.screenWidth;
|
|
220
|
+
if (deviceInfo.screenHeight !== undefined)
|
|
221
|
+
vars['device.screenHeight'] = deviceInfo.screenHeight;
|
|
222
|
+
if (deviceInfo.locale)
|
|
223
|
+
vars['device.locale'] = deviceInfo.locale;
|
|
224
|
+
if (deviceInfo.timezone)
|
|
225
|
+
vars['device.timezone'] = deviceInfo.timezone;
|
|
226
|
+
if (deviceInfo.interfaceStyle)
|
|
227
|
+
vars['device.interfaceStyle'] = deviceInfo.interfaceStyle;
|
|
228
|
+
if (deviceInfo.batteryLevel !== undefined)
|
|
229
|
+
vars['device.batteryLevel'] = deviceInfo.batteryLevel;
|
|
230
|
+
// Map RN field name to canonical SDK key name
|
|
231
|
+
if (deviceInfo.isLowPowerMode !== undefined)
|
|
232
|
+
vars['device.isLowPowerModeEnabled'] = deviceInfo.isLowPowerMode;
|
|
233
|
+
if (deviceInfo.networkType)
|
|
234
|
+
vars['device.radioType'] = deviceInfo.networkType;
|
|
235
|
+
if (deviceInfo.carrierName)
|
|
236
|
+
vars['device.carrierName'] = deviceInfo.carrierName;
|
|
237
|
+
// Nested object for dot-path resolution (includes screenWidth/Height)
|
|
238
|
+
vars.device = {
|
|
239
|
+
osName: deviceInfo.osName,
|
|
240
|
+
osVersion: deviceInfo.osVersion,
|
|
241
|
+
platform: deviceInfo.platform,
|
|
242
|
+
deviceModel: deviceInfo.deviceModel,
|
|
243
|
+
locale: deviceInfo.locale,
|
|
244
|
+
timezone: deviceInfo.timezone,
|
|
245
|
+
interfaceStyle: deviceInfo.interfaceStyle,
|
|
246
|
+
screenWidth: deviceInfo.screenWidth,
|
|
247
|
+
screenHeight: deviceInfo.screenHeight,
|
|
248
|
+
};
|
|
249
|
+
return vars;
|
|
250
|
+
}
|
|
251
|
+
// ─── Factory Methods ───────────────────────────────────────────────────────
|
|
252
|
+
/**
|
|
253
|
+
* Create resolver with offering variables injected
|
|
254
|
+
*/
|
|
255
|
+
static withOfferings(config, offerings) {
|
|
256
|
+
return new VariableResolver(config.variables, offerings, LocalizationResolver_1.LocalizationResolver.fromConfiguration(config), config.variableLocalizations, deviceLocale());
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Create resolver with device info variables
|
|
260
|
+
* Maps device info to namespaced variables (device.*)
|
|
261
|
+
*/
|
|
262
|
+
static withDeviceInfo(config, deviceInfo) {
|
|
263
|
+
var _a;
|
|
264
|
+
return new VariableResolver(config.variables, VariableResolver.buildDeviceVariables(deviceInfo), LocalizationResolver_1.LocalizationResolver.fromConfiguration(config), config.variableLocalizations, (_a = deviceInfo.locale) !== null && _a !== void 0 ? _a : deviceLocale());
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Create resolver with localization support
|
|
268
|
+
*/
|
|
269
|
+
static withLocalization(config, locale) {
|
|
270
|
+
const locResolver = locale
|
|
271
|
+
? LocalizationResolver_1.LocalizationResolver.withLocale(config, locale)
|
|
272
|
+
: LocalizationResolver_1.LocalizationResolver.withDeviceLocale(config);
|
|
273
|
+
return new VariableResolver(config.variables, {}, locResolver, config.variableLocalizations, locale !== null && locale !== void 0 ? locale : deviceLocale());
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Create resolver with full context (offerings, device info, localization)
|
|
277
|
+
*/
|
|
278
|
+
static withFullContext(config, offerings, deviceInfo) {
|
|
279
|
+
var _a;
|
|
280
|
+
const runtimeVars = {
|
|
281
|
+
...VariableResolver.buildDeviceVariables(deviceInfo),
|
|
282
|
+
...offerings,
|
|
283
|
+
};
|
|
284
|
+
return new VariableResolver(config.variables, runtimeVars, LocalizationResolver_1.LocalizationResolver.withDeviceLocale(config), config.variableLocalizations, (_a = deviceInfo.locale) !== null && _a !== void 0 ? _a : deviceLocale());
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Create resolver with user eligibility variables
|
|
288
|
+
* Enables {{user.isEligibleForIntro}}, {{user.hasUsedTrial}}, etc.
|
|
289
|
+
*/
|
|
290
|
+
static withEligibility(config, isEligibleForIntro = true, hasUsedTrial = false, isEligibleForPromo = false) {
|
|
291
|
+
const eligibilityVars = {
|
|
292
|
+
'user.isEligibleForIntro': isEligibleForIntro,
|
|
293
|
+
'user.hasUsedTrial': hasUsedTrial,
|
|
294
|
+
'user.isEligibleForPromo': isEligibleForPromo,
|
|
295
|
+
user: {
|
|
296
|
+
isEligibleForIntro,
|
|
297
|
+
hasUsedTrial,
|
|
298
|
+
isEligibleForPromo,
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
return new VariableResolver(config.variables, eligibilityVars, LocalizationResolver_1.LocalizationResolver.fromConfiguration(config), config.variableLocalizations, deviceLocale());
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Create resolver with product/package variables
|
|
305
|
+
* Enables {{product.price}}, {{selectedProduct.*}}, {{products.N.*}},
|
|
306
|
+
* {{primary.*}}, {{monthly.*}}, etc. (40+ variable keys per package)
|
|
307
|
+
*
|
|
308
|
+
* Delegates to ProductVariableProvider for full parity with Android.
|
|
309
|
+
*/
|
|
310
|
+
static withProducts(config, packages, selectedIndex = 0, trialEligibility, deviceInfo) {
|
|
311
|
+
var _a;
|
|
312
|
+
const provider = new ProductVariableProvider_1.ProductVariableProvider(packages, trialEligibility);
|
|
313
|
+
const productVars = {};
|
|
314
|
+
// Selected product → product.* prefix
|
|
315
|
+
Object.assign(productVars, provider.getVariablesForProduct(selectedIndex));
|
|
316
|
+
// selectedProduct.* mirror
|
|
317
|
+
Object.assign(productVars, provider.getSelectedProductVariables(selectedIndex));
|
|
318
|
+
// products.N.* indexed access
|
|
319
|
+
Object.assign(productVars, provider.getAllProductVariables());
|
|
320
|
+
productVars['packages.count'] = packages.length;
|
|
321
|
+
// Convenience aliases (primary.*, secondary.*, monthly.*, annual.*, etc.)
|
|
322
|
+
Object.assign(productVars, provider.getConvenienceVariables());
|
|
323
|
+
// Add device info if provided — use canonical buildDeviceVariables
|
|
324
|
+
if (deviceInfo) {
|
|
325
|
+
Object.assign(productVars, VariableResolver.buildDeviceVariables(deviceInfo));
|
|
326
|
+
}
|
|
327
|
+
return new VariableResolver(config.variables, productVars, LocalizationResolver_1.LocalizationResolver.fromConfiguration(config), config.variableLocalizations, (_a = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.locale) !== null && _a !== void 0 ? _a : deviceLocale());
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Create resolver with an Offering's packages
|
|
331
|
+
* Convenience wrapper around withProducts
|
|
332
|
+
*/
|
|
333
|
+
static withOffering(config, offering, selectedIndex = 0, trialEligibility, deviceInfo) {
|
|
334
|
+
return VariableResolver.withProducts(config, offering.packages, selectedIndex, trialEligibility, deviceInfo);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Build the FULL render-time variable bag (device + product/offer/user) — the
|
|
338
|
+
* React-Native analog of iOS/Android `VariableResolver.withOffering(...).variables`.
|
|
339
|
+
* Fed to `processFullComponent` so Dynamic Values conditions on
|
|
340
|
+
* `products.*` / `selectedProduct.*` / `offer.*` / `user.*` actually resolve on
|
|
341
|
+
* RN (previously the render bag was device + `config.variables` only, so every
|
|
342
|
+
* product/user DV condition silently rendered `default`).
|
|
343
|
+
*
|
|
344
|
+
* `selectedIndex` is 0 for now — parity with the native Phase-2 render pass,
|
|
345
|
+
* which is not yet selection-reactive (Phase 2b). `appUserId`/`customerInfo` are
|
|
346
|
+
* passed in by the caller (`BillDog.getAppUserId()` + `customerInfoEmitter.latest`,
|
|
347
|
+
* both sync) to avoid a VariableResolver→BillDog import cycle.
|
|
348
|
+
*/
|
|
349
|
+
static buildRenderVariables(config, deviceInfo, opts) {
|
|
350
|
+
var _a, _b, _c, _d, _e;
|
|
351
|
+
const selectedIndex = (_a = opts === null || opts === void 0 ? void 0 : opts.selectedIndex) !== null && _a !== void 0 ? _a : 0;
|
|
352
|
+
const bag = { ...VariableResolver.buildDeviceVariables(deviceInfo) };
|
|
353
|
+
const packages = config.packages;
|
|
354
|
+
if (Array.isArray(packages) && packages.length > 0) {
|
|
355
|
+
const provider = new ProductVariableProvider_1.ProductVariableProvider(packages, config.trialEligibility);
|
|
356
|
+
Object.assign(bag, provider.getVariablesForProduct(selectedIndex)); // product.* (+ user.isEligibleForIntro/hasUsedTrial)
|
|
357
|
+
Object.assign(bag, provider.getSelectedProductVariables(selectedIndex)); // selectedProduct.*
|
|
358
|
+
Object.assign(bag, provider.getAllProductVariables()); // products.N.*
|
|
359
|
+
Object.assign(bag, provider.getConvenienceVariables()); // primary.*/secondary.*/…
|
|
360
|
+
bag['packages.count'] = packages.length;
|
|
361
|
+
bag['products.selectedIndex'] = selectedIndex;
|
|
362
|
+
}
|
|
363
|
+
// user.seed — deterministic A/B bucket (canonical murmur; parity with native).
|
|
364
|
+
if (opts === null || opts === void 0 ? void 0 : opts.appUserId)
|
|
365
|
+
bag['user.seed'] = (0, murmur_1.userSeedBucket)(opts.appUserId);
|
|
366
|
+
// user.subscription — active entitlement tier, from the SYNC-cached CustomerInfo
|
|
367
|
+
// (may be null on cold start; best-effort, same async limitation as iOS Phase 2).
|
|
368
|
+
// Sorted for cross-platform determinism with iOS/Android.
|
|
369
|
+
const customer = opts === null || opts === void 0 ? void 0 : opts.customerInfo;
|
|
370
|
+
if (customer) {
|
|
371
|
+
const active = ((_b = customer.entitlements) !== null && _b !== void 0 ? _b : []).filter((e) => e.isActive).map((e) => e.identifier).sort();
|
|
372
|
+
bag['user.subscription'] = (_c = active[0]) !== null && _c !== void 0 ? _c : '';
|
|
373
|
+
bag['user.hasActiveSubscription'] = ((_e = (_d = customer.activeSubscriptions) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0) > 0 || active.length > 0;
|
|
374
|
+
bag['user.activeEntitlements'] = active.join(',');
|
|
375
|
+
}
|
|
376
|
+
return bag;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
exports.VariableResolver = VariableResolver;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebViewPaywall — Optional WebView-based paywall component for the
|
|
3
|
+
* React Native SDK.
|
|
4
|
+
*
|
|
5
|
+
* The default React Native paywall path is native (`PaywallView` →
|
|
6
|
+
* `ComponentRenderer`). This component is the WebView alternative that
|
|
7
|
+
* loads the same unified renderer bundle iOS / Android / Flutter use,
|
|
8
|
+
* so paywalls visually match across all platforms at the cost of one
|
|
9
|
+
* extra `react-native-webview` dependency.
|
|
10
|
+
*
|
|
11
|
+
* Android parity reference:
|
|
12
|
+
* - `BillDogWebViewPaywall` (Android, billdog-paywalls-webview module)
|
|
13
|
+
* - Canonical message protocol:
|
|
14
|
+
* packages/paywall-renderer/src/native/messageProtocol.ts
|
|
15
|
+
* - postMessage events handled here mirror the protocol's
|
|
16
|
+
* OutboundMessageType union: purchase / restore / close / open_url /
|
|
17
|
+
* track / state_changed / error / ready.
|
|
18
|
+
*
|
|
19
|
+
* Architecture choice: URL-based loading (same as Flutter) rather than
|
|
20
|
+
* bundled HTML asset. The renderer is served by the `render-paywall`
|
|
21
|
+
* edge function at `https://api.billdog.io/v1/render-paywall?...`,
|
|
22
|
+
* and the paywall config is delivered to the renderer via the JS
|
|
23
|
+
* bridge function `window.loadPaywallData(...)` — same contract as
|
|
24
|
+
* the Flutter SDK uses (packages/flutter/lib/src/widgets/paywall_view.dart).
|
|
25
|
+
*
|
|
26
|
+
* Bundling the HTML as a static asset requires a metro.config.js change
|
|
27
|
+
* (`assetExts: ['html', ...]` + `metro-html-loader`). URL-based avoids
|
|
28
|
+
* that and is the simpler MVP. If first-paint latency becomes a problem,
|
|
29
|
+
* a future revision can switch to local-asset loading.
|
|
30
|
+
*/
|
|
31
|
+
import React from 'react';
|
|
32
|
+
export interface WebViewPaywallProps {
|
|
33
|
+
/** Paywall identifier — matches the placement id used elsewhere. */
|
|
34
|
+
paywallIdentifier: string;
|
|
35
|
+
/** API key for the BillDog project. */
|
|
36
|
+
apiKey: string;
|
|
37
|
+
/** Optional current user id; forwarded to the renderer so it can
|
|
38
|
+
* resolve per-user variants / experiments. */
|
|
39
|
+
customerId?: string;
|
|
40
|
+
/** Render the WebView inside a fullscreen modal. Default true. */
|
|
41
|
+
modal?: boolean;
|
|
42
|
+
/** Visibility (only meaningful when `modal=true`). */
|
|
43
|
+
visible?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Explicit locale for the rendered paywall. When omitted the component
|
|
46
|
+
* resolves the effective locale via `LocalizationResolver.getEffectiveLocale()`
|
|
47
|
+
* (runtime override > device locale > "en") — matching the Android
|
|
48
|
+
* `WebViewPaywallActivity.effectiveLocale()` precedence.
|
|
49
|
+
*/
|
|
50
|
+
locale?: string;
|
|
51
|
+
/** Fires when the renderer reports `ready` over the postMessage bridge. */
|
|
52
|
+
onReady?: () => void;
|
|
53
|
+
/** Fires when the consumer should initiate a purchase. The product id
|
|
54
|
+
* comes from the renderer's `purchase` message. */
|
|
55
|
+
onPurchase?: (productIdentifier: string) => void;
|
|
56
|
+
/** Fires when the user taps Restore. */
|
|
57
|
+
onRestore?: () => void;
|
|
58
|
+
/** Fires when the user closes the paywall. */
|
|
59
|
+
onDismiss?: () => void;
|
|
60
|
+
/** Fires on `open_url` messages (e.g. terms / privacy links). */
|
|
61
|
+
onOpenUrl?: (url: string) => void;
|
|
62
|
+
/** Fires on `track` analytics events. */
|
|
63
|
+
onTrackEvent?: (eventName: string, properties?: Record<string, unknown>) => void;
|
|
64
|
+
/** Fires on any error message from the renderer. */
|
|
65
|
+
onError?: (error: Error) => void;
|
|
66
|
+
/**
|
|
67
|
+
* Optional custom action execution handler (Route B).
|
|
68
|
+
* If provided, the consumer must execute the action and return a promise
|
|
69
|
+
* resolving with the result or rejecting with an error. If omitted, the
|
|
70
|
+
* SDK automatically executes it via the server-side Edge function.
|
|
71
|
+
*/
|
|
72
|
+
onExecuteAction?: (paywallId: string | null, componentId: string, actionType: string, idempotencyKey: string | null) => Promise<unknown>;
|
|
73
|
+
}
|
|
74
|
+
export declare const WebViewPaywall: React.FC<WebViewPaywallProps>;
|
|
75
|
+
export default WebViewPaywall;
|