@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,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a, _b;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.initializeReviewTiming = initializeReviewTiming;
|
|
5
|
+
exports.recordReviewSession = recordReviewSession;
|
|
6
|
+
exports.recordSignificantEvent = recordSignificantEvent;
|
|
7
|
+
exports.shouldRequestReview = shouldRequestReview;
|
|
8
|
+
exports.requestReview = requestReview;
|
|
9
|
+
exports.canRequestReview = canRequestReview;
|
|
10
|
+
const react_native_1 = require("react-native");
|
|
11
|
+
const ReviewAnalytics_1 = require("./ReviewAnalytics");
|
|
12
|
+
const ReviewTimingManager_1 = require("./ReviewTimingManager");
|
|
13
|
+
const BillDogConfiguration_1 = require("../BillDogConfiguration");
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
15
|
+
const RN = (() => {
|
|
16
|
+
try {
|
|
17
|
+
return require('react-native');
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
})();
|
|
23
|
+
const native = (_b = (_a = RN === null || RN === void 0 ? void 0 : RN.NativeModules) === null || _a === void 0 ? void 0 : _a.BillDog) !== null && _b !== void 0 ? _b : null;
|
|
24
|
+
let timingManager = null;
|
|
25
|
+
/**
|
|
26
|
+
* Optional native in-app-review module loader.
|
|
27
|
+
*
|
|
28
|
+
* `react-native` core does NOT export `StoreReview`/`InAppReview` — the previous
|
|
29
|
+
* implementation referenced non-existent exports, so the native prompt never ran
|
|
30
|
+
* and every request silently fell through to a (placeholder) store URL. The real
|
|
31
|
+
* native prompt requires `react-native-in-app-review` (cross-platform: iOS
|
|
32
|
+
* `SKStoreReviewController` + Android Play In-App Review), declared as an OPTIONAL
|
|
33
|
+
* peer dependency. Memoized; resolves `null` when the host app hasn't installed
|
|
34
|
+
* it (we then fall back to the configured store page).
|
|
35
|
+
*/
|
|
36
|
+
let inAppReviewModule = undefined;
|
|
37
|
+
async function loadInAppReview() {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
if (inAppReviewModule !== undefined)
|
|
40
|
+
return inAppReviewModule;
|
|
41
|
+
try {
|
|
42
|
+
// STATIC require: React Native's Metro bundler rejects dynamic
|
|
43
|
+
// `require(variable)` ("Invalid call … require(s)"), so the optional native
|
|
44
|
+
// module must be referenced by a literal specifier. The try/catch keeps it
|
|
45
|
+
// soft at runtime; the @ts-ignore avoids TS2307 at SDK build time when the
|
|
46
|
+
// optional peer is absent. Consumers who want native in-app review install
|
|
47
|
+
// `react-native-in-app-review` (declared in peerDependenciesMeta as optional).
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
49
|
+
// @ts-ignore optional peer dependency, may be absent at build time
|
|
50
|
+
const mod = require('react-native-in-app-review');
|
|
51
|
+
inAppReviewModule = (_b = ((_a = mod === null || mod === void 0 ? void 0 : mod.default) !== null && _a !== void 0 ? _a : mod)) !== null && _b !== void 0 ? _b : null;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
inAppReviewModule = null;
|
|
55
|
+
}
|
|
56
|
+
return inAppReviewModule;
|
|
57
|
+
}
|
|
58
|
+
/** True only when a store-page fallback is actually possible (IDs configured). */
|
|
59
|
+
function hasStoreFallback() {
|
|
60
|
+
const cfg = BillDogConfiguration_1.BillDogConfiguration.config;
|
|
61
|
+
return react_native_1.Platform.OS === 'ios' ? !!cfg.appStoreId : react_native_1.Platform.OS === 'android' ? !!cfg.androidPackageName : false;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Initialize review timing manager with configuration
|
|
65
|
+
* Call this on app launch to enable smart timing
|
|
66
|
+
*
|
|
67
|
+
* @param config - Timing configuration options
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import { initializeReviewTiming } from '@billdog.io/react-native';
|
|
72
|
+
*
|
|
73
|
+
* await initializeReviewTiming({
|
|
74
|
+
* minDaysSinceInstall: 7,
|
|
75
|
+
* minSessionCount: 5,
|
|
76
|
+
* minSignificantEvents: 3,
|
|
77
|
+
* enableAutomaticTiming: true,
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
async function initializeReviewTiming(config) {
|
|
82
|
+
if (native && typeof native.initializeReviewTiming === 'function') {
|
|
83
|
+
try {
|
|
84
|
+
await native.initializeReviewTiming(config);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error('Error invoking native initializeReviewTiming:', error);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
timingManager = new ReviewTimingManager_1.ReviewTimingManager(config);
|
|
92
|
+
await timingManager.initialize();
|
|
93
|
+
await timingManager.recordSession();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Record a new app session (call on app launch)
|
|
97
|
+
*/
|
|
98
|
+
async function recordReviewSession() {
|
|
99
|
+
if (native && typeof native.recordReviewSession === 'function') {
|
|
100
|
+
try {
|
|
101
|
+
await native.recordReviewSession();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
catch (e) { }
|
|
105
|
+
}
|
|
106
|
+
if (timingManager) {
|
|
107
|
+
await timingManager.recordSession();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Record a significant event (e.g., purchase, completed action)
|
|
112
|
+
* These events help determine optimal timing for review requests
|
|
113
|
+
*/
|
|
114
|
+
async function recordSignificantEvent() {
|
|
115
|
+
if (native && typeof native.recordSignificantEvent === 'function') {
|
|
116
|
+
try {
|
|
117
|
+
await native.recordSignificantEvent();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
catch (e) { }
|
|
121
|
+
}
|
|
122
|
+
if (timingManager) {
|
|
123
|
+
await timingManager.recordSignificantEvent();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if it's a good time to request a review based on timing logic
|
|
128
|
+
*
|
|
129
|
+
* @returns Object with shouldRequest flag, reason, and timing data
|
|
130
|
+
*/
|
|
131
|
+
async function shouldRequestReview() {
|
|
132
|
+
if (native && typeof native.shouldRequestReview === 'function') {
|
|
133
|
+
try {
|
|
134
|
+
const should = await native.shouldRequestReview();
|
|
135
|
+
return { shouldRequest: should, reason: should ? 'All timing conditions met' : 'Timing conditions not met' };
|
|
136
|
+
}
|
|
137
|
+
catch (e) { }
|
|
138
|
+
}
|
|
139
|
+
if (!timingManager) {
|
|
140
|
+
return { shouldRequest: true, reason: 'Timing manager not initialized' };
|
|
141
|
+
}
|
|
142
|
+
return await timingManager.shouldRequestReview();
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Request in-app review from the user
|
|
146
|
+
* Optionally checks timing logic before showing
|
|
147
|
+
*
|
|
148
|
+
* @param options - Options for review request
|
|
149
|
+
* @param options.checkTiming - Whether to check timing logic before showing (default: true)
|
|
150
|
+
* @returns Promise with success status and whether review was shown
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* import { requestReview } from '@billdog.io/react-native';
|
|
155
|
+
*
|
|
156
|
+
* const { success, shown } = await requestReview();
|
|
157
|
+
* if (shown) {
|
|
158
|
+
* console.log('Review prompt shown to user');
|
|
159
|
+
* }
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
async function requestReview(options = {}) {
|
|
163
|
+
const { checkTiming = true } = options;
|
|
164
|
+
const startTime = Date.now();
|
|
165
|
+
try {
|
|
166
|
+
if (native && typeof native.requestReview === 'function') {
|
|
167
|
+
if (checkTiming) {
|
|
168
|
+
const timingCheck = await shouldRequestReview();
|
|
169
|
+
if (!timingCheck.shouldRequest) {
|
|
170
|
+
console.log('Review request skipped:', timingCheck.reason);
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
shown: false,
|
|
174
|
+
reason: timingCheck.reason
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const result = await native.requestReview();
|
|
179
|
+
await ReviewAnalytics_1.ReviewAnalytics.trackEvent({
|
|
180
|
+
eventType: 'shown',
|
|
181
|
+
platform: react_native_1.Platform.OS === 'ios' ? 'ios' : react_native_1.Platform.OS === 'android' ? 'android' : 'other',
|
|
182
|
+
success: !!(result === null || result === void 0 ? void 0 : result.success),
|
|
183
|
+
timeToShowMs: Date.now() - startTime,
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
success: !!(result === null || result === void 0 ? void 0 : result.success),
|
|
187
|
+
shown: !!(result === null || result === void 0 ? void 0 : result.shown),
|
|
188
|
+
reason: result === null || result === void 0 ? void 0 : result.reason
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// Check timing logic if enabled
|
|
192
|
+
if (checkTiming && timingManager) {
|
|
193
|
+
const timingCheck = await timingManager.shouldRequestReview();
|
|
194
|
+
if (!timingCheck.shouldRequest) {
|
|
195
|
+
console.log('Review request skipped:', timingCheck.reason);
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
shown: false,
|
|
199
|
+
reason: timingCheck.reason
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Record that we're requesting a review
|
|
204
|
+
if (timingManager) {
|
|
205
|
+
await timingManager.recordReviewRequested();
|
|
206
|
+
}
|
|
207
|
+
// Track request event
|
|
208
|
+
await ReviewAnalytics_1.ReviewAnalytics.trackEvent({
|
|
209
|
+
eventType: 'requested',
|
|
210
|
+
platform: react_native_1.Platform.OS === 'ios' ? 'ios' : react_native_1.Platform.OS === 'android' ? 'android' : 'other',
|
|
211
|
+
});
|
|
212
|
+
// Native in-app review — iOS SKStoreReviewController / Android Play In-App
|
|
213
|
+
// Review — via the optional `react-native-in-app-review` module (one call
|
|
214
|
+
// handles both platforms). Audit fix: the old code imported StoreReview/
|
|
215
|
+
// InAppReview from `react-native` (no such exports) so this branch never ran.
|
|
216
|
+
const InAppReview = await loadInAppReview();
|
|
217
|
+
if (InAppReview &&
|
|
218
|
+
typeof InAppReview.isAvailable === 'function' &&
|
|
219
|
+
InAppReview.isAvailable() &&
|
|
220
|
+
typeof InAppReview.RequestInAppReview === 'function') {
|
|
221
|
+
// Resolves true when the flow launched/finished. Note: neither platform
|
|
222
|
+
// reports whether the dialog displayed or whether the user submitted a
|
|
223
|
+
// review, so `shown` is best-effort. We do NOT auto-record "completed"
|
|
224
|
+
// (that permanently blocked future prompts and made maxRequestsPerUser
|
|
225
|
+
// unreachable); recordReviewRequested() above is the correct cap.
|
|
226
|
+
const flowFinished = await InAppReview.RequestInAppReview();
|
|
227
|
+
await ReviewAnalytics_1.ReviewAnalytics.trackEvent({
|
|
228
|
+
eventType: 'shown',
|
|
229
|
+
platform: react_native_1.Platform.OS === 'ios' ? 'ios' : react_native_1.Platform.OS === 'android' ? 'android' : 'other',
|
|
230
|
+
success: !!flowFinished,
|
|
231
|
+
timeToShowMs: Date.now() - startTime,
|
|
232
|
+
});
|
|
233
|
+
return { success: !!flowFinished, shown: !!flowFinished };
|
|
234
|
+
}
|
|
235
|
+
// Fallback: open store page (only when store IDs are configured)
|
|
236
|
+
const result = await openStoreForReview();
|
|
237
|
+
if (result.success) {
|
|
238
|
+
await ReviewAnalytics_1.ReviewAnalytics.trackEvent({
|
|
239
|
+
eventType: 'shown',
|
|
240
|
+
platform: react_native_1.Platform.OS === 'ios' ? 'ios' : react_native_1.Platform.OS === 'android' ? 'android' : 'other',
|
|
241
|
+
success: true,
|
|
242
|
+
timeToShowMs: Date.now() - startTime,
|
|
243
|
+
});
|
|
244
|
+
// Audit fix — removed the auto recordReviewCompleted() call (see above): it
|
|
245
|
+
// permanently blocked future prompts and made maxRequestsPerUser unreachable.
|
|
246
|
+
// recordReviewRequested() already counted this attempt toward the cap.
|
|
247
|
+
}
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
console.error('Error requesting review:', error);
|
|
252
|
+
// Track error event
|
|
253
|
+
await ReviewAnalytics_1.ReviewAnalytics.trackEvent({
|
|
254
|
+
eventType: 'error',
|
|
255
|
+
platform: react_native_1.Platform.OS === 'ios' ? 'ios' : react_native_1.Platform.OS === 'android' ? 'android' : 'other',
|
|
256
|
+
success: false,
|
|
257
|
+
errorMessage: error instanceof Error ? error.message : 'Unknown error',
|
|
258
|
+
});
|
|
259
|
+
return { success: false, shown: false };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Check if review request is available
|
|
264
|
+
* Respects platform rate limiting
|
|
265
|
+
*
|
|
266
|
+
* @returns Promise with availability status
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```typescript
|
|
270
|
+
* import { canRequestReview } from '@billdog.io/react-native';
|
|
271
|
+
*
|
|
272
|
+
* const { available } = await canRequestReview();
|
|
273
|
+
* if (available) {
|
|
274
|
+
* await requestReview();
|
|
275
|
+
* }
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
async function canRequestReview() {
|
|
279
|
+
if (native && typeof native.canRequestReview === 'function') {
|
|
280
|
+
try {
|
|
281
|
+
const available = await native.canRequestReview();
|
|
282
|
+
return { available: !!available };
|
|
283
|
+
}
|
|
284
|
+
catch (e) { }
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const InAppReview = await loadInAppReview();
|
|
288
|
+
if (InAppReview && typeof InAppReview.isAvailable === 'function') {
|
|
289
|
+
return { available: !!InAppReview.isAvailable() };
|
|
290
|
+
}
|
|
291
|
+
// No native module installed — only the store-page fallback is possible,
|
|
292
|
+
// and only if store IDs are configured. (Previously this returned `true`
|
|
293
|
+
// unconditionally even though the fallback URL was a placeholder.)
|
|
294
|
+
return { available: hasStoreFallback() };
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
console.error('Error checking review availability:', error);
|
|
298
|
+
return { available: false };
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Open the app store for manual review
|
|
303
|
+
* Used as fallback when native review is unavailable
|
|
304
|
+
*/
|
|
305
|
+
async function openStoreForReview() {
|
|
306
|
+
try {
|
|
307
|
+
// Audit fix — source real store IDs from BillDogConfiguration instead of the
|
|
308
|
+
// old `YOUR_APP_ID` / `YOUR_PACKAGE_NAME` placeholders (which produced a
|
|
309
|
+
// broken deep-link). If neither a native module nor a configured ID exists,
|
|
310
|
+
// report unavailable rather than opening a dead URL.
|
|
311
|
+
const cfg = BillDogConfiguration_1.BillDogConfiguration.config;
|
|
312
|
+
let storeUrl = null;
|
|
313
|
+
if (react_native_1.Platform.OS === 'ios') {
|
|
314
|
+
if (cfg.appStoreId) {
|
|
315
|
+
storeUrl = `itms-apps://itunes.apple.com/app/id${cfg.appStoreId}?action=write-review`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
else if (react_native_1.Platform.OS === 'android') {
|
|
319
|
+
if (cfg.androidPackageName) {
|
|
320
|
+
storeUrl = `market://details?id=${cfg.androidPackageName}`;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (!storeUrl) {
|
|
324
|
+
console.warn('[BillDog] Cannot request a review: install `react-native-in-app-review` for the native prompt, ' +
|
|
325
|
+
'or set `appStoreId` (iOS) / `androidPackageName` (Android) in BillDogConfiguration.configure() for the store-page fallback.');
|
|
326
|
+
return { success: false, shown: false };
|
|
327
|
+
}
|
|
328
|
+
const canOpen = await react_native_1.Linking.canOpenURL(storeUrl);
|
|
329
|
+
if (canOpen) {
|
|
330
|
+
await react_native_1.Linking.openURL(storeUrl);
|
|
331
|
+
return { success: true, shown: true };
|
|
332
|
+
}
|
|
333
|
+
return { success: false, shown: false };
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
console.error('Error opening store:', error);
|
|
337
|
+
return { success: false, shown: false };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for review timing logic
|
|
3
|
+
*/
|
|
4
|
+
export interface ReviewTimingConfig {
|
|
5
|
+
/** Minimum number of days since app install before requesting review */
|
|
6
|
+
minDaysSinceInstall?: number;
|
|
7
|
+
/** Minimum number of app sessions before requesting review */
|
|
8
|
+
minSessionCount?: number;
|
|
9
|
+
/** Minimum number of significant events (e.g., purchases, completed actions) */
|
|
10
|
+
minSignificantEvents?: number;
|
|
11
|
+
/** Days to wait after a user dismisses a review prompt */
|
|
12
|
+
cooldownAfterDismissal?: number;
|
|
13
|
+
/** Maximum number of review requests to show to a user */
|
|
14
|
+
maxRequestsPerUser?: number;
|
|
15
|
+
/** Enable automatic timing checks */
|
|
16
|
+
enableAutomaticTiming?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Timing data stored locally
|
|
20
|
+
*/
|
|
21
|
+
interface ReviewTimingData {
|
|
22
|
+
installDate: string;
|
|
23
|
+
sessionCount: number;
|
|
24
|
+
significantEventCount: number;
|
|
25
|
+
lastRequestDate?: string;
|
|
26
|
+
lastDismissalDate?: string;
|
|
27
|
+
totalRequestCount: number;
|
|
28
|
+
lastReviewCompletedDate?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Manager for smart review timing logic
|
|
32
|
+
* Determines optimal moments to request reviews based on user behavior
|
|
33
|
+
*/
|
|
34
|
+
export declare class ReviewTimingManager {
|
|
35
|
+
private config;
|
|
36
|
+
private timingData;
|
|
37
|
+
constructor(config?: ReviewTimingConfig);
|
|
38
|
+
/**
|
|
39
|
+
* Initialize timing data (call on app launch)
|
|
40
|
+
*/
|
|
41
|
+
initialize(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Record a new app session
|
|
44
|
+
*/
|
|
45
|
+
recordSession(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Record a significant event (e.g., purchase, completed action)
|
|
48
|
+
*/
|
|
49
|
+
recordSignificantEvent(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Record that a review was requested
|
|
52
|
+
*/
|
|
53
|
+
recordReviewRequested(): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Record that a review was dismissed by the user
|
|
56
|
+
*/
|
|
57
|
+
recordReviewDismissed(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Record that a review was completed by the user
|
|
60
|
+
*/
|
|
61
|
+
recordReviewCompleted(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Check if it's a good time to request a review
|
|
64
|
+
* Returns true if all timing conditions are met
|
|
65
|
+
*/
|
|
66
|
+
shouldRequestReview(): Promise<{
|
|
67
|
+
shouldRequest: boolean;
|
|
68
|
+
reason?: string;
|
|
69
|
+
timingData?: ReviewTimingData;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Get current timing data
|
|
73
|
+
*/
|
|
74
|
+
getTimingData(): Promise<ReviewTimingData | null>;
|
|
75
|
+
/**
|
|
76
|
+
* Reset timing data (useful for testing)
|
|
77
|
+
*/
|
|
78
|
+
resetTimingData(): Promise<void>;
|
|
79
|
+
private saveTimingData;
|
|
80
|
+
private getDaysSince;
|
|
81
|
+
}
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ReviewTimingManager = void 0;
|
|
7
|
+
const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
|
|
8
|
+
const STORAGE_KEY = '@billdog_review_timing';
|
|
9
|
+
/**
|
|
10
|
+
* Default timing configuration
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_CONFIG = {
|
|
13
|
+
minDaysSinceInstall: 7,
|
|
14
|
+
minSessionCount: 5,
|
|
15
|
+
minSignificantEvents: 3,
|
|
16
|
+
cooldownAfterDismissal: 30,
|
|
17
|
+
maxRequestsPerUser: 3,
|
|
18
|
+
enableAutomaticTiming: true,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Manager for smart review timing logic
|
|
22
|
+
* Determines optimal moments to request reviews based on user behavior
|
|
23
|
+
*/
|
|
24
|
+
class ReviewTimingManager {
|
|
25
|
+
constructor(config = {}) {
|
|
26
|
+
this.timingData = null;
|
|
27
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Initialize timing data (call on app launch)
|
|
31
|
+
*/
|
|
32
|
+
async initialize() {
|
|
33
|
+
try {
|
|
34
|
+
const stored = await async_storage_1.default.getItem(STORAGE_KEY);
|
|
35
|
+
if (stored) {
|
|
36
|
+
this.timingData = JSON.parse(stored);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// First time - create initial data
|
|
40
|
+
this.timingData = {
|
|
41
|
+
installDate: new Date().toISOString(),
|
|
42
|
+
sessionCount: 0,
|
|
43
|
+
significantEventCount: 0,
|
|
44
|
+
totalRequestCount: 0,
|
|
45
|
+
};
|
|
46
|
+
await this.saveTimingData();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error('Error initializing review timing:', error);
|
|
51
|
+
// Fallback to in-memory only
|
|
52
|
+
this.timingData = {
|
|
53
|
+
installDate: new Date().toISOString(),
|
|
54
|
+
sessionCount: 0,
|
|
55
|
+
significantEventCount: 0,
|
|
56
|
+
totalRequestCount: 0,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Record a new app session
|
|
62
|
+
*/
|
|
63
|
+
async recordSession() {
|
|
64
|
+
if (!this.timingData)
|
|
65
|
+
await this.initialize();
|
|
66
|
+
if (this.timingData) {
|
|
67
|
+
this.timingData.sessionCount++;
|
|
68
|
+
await this.saveTimingData();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Record a significant event (e.g., purchase, completed action)
|
|
73
|
+
*/
|
|
74
|
+
async recordSignificantEvent() {
|
|
75
|
+
if (!this.timingData)
|
|
76
|
+
await this.initialize();
|
|
77
|
+
if (this.timingData) {
|
|
78
|
+
this.timingData.significantEventCount++;
|
|
79
|
+
await this.saveTimingData();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Record that a review was requested
|
|
84
|
+
*/
|
|
85
|
+
async recordReviewRequested() {
|
|
86
|
+
if (!this.timingData)
|
|
87
|
+
await this.initialize();
|
|
88
|
+
if (this.timingData) {
|
|
89
|
+
this.timingData.lastRequestDate = new Date().toISOString();
|
|
90
|
+
this.timingData.totalRequestCount++;
|
|
91
|
+
await this.saveTimingData();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Record that a review was dismissed by the user
|
|
96
|
+
*/
|
|
97
|
+
async recordReviewDismissed() {
|
|
98
|
+
if (!this.timingData)
|
|
99
|
+
await this.initialize();
|
|
100
|
+
if (this.timingData) {
|
|
101
|
+
this.timingData.lastDismissalDate = new Date().toISOString();
|
|
102
|
+
await this.saveTimingData();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Record that a review was completed by the user
|
|
107
|
+
*/
|
|
108
|
+
async recordReviewCompleted() {
|
|
109
|
+
if (!this.timingData)
|
|
110
|
+
await this.initialize();
|
|
111
|
+
if (this.timingData) {
|
|
112
|
+
this.timingData.lastReviewCompletedDate = new Date().toISOString();
|
|
113
|
+
await this.saveTimingData();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if it's a good time to request a review
|
|
118
|
+
* Returns true if all timing conditions are met
|
|
119
|
+
*/
|
|
120
|
+
async shouldRequestReview() {
|
|
121
|
+
if (!this.config.enableAutomaticTiming) {
|
|
122
|
+
return { shouldRequest: true, reason: 'Automatic timing disabled' };
|
|
123
|
+
}
|
|
124
|
+
if (!this.timingData)
|
|
125
|
+
await this.initialize();
|
|
126
|
+
if (!this.timingData) {
|
|
127
|
+
return { shouldRequest: false, reason: 'Timing data not available' };
|
|
128
|
+
}
|
|
129
|
+
// Check if user has already completed a review
|
|
130
|
+
if (this.timingData.lastReviewCompletedDate) {
|
|
131
|
+
return {
|
|
132
|
+
shouldRequest: false,
|
|
133
|
+
reason: 'User has already completed a review',
|
|
134
|
+
timingData: this.timingData
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// Check max requests limit
|
|
138
|
+
if (this.timingData.totalRequestCount >= this.config.maxRequestsPerUser) {
|
|
139
|
+
return {
|
|
140
|
+
shouldRequest: false,
|
|
141
|
+
reason: `Maximum review requests (${this.config.maxRequestsPerUser}) reached`,
|
|
142
|
+
timingData: this.timingData
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Check days since install
|
|
146
|
+
const daysSinceInstall = this.getDaysSince(new Date(this.timingData.installDate));
|
|
147
|
+
if (daysSinceInstall < this.config.minDaysSinceInstall) {
|
|
148
|
+
return {
|
|
149
|
+
shouldRequest: false,
|
|
150
|
+
reason: `Only ${daysSinceInstall} days since install (need ${this.config.minDaysSinceInstall})`,
|
|
151
|
+
timingData: this.timingData
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// Check session count
|
|
155
|
+
if (this.timingData.sessionCount < this.config.minSessionCount) {
|
|
156
|
+
return {
|
|
157
|
+
shouldRequest: false,
|
|
158
|
+
reason: `Only ${this.timingData.sessionCount} sessions (need ${this.config.minSessionCount})`,
|
|
159
|
+
timingData: this.timingData
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// Check significant events
|
|
163
|
+
if (this.timingData.significantEventCount < this.config.minSignificantEvents) {
|
|
164
|
+
return {
|
|
165
|
+
shouldRequest: false,
|
|
166
|
+
reason: `Only ${this.timingData.significantEventCount} significant events (need ${this.config.minSignificantEvents})`,
|
|
167
|
+
timingData: this.timingData
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// Check cooldown after dismissal
|
|
171
|
+
if (this.timingData.lastDismissalDate) {
|
|
172
|
+
const daysSinceDismissal = this.getDaysSince(new Date(this.timingData.lastDismissalDate));
|
|
173
|
+
if (daysSinceDismissal < this.config.cooldownAfterDismissal) {
|
|
174
|
+
return {
|
|
175
|
+
shouldRequest: false,
|
|
176
|
+
reason: `Only ${daysSinceDismissal} days since last dismissal (need ${this.config.cooldownAfterDismissal})`,
|
|
177
|
+
timingData: this.timingData
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// All conditions met!
|
|
182
|
+
return {
|
|
183
|
+
shouldRequest: true,
|
|
184
|
+
reason: 'All timing conditions met',
|
|
185
|
+
timingData: this.timingData
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get current timing data
|
|
190
|
+
*/
|
|
191
|
+
async getTimingData() {
|
|
192
|
+
if (!this.timingData)
|
|
193
|
+
await this.initialize();
|
|
194
|
+
return this.timingData;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Reset timing data (useful for testing)
|
|
198
|
+
*/
|
|
199
|
+
async resetTimingData() {
|
|
200
|
+
this.timingData = {
|
|
201
|
+
installDate: new Date().toISOString(),
|
|
202
|
+
sessionCount: 0,
|
|
203
|
+
significantEventCount: 0,
|
|
204
|
+
totalRequestCount: 0,
|
|
205
|
+
};
|
|
206
|
+
await this.saveTimingData();
|
|
207
|
+
}
|
|
208
|
+
async saveTimingData() {
|
|
209
|
+
if (!this.timingData)
|
|
210
|
+
return;
|
|
211
|
+
try {
|
|
212
|
+
await async_storage_1.default.setItem(STORAGE_KEY, JSON.stringify(this.timingData));
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
console.error('Error saving review timing data:', error);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
getDaysSince(date) {
|
|
219
|
+
const now = new Date();
|
|
220
|
+
const diffMs = now.getTime() - date.getTime();
|
|
221
|
+
return Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
exports.ReviewTimingManager = ReviewTimingManager;
|