@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,265 @@
|
|
|
1
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for review timing logic
|
|
5
|
+
*/
|
|
6
|
+
export interface ReviewTimingConfig {
|
|
7
|
+
/** Minimum number of days since app install before requesting review */
|
|
8
|
+
minDaysSinceInstall?: number;
|
|
9
|
+
/** Minimum number of app sessions before requesting review */
|
|
10
|
+
minSessionCount?: number;
|
|
11
|
+
/** Minimum number of significant events (e.g., purchases, completed actions) */
|
|
12
|
+
minSignificantEvents?: number;
|
|
13
|
+
/** Days to wait after a user dismisses a review prompt */
|
|
14
|
+
cooldownAfterDismissal?: number;
|
|
15
|
+
/** Maximum number of review requests to show to a user */
|
|
16
|
+
maxRequestsPerUser?: number;
|
|
17
|
+
/** Enable automatic timing checks */
|
|
18
|
+
enableAutomaticTiming?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Timing data stored locally
|
|
23
|
+
*/
|
|
24
|
+
interface ReviewTimingData {
|
|
25
|
+
installDate: string;
|
|
26
|
+
sessionCount: number;
|
|
27
|
+
significantEventCount: number;
|
|
28
|
+
lastRequestDate?: string;
|
|
29
|
+
lastDismissalDate?: string;
|
|
30
|
+
totalRequestCount: number;
|
|
31
|
+
lastReviewCompletedDate?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const STORAGE_KEY = '@billdog_review_timing';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Default timing configuration
|
|
38
|
+
*/
|
|
39
|
+
const DEFAULT_CONFIG: Required<ReviewTimingConfig> = {
|
|
40
|
+
minDaysSinceInstall: 7,
|
|
41
|
+
minSessionCount: 5,
|
|
42
|
+
minSignificantEvents: 3,
|
|
43
|
+
cooldownAfterDismissal: 30,
|
|
44
|
+
maxRequestsPerUser: 3,
|
|
45
|
+
enableAutomaticTiming: true,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Manager for smart review timing logic
|
|
50
|
+
* Determines optimal moments to request reviews based on user behavior
|
|
51
|
+
*/
|
|
52
|
+
export class ReviewTimingManager {
|
|
53
|
+
private config: Required<ReviewTimingConfig>;
|
|
54
|
+
private timingData: ReviewTimingData | null = null;
|
|
55
|
+
|
|
56
|
+
constructor(config: ReviewTimingConfig = {}) {
|
|
57
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Initialize timing data (call on app launch)
|
|
62
|
+
*/
|
|
63
|
+
async initialize(): Promise<void> {
|
|
64
|
+
try {
|
|
65
|
+
const stored = await AsyncStorage.getItem(STORAGE_KEY);
|
|
66
|
+
if (stored) {
|
|
67
|
+
this.timingData = JSON.parse(stored);
|
|
68
|
+
} else {
|
|
69
|
+
// First time - create initial data
|
|
70
|
+
this.timingData = {
|
|
71
|
+
installDate: new Date().toISOString(),
|
|
72
|
+
sessionCount: 0,
|
|
73
|
+
significantEventCount: 0,
|
|
74
|
+
totalRequestCount: 0,
|
|
75
|
+
};
|
|
76
|
+
await this.saveTimingData();
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error('Error initializing review timing:', error);
|
|
80
|
+
// Fallback to in-memory only
|
|
81
|
+
this.timingData = {
|
|
82
|
+
installDate: new Date().toISOString(),
|
|
83
|
+
sessionCount: 0,
|
|
84
|
+
significantEventCount: 0,
|
|
85
|
+
totalRequestCount: 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Record a new app session
|
|
92
|
+
*/
|
|
93
|
+
async recordSession(): Promise<void> {
|
|
94
|
+
if (!this.timingData) await this.initialize();
|
|
95
|
+
if (this.timingData) {
|
|
96
|
+
this.timingData.sessionCount++;
|
|
97
|
+
await this.saveTimingData();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Record a significant event (e.g., purchase, completed action)
|
|
103
|
+
*/
|
|
104
|
+
async recordSignificantEvent(): Promise<void> {
|
|
105
|
+
if (!this.timingData) await this.initialize();
|
|
106
|
+
if (this.timingData) {
|
|
107
|
+
this.timingData.significantEventCount++;
|
|
108
|
+
await this.saveTimingData();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Record that a review was requested
|
|
114
|
+
*/
|
|
115
|
+
async recordReviewRequested(): Promise<void> {
|
|
116
|
+
if (!this.timingData) await this.initialize();
|
|
117
|
+
if (this.timingData) {
|
|
118
|
+
this.timingData.lastRequestDate = new Date().toISOString();
|
|
119
|
+
this.timingData.totalRequestCount++;
|
|
120
|
+
await this.saveTimingData();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Record that a review was dismissed by the user
|
|
126
|
+
*/
|
|
127
|
+
async recordReviewDismissed(): Promise<void> {
|
|
128
|
+
if (!this.timingData) await this.initialize();
|
|
129
|
+
if (this.timingData) {
|
|
130
|
+
this.timingData.lastDismissalDate = new Date().toISOString();
|
|
131
|
+
await this.saveTimingData();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Record that a review was completed by the user
|
|
137
|
+
*/
|
|
138
|
+
async recordReviewCompleted(): Promise<void> {
|
|
139
|
+
if (!this.timingData) await this.initialize();
|
|
140
|
+
if (this.timingData) {
|
|
141
|
+
this.timingData.lastReviewCompletedDate = new Date().toISOString();
|
|
142
|
+
await this.saveTimingData();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Check if it's a good time to request a review
|
|
148
|
+
* Returns true if all timing conditions are met
|
|
149
|
+
*/
|
|
150
|
+
async shouldRequestReview(): Promise<{
|
|
151
|
+
shouldRequest: boolean;
|
|
152
|
+
reason?: string;
|
|
153
|
+
timingData?: ReviewTimingData;
|
|
154
|
+
}> {
|
|
155
|
+
if (!this.config.enableAutomaticTiming) {
|
|
156
|
+
return { shouldRequest: true, reason: 'Automatic timing disabled' };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!this.timingData) await this.initialize();
|
|
160
|
+
if (!this.timingData) {
|
|
161
|
+
return { shouldRequest: false, reason: 'Timing data not available' };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check if user has already completed a review
|
|
165
|
+
if (this.timingData.lastReviewCompletedDate) {
|
|
166
|
+
return {
|
|
167
|
+
shouldRequest: false,
|
|
168
|
+
reason: 'User has already completed a review',
|
|
169
|
+
timingData: this.timingData
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Check max requests limit
|
|
174
|
+
if (this.timingData.totalRequestCount >= this.config.maxRequestsPerUser) {
|
|
175
|
+
return {
|
|
176
|
+
shouldRequest: false,
|
|
177
|
+
reason: `Maximum review requests (${this.config.maxRequestsPerUser}) reached`,
|
|
178
|
+
timingData: this.timingData
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check days since install
|
|
183
|
+
const daysSinceInstall = this.getDaysSince(new Date(this.timingData.installDate));
|
|
184
|
+
if (daysSinceInstall < this.config.minDaysSinceInstall) {
|
|
185
|
+
return {
|
|
186
|
+
shouldRequest: false,
|
|
187
|
+
reason: `Only ${daysSinceInstall} days since install (need ${this.config.minDaysSinceInstall})`,
|
|
188
|
+
timingData: this.timingData
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check session count
|
|
193
|
+
if (this.timingData.sessionCount < this.config.minSessionCount) {
|
|
194
|
+
return {
|
|
195
|
+
shouldRequest: false,
|
|
196
|
+
reason: `Only ${this.timingData.sessionCount} sessions (need ${this.config.minSessionCount})`,
|
|
197
|
+
timingData: this.timingData
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check significant events
|
|
202
|
+
if (this.timingData.significantEventCount < this.config.minSignificantEvents) {
|
|
203
|
+
return {
|
|
204
|
+
shouldRequest: false,
|
|
205
|
+
reason: `Only ${this.timingData.significantEventCount} significant events (need ${this.config.minSignificantEvents})`,
|
|
206
|
+
timingData: this.timingData
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Check cooldown after dismissal
|
|
211
|
+
if (this.timingData.lastDismissalDate) {
|
|
212
|
+
const daysSinceDismissal = this.getDaysSince(new Date(this.timingData.lastDismissalDate));
|
|
213
|
+
if (daysSinceDismissal < this.config.cooldownAfterDismissal) {
|
|
214
|
+
return {
|
|
215
|
+
shouldRequest: false,
|
|
216
|
+
reason: `Only ${daysSinceDismissal} days since last dismissal (need ${this.config.cooldownAfterDismissal})`,
|
|
217
|
+
timingData: this.timingData
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// All conditions met!
|
|
223
|
+
return {
|
|
224
|
+
shouldRequest: true,
|
|
225
|
+
reason: 'All timing conditions met',
|
|
226
|
+
timingData: this.timingData
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get current timing data
|
|
232
|
+
*/
|
|
233
|
+
async getTimingData(): Promise<ReviewTimingData | null> {
|
|
234
|
+
if (!this.timingData) await this.initialize();
|
|
235
|
+
return this.timingData;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Reset timing data (useful for testing)
|
|
240
|
+
*/
|
|
241
|
+
async resetTimingData(): Promise<void> {
|
|
242
|
+
this.timingData = {
|
|
243
|
+
installDate: new Date().toISOString(),
|
|
244
|
+
sessionCount: 0,
|
|
245
|
+
significantEventCount: 0,
|
|
246
|
+
totalRequestCount: 0,
|
|
247
|
+
};
|
|
248
|
+
await this.saveTimingData();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private async saveTimingData(): Promise<void> {
|
|
252
|
+
if (!this.timingData) return;
|
|
253
|
+
try {
|
|
254
|
+
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(this.timingData));
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error('Error saving review timing data:', error);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private getDaysSince(date: Date): number {
|
|
261
|
+
const now = new Date();
|
|
262
|
+
const diffMs = now.getTime() - date.getTime();
|
|
263
|
+
return Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BDSurvey Types — Standalone survey SDK types
|
|
3
|
+
*
|
|
4
|
+
* These types are the canonical definitions for the BDSurvey system.
|
|
5
|
+
* The BillDog SDK re-exports these for backward compatibility.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============= Survey Configuration (from bdsurvey-fetch) =============
|
|
9
|
+
|
|
10
|
+
export type SurveyQuestionType =
|
|
11
|
+
| 'multiple_choice'
|
|
12
|
+
| 'multi_select'
|
|
13
|
+
| 'text'
|
|
14
|
+
| 'rating'
|
|
15
|
+
| 'nps'
|
|
16
|
+
| 'boolean'
|
|
17
|
+
| 'dropdown'
|
|
18
|
+
| 'matrix'
|
|
19
|
+
| 'ranking'
|
|
20
|
+
| 'slider'
|
|
21
|
+
// PostHog-parity question types (added):
|
|
22
|
+
| 'emoji' // emoji-scale rating; config: { scale?: number; emojis?: string[]; labels?: { low?: string; high?: string } }
|
|
23
|
+
| 'thumbs' // thumbs up/down; answered via answer_number (1 = up, 0 = down) or boolean
|
|
24
|
+
| 'link'; // call-to-action question; config: { url: string; buttonText?: string }
|
|
25
|
+
|
|
26
|
+
export interface SurveyChoice {
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
value: string | null;
|
|
30
|
+
is_other: boolean;
|
|
31
|
+
choice_order: number;
|
|
32
|
+
image_url?: string | null;
|
|
33
|
+
metadata: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type SurveyLogicOperator =
|
|
37
|
+
| 'equals'
|
|
38
|
+
| 'not_equals'
|
|
39
|
+
| 'contains'
|
|
40
|
+
| 'is_any_of'
|
|
41
|
+
| 'greater_than'
|
|
42
|
+
| 'less_than';
|
|
43
|
+
|
|
44
|
+
export interface SurveyLogicCondition {
|
|
45
|
+
operator: SurveyLogicOperator;
|
|
46
|
+
value?: string | number;
|
|
47
|
+
choice_id?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SurveyLogicRule {
|
|
51
|
+
id: string;
|
|
52
|
+
/**
|
|
53
|
+
* Single condition (legacy / simple rules). Retained for backward compatibility.
|
|
54
|
+
* When `conditions` is present and non-empty it takes precedence over `condition`.
|
|
55
|
+
*/
|
|
56
|
+
condition?: SurveyLogicCondition;
|
|
57
|
+
/**
|
|
58
|
+
* Compound conditions (PostHog-parity, added). Combined per `condition_logic`.
|
|
59
|
+
* Evaluators prefer this over `condition` when present.
|
|
60
|
+
*/
|
|
61
|
+
conditions?: SurveyLogicCondition[];
|
|
62
|
+
/** How to combine `conditions`. Defaults to 'and'. */
|
|
63
|
+
condition_logic?: 'and' | 'or';
|
|
64
|
+
action_type: 'skip_to_question' | 'skip_to_page' | 'end_survey';
|
|
65
|
+
target_id?: string;
|
|
66
|
+
rule_order: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface SurveyQuestion {
|
|
70
|
+
id: string;
|
|
71
|
+
type: SurveyQuestionType;
|
|
72
|
+
text: string;
|
|
73
|
+
description: string | null;
|
|
74
|
+
is_required: boolean;
|
|
75
|
+
question_order: number;
|
|
76
|
+
config: Record<string, unknown>;
|
|
77
|
+
settings: Record<string, unknown>;
|
|
78
|
+
choices: SurveyChoice[];
|
|
79
|
+
logic_rules?: SurveyLogicRule[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface SurveyPage {
|
|
83
|
+
id: string;
|
|
84
|
+
title: string | null;
|
|
85
|
+
description: string | null;
|
|
86
|
+
page_order: number;
|
|
87
|
+
settings: Record<string, unknown>;
|
|
88
|
+
questions: SurveyQuestion[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface SurveyMobileDisplayConfig {
|
|
92
|
+
displayStyle?: 'full_screen' | 'sheet' | 'push_navigation' | 'no_transition' | 'side_drawer' | 'overlay_popup';
|
|
93
|
+
allowScrolling?: boolean;
|
|
94
|
+
interceptBackButton?: boolean;
|
|
95
|
+
accessControl?: 'restricted' | 'unrestricted';
|
|
96
|
+
safeAreaInsets?: boolean;
|
|
97
|
+
sheetConfig?: { heightPercent?: number; cornerRadius?: number };
|
|
98
|
+
popupConfig?: { widthPercent?: number; heightPercent?: number; cornerRadius?: number };
|
|
99
|
+
drawerConfig?: { widthPercent?: number; cornerRadius?: number };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface SurveyConfiguration {
|
|
103
|
+
id: string;
|
|
104
|
+
name: string;
|
|
105
|
+
description: string | null;
|
|
106
|
+
type: string;
|
|
107
|
+
settings: SurveySettings;
|
|
108
|
+
pages: SurveyPage[];
|
|
109
|
+
mobile_display?: SurveyMobileDisplayConfig;
|
|
110
|
+
version?: number;
|
|
111
|
+
variant_group?: string | null;
|
|
112
|
+
variant_name?: string | null;
|
|
113
|
+
should_show?: boolean;
|
|
114
|
+
presentation_percentage?: number;
|
|
115
|
+
/** Display/targeting conditions (PostHog-parity, added). */
|
|
116
|
+
display_conditions?: SurveyDisplayConditions;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Display/targeting conditions evaluated client-side before a survey is shown
|
|
121
|
+
* (PostHog-parity, added). All present conditions must pass (logical AND).
|
|
122
|
+
*/
|
|
123
|
+
export interface SurveyDisplayConditions {
|
|
124
|
+
/** Restrict to device categories. Empty/undefined = all devices. */
|
|
125
|
+
device_types?: SurveyDeviceType[];
|
|
126
|
+
/** Only show when a feature flag is enabled (optionally a specific variant). */
|
|
127
|
+
feature_flag?: { key: string; variant?: string | null };
|
|
128
|
+
/** Web/screen targeting: match the current URL/route. */
|
|
129
|
+
url_match?: { operator: 'exact' | 'contains' | 'regex' | 'not_contains'; value: string };
|
|
130
|
+
/** Only show after an event fires during the session (optional property filters). */
|
|
131
|
+
event_trigger?: { event_name: string; property_filters?: Record<string, unknown> };
|
|
132
|
+
/** Delay before showing, in milliseconds. */
|
|
133
|
+
delay_ms?: number;
|
|
134
|
+
[key: string]: unknown;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type SurveyDeviceType = 'phone' | 'tablet' | 'desktop' | 'tv' | 'watch';
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Recurrence/repeat policy (PostHog-parity, added).
|
|
141
|
+
* `show_once` is the legacy single-show flag; `recurring` supersedes it when enabled.
|
|
142
|
+
*/
|
|
143
|
+
export interface SurveyRecurrence {
|
|
144
|
+
enabled: boolean;
|
|
145
|
+
/** Maximum number of times the survey may be shown to a user. */
|
|
146
|
+
max_repeats?: number;
|
|
147
|
+
/** Minimum days between repeated showings. */
|
|
148
|
+
interval_days?: number;
|
|
149
|
+
/** Re-show every time the trigger event fires (ignores interval_days when true). */
|
|
150
|
+
on_every_trigger?: boolean;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface SurveySettings {
|
|
154
|
+
show_once?: boolean;
|
|
155
|
+
shuffle_options?: boolean;
|
|
156
|
+
/** Randomize question order (PostHog-parity, added). */
|
|
157
|
+
shuffle_questions?: boolean;
|
|
158
|
+
/** Recurring/repeat policy (PostHog-parity, added). */
|
|
159
|
+
recurring?: SurveyRecurrence;
|
|
160
|
+
/** Global wait period: don't show if the user saw ANY survey in the last N days (added). */
|
|
161
|
+
wait_period_days?: number;
|
|
162
|
+
presentation_percentage?: number;
|
|
163
|
+
locale?: string | null;
|
|
164
|
+
theme?: SurveyTheme;
|
|
165
|
+
thankYouMessage?: { title: string; body?: string | null } | null;
|
|
166
|
+
welcomeScreen?: {
|
|
167
|
+
title: string;
|
|
168
|
+
body?: string | null;
|
|
169
|
+
buttonText?: string | null;
|
|
170
|
+
} | null;
|
|
171
|
+
[key: string]: unknown;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface SurveyTheme {
|
|
175
|
+
primaryColor?: string;
|
|
176
|
+
backgroundColor?: string;
|
|
177
|
+
textColor?: string;
|
|
178
|
+
borderRadius?: number;
|
|
179
|
+
fontFamily?: string;
|
|
180
|
+
[key: string]: unknown;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ============= Survey Answers (for bdsurvey-submit) =============
|
|
184
|
+
|
|
185
|
+
export interface SurveyAnswer {
|
|
186
|
+
question_id: string;
|
|
187
|
+
choice_id?: string | null;
|
|
188
|
+
answer_text?: string | null;
|
|
189
|
+
answer_number?: number | null;
|
|
190
|
+
answer_json?: Record<string, unknown> | null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface SurveyContext {
|
|
194
|
+
customer_id?: string;
|
|
195
|
+
anonymous_id?: string;
|
|
196
|
+
platform?: string;
|
|
197
|
+
session_id?: string;
|
|
198
|
+
device_info?: Record<string, unknown>;
|
|
199
|
+
custom_variables?: Record<string, string>;
|
|
200
|
+
[key: string]: unknown;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface SurveySubmitResult {
|
|
204
|
+
respondent_id: string;
|
|
205
|
+
survey_id: string;
|
|
206
|
+
status: string;
|
|
207
|
+
/** Submitted answers — included so host apps can implement conditional post-submit flows */
|
|
208
|
+
answers?: SurveyAnswer[];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ============= Presentation Options =============
|
|
212
|
+
|
|
213
|
+
export interface SurveyPresentationOptions {
|
|
214
|
+
customerId?: string;
|
|
215
|
+
anonymousId?: string;
|
|
216
|
+
platform?: string;
|
|
217
|
+
customVariables?: Record<string, string>;
|
|
218
|
+
onPresent?: (surveyId: string) => void;
|
|
219
|
+
onDismiss?: (surveyId: string) => void;
|
|
220
|
+
onComplete?: (surveyId: string, result: SurveySubmitResult) => void;
|
|
221
|
+
onError?: (error: Error) => void;
|
|
222
|
+
onAnswerChanged?: (answer: SurveyAnswer) => void;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ============= Cache Entry =============
|
|
226
|
+
|
|
227
|
+
export interface SurveyCacheEntry {
|
|
228
|
+
config: SurveyConfiguration;
|
|
229
|
+
fetchedAt: number;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ============= Survey List (from bdsurvey-list) =============
|
|
233
|
+
|
|
234
|
+
export interface SurveyListItem {
|
|
235
|
+
id: string;
|
|
236
|
+
name: string;
|
|
237
|
+
description: string | null;
|
|
238
|
+
type: string;
|
|
239
|
+
status: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface SurveyListResult {
|
|
243
|
+
surveys: SurveyListItem[];
|
|
244
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Ambient module declarations for OPTIONAL native peer dependencies that are
|
|
2
|
+
// not installed by default in this package. These are runtime-optional deps;
|
|
3
|
+
// consumers who use the notification / icon features are expected to install
|
|
4
|
+
// them. Declaring them as `any` modules is the standard approach for optional
|
|
5
|
+
// peers so the type build does not fail when they are absent.
|
|
6
|
+
|
|
7
|
+
declare module 'react-native-push-notification';
|
|
8
|
+
declare module '@react-native-community/push-notification-ios';
|
|
9
|
+
declare module 'lucide-react-native';
|