@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,48 @@
|
|
|
1
|
+
import { Platform, PermissionsAndroid } from 'react-native';
|
|
2
|
+
import PushNotification from 'react-native-push-notification';
|
|
3
|
+
|
|
4
|
+
export type NotificationPermissionStatus = 'granted' | 'denied' | 'default';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Request push notification permissions
|
|
8
|
+
*/
|
|
9
|
+
export async function requestNotificationPermission(): Promise<NotificationPermissionStatus> {
|
|
10
|
+
if (Platform.OS === 'ios') {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
PushNotification.requestPermissions((permissions: any) => {
|
|
13
|
+
resolve(permissions.alert ? 'granted' : 'denied');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
} else if (Platform.OS === 'android') {
|
|
17
|
+
if (Platform.Version >= 33) {
|
|
18
|
+
const granted = await PermissionsAndroid.request(
|
|
19
|
+
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
|
|
20
|
+
);
|
|
21
|
+
return granted === PermissionsAndroid.RESULTS.GRANTED ? 'granted' : 'denied';
|
|
22
|
+
}
|
|
23
|
+
return 'granted'; // Granted by default on older Android versions
|
|
24
|
+
}
|
|
25
|
+
return 'denied';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check current notification permission status
|
|
30
|
+
*/
|
|
31
|
+
export async function checkNotificationPermission(): Promise<NotificationPermissionStatus> {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
PushNotification.checkPermissions((permissions: any) => {
|
|
34
|
+
if (permissions.alert === true || permissions.alert === 1) {
|
|
35
|
+
resolve('granted');
|
|
36
|
+
} else {
|
|
37
|
+
resolve('denied');
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if notifications are supported
|
|
45
|
+
*/
|
|
46
|
+
export function isNotificationSupported(): boolean {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for formatting prices and subscription periods
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Format a price with currency
|
|
7
|
+
*/
|
|
8
|
+
export function formatPrice(
|
|
9
|
+
price: number,
|
|
10
|
+
currency: string = 'USD',
|
|
11
|
+
locale: string = 'en-US'
|
|
12
|
+
): string {
|
|
13
|
+
try {
|
|
14
|
+
const formatter = new Intl.NumberFormat(locale, {
|
|
15
|
+
style: 'currency',
|
|
16
|
+
currency: currency,
|
|
17
|
+
});
|
|
18
|
+
return formatter.format(price);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return `${price} ${currency}`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Format a subscription period
|
|
26
|
+
*/
|
|
27
|
+
export function formatSubscriptionPeriod(
|
|
28
|
+
duration: number,
|
|
29
|
+
period: string,
|
|
30
|
+
locale: string = 'en-US'
|
|
31
|
+
): string {
|
|
32
|
+
const periodUnit = period.toLowerCase();
|
|
33
|
+
|
|
34
|
+
// Handle singular vs plural
|
|
35
|
+
if (duration === 1) {
|
|
36
|
+
switch (periodUnit) {
|
|
37
|
+
case 'day':
|
|
38
|
+
return 'daily';
|
|
39
|
+
case 'week':
|
|
40
|
+
return 'weekly';
|
|
41
|
+
case 'month':
|
|
42
|
+
return 'monthly';
|
|
43
|
+
case 'year':
|
|
44
|
+
return 'yearly';
|
|
45
|
+
default:
|
|
46
|
+
return `${duration} ${period}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Plural
|
|
51
|
+
const pluralPeriod = (() => {
|
|
52
|
+
switch (periodUnit) {
|
|
53
|
+
case 'day':
|
|
54
|
+
return 'days';
|
|
55
|
+
case 'week':
|
|
56
|
+
return 'weeks';
|
|
57
|
+
case 'month':
|
|
58
|
+
return 'months';
|
|
59
|
+
case 'year':
|
|
60
|
+
return 'years';
|
|
61
|
+
default:
|
|
62
|
+
return `${period}s`;
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
|
|
66
|
+
return `${duration} ${pluralPeriod}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Format intro pricing info
|
|
71
|
+
*/
|
|
72
|
+
export function formatIntroPricing(
|
|
73
|
+
introPrice: number | null | undefined,
|
|
74
|
+
introDuration: number | null | undefined,
|
|
75
|
+
introPeriod: string | null | undefined,
|
|
76
|
+
introCycles: number | null | undefined,
|
|
77
|
+
currency: string = 'USD',
|
|
78
|
+
locale: string = 'en-US'
|
|
79
|
+
): string | null {
|
|
80
|
+
if (!introDuration || !introPeriod) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const periodText = formatSubscriptionPeriod(introDuration, introPeriod, locale);
|
|
85
|
+
|
|
86
|
+
// Free trial
|
|
87
|
+
if (introPrice === 0) {
|
|
88
|
+
return `${periodText} free trial`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Intro price
|
|
92
|
+
if (introPrice != null) {
|
|
93
|
+
const priceText = formatPrice(introPrice, currency, locale);
|
|
94
|
+
if (introCycles) {
|
|
95
|
+
return `${priceText} for ${introCycles} ${periodText}`;
|
|
96
|
+
}
|
|
97
|
+
return `${priceText} for ${periodText}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Helper to get package type display name
|
|
105
|
+
*/
|
|
106
|
+
export function getPackageTypeDisplayName(packageType: string): string {
|
|
107
|
+
const typeMap: Record<string, string> = {
|
|
108
|
+
monthly: 'Monthly',
|
|
109
|
+
annual: 'Annual',
|
|
110
|
+
six_month: '6 Months',
|
|
111
|
+
three_month: '3 Months',
|
|
112
|
+
two_month: '2 Months',
|
|
113
|
+
weekly: 'Weekly',
|
|
114
|
+
lifetime: 'Lifetime',
|
|
115
|
+
custom: 'Custom',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return typeMap[packageType] || packageType;
|
|
119
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Share } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface ShareData {
|
|
4
|
+
title?: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ShareResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Share content using native share sheet
|
|
16
|
+
*/
|
|
17
|
+
export async function share(data: ShareData): Promise<ShareResult> {
|
|
18
|
+
try {
|
|
19
|
+
const shareOptions: any = {};
|
|
20
|
+
|
|
21
|
+
if (data.message) {
|
|
22
|
+
shareOptions.message = data.message;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (data.url) {
|
|
26
|
+
shareOptions.url = data.url;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (data.title) {
|
|
30
|
+
shareOptions.title = data.title;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = await Share.share(shareOptions);
|
|
34
|
+
|
|
35
|
+
if (result.action === Share.sharedAction) {
|
|
36
|
+
return { success: true };
|
|
37
|
+
} else if (result.action === Share.dismissedAction) {
|
|
38
|
+
return { success: false, error: 'Share cancelled' };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { success: true };
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: error instanceof Error ? error.message : 'Share failed',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if sharing is supported
|
|
52
|
+
*/
|
|
53
|
+
export function isShareSupported(): boolean {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscription Upgrade/Downgrade Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides methods for managing subscription plan changes
|
|
5
|
+
* via the upgrade-subscription edge function.
|
|
6
|
+
*
|
|
7
|
+
* URLs are resolved through ApiClient → BillDogConfiguration.apiBaseUrl (api.billdog.io).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ApiClient } from './ApiClient';
|
|
11
|
+
|
|
12
|
+
// MARK: - Types
|
|
13
|
+
|
|
14
|
+
/** Proration behavior options */
|
|
15
|
+
export type ProrationBehavior = 'create_prorations' | 'none' | 'always_invoice';
|
|
16
|
+
|
|
17
|
+
/** Billing cycle anchor options */
|
|
18
|
+
export type BillingCycleAnchor = 'now' | 'unchanged';
|
|
19
|
+
|
|
20
|
+
/** Options for configuring subscription upgrade/downgrade */
|
|
21
|
+
export interface SubscriptionUpgradeOptions {
|
|
22
|
+
/** Customer ID (optional if already identified) */
|
|
23
|
+
customerId?: string;
|
|
24
|
+
/** App user ID (alternative identifier) */
|
|
25
|
+
appUserId?: string;
|
|
26
|
+
/** Specific web purchase ID to upgrade */
|
|
27
|
+
webPurchaseId?: string;
|
|
28
|
+
/** How proration should be handled */
|
|
29
|
+
prorationBehavior?: ProrationBehavior;
|
|
30
|
+
/** When the new billing cycle should start */
|
|
31
|
+
billingCycleAnchor?: BillingCycleAnchor;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Preview of subscription change costs */
|
|
35
|
+
export interface ProrationPreview {
|
|
36
|
+
/** Current subscription price ID */
|
|
37
|
+
currentPriceId: string;
|
|
38
|
+
/** New price ID after upgrade/downgrade */
|
|
39
|
+
newPriceId: string;
|
|
40
|
+
/** Amount due immediately (in cents) */
|
|
41
|
+
immediateAmountCents: number;
|
|
42
|
+
/** Human-readable immediate amount */
|
|
43
|
+
formattedImmediateAmount: string;
|
|
44
|
+
/** Prorated credit amount (in cents) */
|
|
45
|
+
proratedCreditCents: number;
|
|
46
|
+
/** Prorated charge amount (in cents) */
|
|
47
|
+
proratedChargeCents: number;
|
|
48
|
+
/** Next billing date (ISO 8601 string) */
|
|
49
|
+
nextBillingDate: string | null;
|
|
50
|
+
/** Currency code */
|
|
51
|
+
currency: string;
|
|
52
|
+
/** Whether this is an upgrade or downgrade */
|
|
53
|
+
isUpgrade: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Result of a subscription upgrade/downgrade operation */
|
|
57
|
+
export interface SubscriptionUpgradeResult {
|
|
58
|
+
/** Whether the operation succeeded */
|
|
59
|
+
success: boolean;
|
|
60
|
+
/** Stripe subscription ID */
|
|
61
|
+
subscriptionId?: string;
|
|
62
|
+
/** Type of change: "upgrade" or "downgrade" */
|
|
63
|
+
upgradeType: string;
|
|
64
|
+
/** Previous price ID */
|
|
65
|
+
oldPriceId?: string;
|
|
66
|
+
/** New price ID */
|
|
67
|
+
newPriceId: string;
|
|
68
|
+
/** Current period start date (ISO 8601 string) */
|
|
69
|
+
currentPeriodStart?: string;
|
|
70
|
+
/** Current period end date (ISO 8601 string) */
|
|
71
|
+
currentPeriodEnd?: string;
|
|
72
|
+
/** Applied proration behavior */
|
|
73
|
+
prorationBehavior: string;
|
|
74
|
+
/** Error message if failed */
|
|
75
|
+
errorMessage?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// MARK: - API Functions
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Preview a subscription upgrade/downgrade before executing.
|
|
82
|
+
* Shows prorated amounts and billing changes without making changes.
|
|
83
|
+
*
|
|
84
|
+
* @param newPriceId - The Stripe price ID to change to
|
|
85
|
+
* @param options - Upgrade options
|
|
86
|
+
* @param apiKey - @deprecated Resolved from BillDogConfiguration if omitted
|
|
87
|
+
*/
|
|
88
|
+
export async function previewSubscriptionUpgrade(
|
|
89
|
+
apiKey: string | undefined,
|
|
90
|
+
newPriceId: string,
|
|
91
|
+
options?: SubscriptionUpgradeOptions
|
|
92
|
+
): Promise<ProrationPreview> {
|
|
93
|
+
const data = await ApiClient.post('upgrade-subscription', {
|
|
94
|
+
new_price_id: newPriceId,
|
|
95
|
+
preview: true,
|
|
96
|
+
customer_id: options?.customerId,
|
|
97
|
+
app_user_id: options?.appUserId,
|
|
98
|
+
web_purchase_id: options?.webPurchaseId,
|
|
99
|
+
proration_behavior: options?.prorationBehavior ?? 'create_prorations',
|
|
100
|
+
billing_cycle_anchor: options?.billingCycleAnchor ?? 'unchanged',
|
|
101
|
+
}, {
|
|
102
|
+
apiKey: apiKey || undefined,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const preview = data.preview;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
currentPriceId: preview.current_price_id || '',
|
|
109
|
+
newPriceId: preview.new_price_id || '',
|
|
110
|
+
immediateAmountCents: preview.immediate_amount_cents || 0,
|
|
111
|
+
formattedImmediateAmount: preview.formatted_immediate_amount || '$0.00',
|
|
112
|
+
proratedCreditCents: preview.prorated_credit_cents || 0,
|
|
113
|
+
proratedChargeCents: preview.prorated_charge_cents || 0,
|
|
114
|
+
nextBillingDate: preview.next_billing_date || null,
|
|
115
|
+
currency: preview.currency || 'usd',
|
|
116
|
+
isUpgrade: preview.is_upgrade ?? true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Upgrade a subscription to a higher-tier plan
|
|
122
|
+
*/
|
|
123
|
+
export async function upgradeSubscription(
|
|
124
|
+
apiKey: string | undefined,
|
|
125
|
+
newPriceId: string,
|
|
126
|
+
options?: SubscriptionUpgradeOptions
|
|
127
|
+
): Promise<SubscriptionUpgradeResult> {
|
|
128
|
+
return executeSubscriptionChange(apiKey, newPriceId, options);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Downgrade a subscription to a lower-tier plan
|
|
133
|
+
*/
|
|
134
|
+
export async function downgradeSubscription(
|
|
135
|
+
apiKey: string | undefined,
|
|
136
|
+
newPriceId: string,
|
|
137
|
+
options?: SubscriptionUpgradeOptions
|
|
138
|
+
): Promise<SubscriptionUpgradeResult> {
|
|
139
|
+
return executeSubscriptionChange(apiKey, newPriceId, options);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Internal function to execute subscription change
|
|
144
|
+
*/
|
|
145
|
+
async function executeSubscriptionChange(
|
|
146
|
+
apiKey: string | undefined,
|
|
147
|
+
newPriceId: string,
|
|
148
|
+
options?: SubscriptionUpgradeOptions
|
|
149
|
+
): Promise<SubscriptionUpgradeResult> {
|
|
150
|
+
try {
|
|
151
|
+
const data = await ApiClient.post('upgrade-subscription', {
|
|
152
|
+
new_price_id: newPriceId,
|
|
153
|
+
preview: false,
|
|
154
|
+
customer_id: options?.customerId,
|
|
155
|
+
app_user_id: options?.appUserId,
|
|
156
|
+
web_purchase_id: options?.webPurchaseId,
|
|
157
|
+
proration_behavior: options?.prorationBehavior ?? 'create_prorations',
|
|
158
|
+
billing_cycle_anchor: options?.billingCycleAnchor ?? 'unchanged',
|
|
159
|
+
}, {
|
|
160
|
+
apiKey: apiKey || undefined,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
success: data.success ?? false,
|
|
165
|
+
subscriptionId: data.subscription_id,
|
|
166
|
+
upgradeType: data.upgrade_type || 'upgrade',
|
|
167
|
+
oldPriceId: data.old_price_id,
|
|
168
|
+
newPriceId: data.new_price_id || newPriceId,
|
|
169
|
+
currentPeriodStart: data.current_period_start,
|
|
170
|
+
currentPeriodEnd: data.current_period_end,
|
|
171
|
+
prorationBehavior: data.proration_behavior || 'create_prorations',
|
|
172
|
+
errorMessage: data.error,
|
|
173
|
+
};
|
|
174
|
+
} catch (err: any) {
|
|
175
|
+
return {
|
|
176
|
+
success: false,
|
|
177
|
+
upgradeType: 'unknown',
|
|
178
|
+
newPriceId: newPriceId,
|
|
179
|
+
prorationBehavior: options?.prorationBehavior ?? 'create_prorations',
|
|
180
|
+
errorMessage: err.message || `Failed to change subscription`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ValueUtils — Shared primitive utilities for BillDog React Native SDK
|
|
3
|
+
*
|
|
4
|
+
* Canonical source for type coercion, comparison, and nested path walking.
|
|
5
|
+
* Used by dynamicValues.ts, xFlowTags.ts, and other modules.
|
|
6
|
+
*
|
|
7
|
+
* stringify/toNumber implement the canonical xFlow grammar (§5 of
|
|
8
|
+
* src/schemas/xflow-grammar.md): null/undefined → "", non-numeric → 0.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Walk a dot-separated path on a nested object.
|
|
13
|
+
*
|
|
14
|
+
* Flat-key-first (grammar §4): a literal key matching the whole path wins
|
|
15
|
+
* over dotted traversal, so a variable stored under the literal key
|
|
16
|
+
* "user.name" resolves before descending into `user`.
|
|
17
|
+
*/
|
|
18
|
+
export function getNestedValue(obj: any, path: string): any {
|
|
19
|
+
if (obj && typeof obj === 'object' && path in obj) return obj[path];
|
|
20
|
+
return path.split('.').reduce((current, key) => current?.[key], obj);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Numeric-first equality for dynamic value conditions, matching native `ValueUtils.isEqual` (iOS +
|
|
25
|
+
* Android) and the web/renderer copies. Both-null → true; if BOTH operands coerce to a number, compare
|
|
26
|
+
* numerically (so `10.0` equals `"10"`); otherwise string-compare. The string-only version this replaced
|
|
27
|
+
* diverged from the device the moment a fractional or typed value was involved.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* STRUCTURAL equality — the counterpart to `isEqual`, mirroring the split native
|
|
31
|
+
* `ValueUtils` already makes (`isDeepEqual` vs `valuesEqual` on Android).
|
|
32
|
+
*
|
|
33
|
+
* Do NOT confuse the two. `isEqual` coerces (`1` equals `"1"`) because DV CONDITIONS
|
|
34
|
+
* must; state identity must not, or PaywallStateManager would swallow a genuine
|
|
35
|
+
* `1` → `"1"` transition that Android/iOS emit. This mirrors Android's
|
|
36
|
+
* `ValueUtils.isDeepEqual`: numbers compare numerically, arrays/objects compare
|
|
37
|
+
* structurally, everything else is strict.
|
|
38
|
+
*/
|
|
39
|
+
export function isDeepEqual(a: any, b: any): boolean {
|
|
40
|
+
if (a === b) return true;
|
|
41
|
+
if (a == null || b == null) return false;
|
|
42
|
+
if (typeof a === 'number' && typeof b === 'number') return a === b;
|
|
43
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
44
|
+
return a.length === b.length && a.every((v, i) => isDeepEqual(v, b[i]));
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
47
|
+
if (typeof a === 'object' && typeof b === 'object') {
|
|
48
|
+
const ak = Object.keys(a);
|
|
49
|
+
const bk = Object.keys(b);
|
|
50
|
+
return ak.length === bk.length && ak.every((k) => isDeepEqual(a[k], b[k]));
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function isEqual(a: any, b: any): boolean {
|
|
56
|
+
if (a == null && b == null) return true;
|
|
57
|
+
if (a == null || b == null) return false;
|
|
58
|
+
const an = toNumberOrNull(a);
|
|
59
|
+
const bn = toNumberOrNull(b);
|
|
60
|
+
if (an !== null && bn !== null) return an === bn;
|
|
61
|
+
return stringify(a) === stringify(b);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check if a value is truthy (non-null, non-empty, non-zero).
|
|
66
|
+
*/
|
|
67
|
+
export function isTruthy(value: any): boolean {
|
|
68
|
+
if (value === null || value === undefined) return false;
|
|
69
|
+
if (value === false) return false;
|
|
70
|
+
if (value === '' || value === 0) return false;
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Canonical stringify — null/undefined → "" (grammar §5.1). */
|
|
75
|
+
export function stringify(value: any): string {
|
|
76
|
+
if (value === null || value === undefined) return '';
|
|
77
|
+
if (typeof value === 'string') return value;
|
|
78
|
+
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
|
79
|
+
if (typeof value === 'number') return String(value);
|
|
80
|
+
try {
|
|
81
|
+
return JSON.stringify(value) ?? '';
|
|
82
|
+
} catch {
|
|
83
|
+
return String(value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Canonical numeric coercion — non-numeric → 0, never NaN (grammar §5.2). */
|
|
88
|
+
export function toNumber(value: any): number {
|
|
89
|
+
if (typeof value === 'number') return Number.isFinite(value) ? value : 0;
|
|
90
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
91
|
+
const n = Number(value);
|
|
92
|
+
return Number.isFinite(n) ? n : 0;
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Number-or-null, matching native `toDoubleOrNull`. Used by `isEqual` to decide whether to compare
|
|
99
|
+
* numerically. Booleans return null so `true` never numeric-equals `1` (they string-compare instead).
|
|
100
|
+
*/
|
|
101
|
+
export function toNumberOrNull(value: any): number | null {
|
|
102
|
+
if (typeof value === 'number') return Number.isFinite(value) ? value : null;
|
|
103
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
104
|
+
const n = Number(value);
|
|
105
|
+
return Number.isFinite(n) ? n : null;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|