@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.
Files changed (309) hide show
  1. package/BillDogReactNative.podspec +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +330 -0
  4. package/android/build.gradle +99 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/kotlin/io/billdog/reactnative/BillDogABTestModule.kt +317 -0
  7. package/android/src/main/kotlin/io/billdog/reactnative/BillDogAnalyticsModule.kt +389 -0
  8. package/android/src/main/kotlin/io/billdog/reactnative/BillDogModule.kt +817 -0
  9. package/android/src/main/kotlin/io/billdog/reactnative/BillDogPaywallViewManager.kt +383 -0
  10. package/android/src/main/kotlin/io/billdog/reactnative/BillDogSurveyModule.kt +427 -0
  11. package/android/src/main/kotlin/io/billdog/reactnative/BillDogVirtualCurrencyModule.kt +407 -0
  12. package/android/src/main/kotlin/io/billdog/virtualcurrency/BillDogVirtualCurrencyPackage.kt +44 -0
  13. package/dist/AdvancedPaywallCache.d.ts +70 -0
  14. package/dist/AdvancedPaywallCache.js +344 -0
  15. package/dist/BillDog.d.ts +255 -0
  16. package/dist/BillDog.js +918 -0
  17. package/dist/BillDogConfiguration.d.ts +145 -0
  18. package/dist/BillDogConfiguration.js +229 -0
  19. package/dist/ComponentRenderer.d.ts +17 -0
  20. package/dist/ComponentRenderer.js +886 -0
  21. package/dist/LayoutEngine.d.ts +101 -0
  22. package/dist/LayoutEngine.js +394 -0
  23. package/dist/LocalizationResolver.d.ts +99 -0
  24. package/dist/LocalizationResolver.js +266 -0
  25. package/dist/NativePaywallView.d.ts +36 -0
  26. package/dist/NativePaywallView.js +59 -0
  27. package/dist/PaywallCache.d.ts +74 -0
  28. package/dist/PaywallCache.js +293 -0
  29. package/dist/PaywallStateManager.d.ts +62 -0
  30. package/dist/PaywallStateManager.js +147 -0
  31. package/dist/PaywallView.d.ts +42 -0
  32. package/dist/PaywallView.js +275 -0
  33. package/dist/ProductVariableProvider.d.ts +60 -0
  34. package/dist/ProductVariableProvider.js +239 -0
  35. package/dist/VariableResolver.d.ts +120 -0
  36. package/dist/VariableResolver.js +379 -0
  37. package/dist/WebViewPaywall.d.ts +75 -0
  38. package/dist/WebViewPaywall.js +360 -0
  39. package/dist/animations/presets.d.ts +49 -0
  40. package/dist/animations/presets.js +367 -0
  41. package/dist/components/AccoladeCpsComponent.d.ts +13 -0
  42. package/dist/components/AccoladeCpsComponent.js +248 -0
  43. package/dist/components/AccordionComponent.d.ts +10 -0
  44. package/dist/components/AccordionComponent.js +187 -0
  45. package/dist/components/AnimatedWrapper.d.ts +35 -0
  46. package/dist/components/AnimatedWrapper.js +277 -0
  47. package/dist/components/CarouselComponent.d.ts +23 -0
  48. package/dist/components/CarouselComponent.js +484 -0
  49. package/dist/components/ChartCurveComponent.d.ts +13 -0
  50. package/dist/components/ChartCurveComponent.js +333 -0
  51. package/dist/components/CheckboxComponent.d.ts +9 -0
  52. package/dist/components/CheckboxComponent.js +172 -0
  53. package/dist/components/ConfettiComponent.d.ts +9 -0
  54. package/dist/components/ConfettiComponent.js +172 -0
  55. package/dist/components/CountdownComponent.d.ts +16 -0
  56. package/dist/components/CountdownComponent.js +414 -0
  57. package/dist/components/DelayedXComponent.d.ts +12 -0
  58. package/dist/components/DelayedXComponent.js +240 -0
  59. package/dist/components/DrawerModalComponent.d.ts +20 -0
  60. package/dist/components/DrawerModalComponent.js +286 -0
  61. package/dist/components/GradientView.d.ts +38 -0
  62. package/dist/components/GradientView.js +126 -0
  63. package/dist/components/HapticFeedbackComponent.d.ts +16 -0
  64. package/dist/components/HapticFeedbackComponent.js +64 -0
  65. package/dist/components/HorizontalProductsComponent.d.ts +17 -0
  66. package/dist/components/HorizontalProductsComponent.js +284 -0
  67. package/dist/components/MultiChoiceGridComponent.d.ts +13 -0
  68. package/dist/components/MultiChoiceGridComponent.js +234 -0
  69. package/dist/components/NPSScaleComponent.d.ts +16 -0
  70. package/dist/components/NPSScaleComponent.js +260 -0
  71. package/dist/components/NavigationComponent.d.ts +26 -0
  72. package/dist/components/NavigationComponent.js +302 -0
  73. package/dist/components/OAuthButtonComponent.d.ts +20 -0
  74. package/dist/components/OAuthButtonComponent.js +237 -0
  75. package/dist/components/ProgressLoaderComponent.d.ts +13 -0
  76. package/dist/components/ProgressLoaderComponent.js +308 -0
  77. package/dist/components/ProgressStepperComponent.d.ts +18 -0
  78. package/dist/components/ProgressStepperComponent.js +228 -0
  79. package/dist/components/SegmentedPickerComponent.d.ts +10 -0
  80. package/dist/components/SegmentedPickerComponent.js +221 -0
  81. package/dist/components/ShareButtonsComponent.d.ts +21 -0
  82. package/dist/components/ShareButtonsComponent.js +265 -0
  83. package/dist/components/ShareCodeComponent.d.ts +16 -0
  84. package/dist/components/ShareCodeComponent.js +274 -0
  85. package/dist/components/SingleChoiceGridComponent.d.ts +13 -0
  86. package/dist/components/SingleChoiceGridComponent.js +210 -0
  87. package/dist/components/SliderComponent.d.ts +9 -0
  88. package/dist/components/SliderComponent.js +263 -0
  89. package/dist/components/StarRatingInputComponent.d.ts +20 -0
  90. package/dist/components/StarRatingInputComponent.js +198 -0
  91. package/dist/components/StickerCompositeComponent.d.ts +13 -0
  92. package/dist/components/StickerCompositeComponent.js +136 -0
  93. package/dist/components/TabsComponent.d.ts +19 -0
  94. package/dist/components/TabsComponent.js +431 -0
  95. package/dist/components/TextInputComponent.d.ts +18 -0
  96. package/dist/components/TextInputComponent.js +227 -0
  97. package/dist/components/TimelineComponent.d.ts +19 -0
  98. package/dist/components/TimelineComponent.js +104 -0
  99. package/dist/components/ToggleComponent.d.ts +14 -0
  100. package/dist/components/ToggleComponent.js +132 -0
  101. package/dist/components/VerticalProductsComponent.d.ts +17 -0
  102. package/dist/components/VerticalProductsComponent.js +311 -0
  103. package/dist/dynamicValues.d.ts +36 -0
  104. package/dist/dynamicValues.js +142 -0
  105. package/dist/filters/dateFilters.d.ts +50 -0
  106. package/dist/filters/dateFilters.js +390 -0
  107. package/dist/filters/index.d.ts +18 -0
  108. package/dist/filters/index.js +253 -0
  109. package/dist/hooks/useOfferings.d.ts +76 -0
  110. package/dist/hooks/useOfferings.js +99 -0
  111. package/dist/hooks/usePaywall.d.ts +27 -0
  112. package/dist/hooks/usePaywall.js +114 -0
  113. package/dist/hooks/usePlacement.d.ts +34 -0
  114. package/dist/hooks/usePlacement.js +89 -0
  115. package/dist/hooks/useSubscription.d.ts +40 -0
  116. package/dist/hooks/useSubscription.js +97 -0
  117. package/dist/index.d.ts +98 -0
  118. package/dist/index.js +198 -0
  119. package/dist/models/BillDogError.d.ts +40 -0
  120. package/dist/models/BillDogError.js +11 -0
  121. package/dist/models/CacheFetchPolicy.d.ts +8 -0
  122. package/dist/models/CacheFetchPolicy.js +4 -0
  123. package/dist/models/CustomProduct.d.ts +32 -0
  124. package/dist/models/CustomProduct.js +15 -0
  125. package/dist/models/CustomerInfo.d.ts +17 -0
  126. package/dist/models/CustomerInfo.js +11 -0
  127. package/dist/models/Delegate.d.ts +20 -0
  128. package/dist/models/Delegate.js +11 -0
  129. package/dist/models/EntitlementInfo.d.ts +19 -0
  130. package/dist/models/EntitlementInfo.js +2 -0
  131. package/dist/models/Placement.d.ts +54 -0
  132. package/dist/models/Placement.js +15 -0
  133. package/dist/models/PurchaseParams.d.ts +39 -0
  134. package/dist/models/PurchaseParams.js +60 -0
  135. package/dist/models/PurchaseResult.d.ts +30 -0
  136. package/dist/models/PurchaseResult.js +15 -0
  137. package/dist/models/RestoreResult.d.ts +30 -0
  138. package/dist/models/RestoreResult.js +22 -0
  139. package/dist/models/RestoredItem.d.ts +26 -0
  140. package/dist/models/RestoredItem.js +13 -0
  141. package/dist/modules/ABTest.d.ts +65 -0
  142. package/dist/modules/ABTest.js +113 -0
  143. package/dist/modules/Analytics.d.ts +91 -0
  144. package/dist/modules/Analytics.js +182 -0
  145. package/dist/modules/Survey.d.ts +65 -0
  146. package/dist/modules/Survey.js +82 -0
  147. package/dist/modules/VirtualCurrency.d.ts +63 -0
  148. package/dist/modules/VirtualCurrency.js +147 -0
  149. package/dist/murmur.d.ts +23 -0
  150. package/dist/murmur.js +104 -0
  151. package/dist/notifications/LiveActivitiesManager.d.ts +47 -0
  152. package/dist/notifications/LiveActivitiesManager.js +106 -0
  153. package/dist/notifications/NotificationScheduler.d.ts +55 -0
  154. package/dist/notifications/NotificationScheduler.js +199 -0
  155. package/dist/notifications/PurchaseNotificationHandler.d.ts +24 -0
  156. package/dist/notifications/PurchaseNotificationHandler.js +55 -0
  157. package/dist/notifications/useDeviceToken.d.ts +36 -0
  158. package/dist/notifications/useDeviceToken.js +97 -0
  159. package/dist/notifications/useLiveActivity.d.ts +39 -0
  160. package/dist/notifications/useLiveActivity.js +104 -0
  161. package/dist/review/ReviewAnalytics.d.ts +34 -0
  162. package/dist/review/ReviewAnalytics.js +73 -0
  163. package/dist/review/ReviewManager.d.ts +83 -0
  164. package/dist/review/ReviewManager.js +339 -0
  165. package/dist/review/ReviewTimingManager.d.ts +82 -0
  166. package/dist/review/ReviewTimingManager.js +224 -0
  167. package/dist/survey-core/types.d.ts +215 -0
  168. package/dist/survey-core/types.js +8 -0
  169. package/dist/types.d.ts +438 -0
  170. package/dist/types.js +158 -0
  171. package/dist/utils/ApiClient.d.ts +65 -0
  172. package/dist/utils/ApiClient.js +132 -0
  173. package/dist/utils/CSSConverter.d.ts +49 -0
  174. package/dist/utils/CSSConverter.js +470 -0
  175. package/dist/utils/animations.d.ts +41 -0
  176. package/dist/utils/animations.js +194 -0
  177. package/dist/utils/calendarIntegration.d.ts +36 -0
  178. package/dist/utils/calendarIntegration.js +89 -0
  179. package/dist/utils/configurationResolver.d.ts +23 -0
  180. package/dist/utils/configurationResolver.js +78 -0
  181. package/dist/utils/deviceDetection.d.ts +38 -0
  182. package/dist/utils/deviceDetection.js +59 -0
  183. package/dist/utils/hapticFeedback.d.ts +17 -0
  184. package/dist/utils/hapticFeedback.js +53 -0
  185. package/dist/utils/iconMapper.d.ts +26 -0
  186. package/dist/utils/iconMapper.js +113 -0
  187. package/dist/utils/notificationPermissions.d.ts +13 -0
  188. package/dist/utils/notificationPermissions.js +51 -0
  189. package/dist/utils/priceFormatting.d.ts +19 -0
  190. package/dist/utils/priceFormatting.js +99 -0
  191. package/dist/utils/shareSheet.d.ts +17 -0
  192. package/dist/utils/shareSheet.js +42 -0
  193. package/dist/utils/subscriptionUpgrade.d.ts +84 -0
  194. package/dist/utils/subscriptionUpgrade.js +100 -0
  195. package/dist/valueUtils.d.ts +48 -0
  196. package/dist/valueUtils.js +129 -0
  197. package/dist/xFlowTags.d.ts +18 -0
  198. package/dist/xFlowTags.js +385 -0
  199. package/ios/BillDogABTest.m +50 -0
  200. package/ios/BillDogABTest.swift +188 -0
  201. package/ios/BillDogAnalytics.m +105 -0
  202. package/ios/BillDogAnalytics.swift +324 -0
  203. package/ios/BillDogBridge.m +222 -0
  204. package/ios/BillDogBridge.swift +715 -0
  205. package/ios/BillDogLiveActivities.m +41 -0
  206. package/ios/BillDogLiveActivities.swift +104 -0
  207. package/ios/BillDogPaywallViewManager.m +39 -0
  208. package/ios/BillDogPaywallViewManager.swift +361 -0
  209. package/ios/BillDogSurvey.m +34 -0
  210. package/ios/BillDogSurvey.swift +257 -0
  211. package/ios/BillDogVirtualCurrencyBridge.m +63 -0
  212. package/ios/BillDogVirtualCurrencyBridge.swift +511 -0
  213. package/junit.xml +25 -0
  214. package/package.json +61 -0
  215. package/react-native.config.js +13 -0
  216. package/src/AdvancedPaywallCache.ts +433 -0
  217. package/src/BillDog.ts +983 -0
  218. package/src/BillDogConfiguration.ts +318 -0
  219. package/src/ComponentRenderer.tsx +1325 -0
  220. package/src/LayoutEngine.ts +459 -0
  221. package/src/LocalizationResolver.ts +311 -0
  222. package/src/NativePaywallView.tsx +132 -0
  223. package/src/PaywallCache.ts +358 -0
  224. package/src/PaywallStateManager.ts +154 -0
  225. package/src/PaywallView.tsx +328 -0
  226. package/src/ProductVariableProvider.ts +282 -0
  227. package/src/VariableResolver.ts +516 -0
  228. package/src/WebViewPaywall.tsx +454 -0
  229. package/src/animations/presets.ts +421 -0
  230. package/src/components/AccoladeCpsComponent.tsx +259 -0
  231. package/src/components/AccordionComponent.tsx +213 -0
  232. package/src/components/AnimatedWrapper.tsx +316 -0
  233. package/src/components/CarouselComponent.tsx +615 -0
  234. package/src/components/ChartCurveComponent.tsx +452 -0
  235. package/src/components/CheckboxComponent.tsx +185 -0
  236. package/src/components/ConfettiComponent.tsx +178 -0
  237. package/src/components/CountdownComponent.tsx +454 -0
  238. package/src/components/DelayedXComponent.tsx +266 -0
  239. package/src/components/DrawerModalComponent.tsx +324 -0
  240. package/src/components/GradientView.tsx +129 -0
  241. package/src/components/HapticFeedbackComponent.tsx +39 -0
  242. package/src/components/HorizontalProductsComponent.tsx +329 -0
  243. package/src/components/MultiChoiceGridComponent.tsx +265 -0
  244. package/src/components/NPSScaleComponent.tsx +294 -0
  245. package/src/components/NavigationComponent.tsx +329 -0
  246. package/src/components/OAuthButtonComponent.tsx +266 -0
  247. package/src/components/ProgressLoaderComponent.tsx +366 -0
  248. package/src/components/ProgressStepperComponent.tsx +250 -0
  249. package/src/components/SegmentedPickerComponent.tsx +245 -0
  250. package/src/components/ShareButtonsComponent.tsx +299 -0
  251. package/src/components/ShareCodeComponent.tsx +303 -0
  252. package/src/components/SingleChoiceGridComponent.tsx +229 -0
  253. package/src/components/SliderComponent.tsx +280 -0
  254. package/src/components/StarRatingInputComponent.tsx +223 -0
  255. package/src/components/StickerCompositeComponent.tsx +168 -0
  256. package/src/components/TabsComponent.tsx +496 -0
  257. package/src/components/TextInputComponent.tsx +258 -0
  258. package/src/components/TimelineComponent.tsx +129 -0
  259. package/src/components/ToggleComponent.tsx +142 -0
  260. package/src/components/VerticalProductsComponent.tsx +357 -0
  261. package/src/dynamicValues.ts +180 -0
  262. package/src/filters/dateFilters.ts +411 -0
  263. package/src/filters/index.ts +281 -0
  264. package/src/hooks/useOfferings.ts +192 -0
  265. package/src/hooks/usePaywall.ts +154 -0
  266. package/src/hooks/usePlacement.ts +143 -0
  267. package/src/hooks/useSubscription.ts +154 -0
  268. package/src/index.ts +245 -0
  269. package/src/models/BillDogError.ts +24 -0
  270. package/src/models/CacheFetchPolicy.ts +13 -0
  271. package/src/models/CustomProduct.ts +33 -0
  272. package/src/models/CustomerInfo.ts +26 -0
  273. package/src/models/Delegate.ts +22 -0
  274. package/src/models/EntitlementInfo.ts +27 -0
  275. package/src/models/Placement.ts +50 -0
  276. package/src/models/PurchaseParams.ts +81 -0
  277. package/src/models/PurchaseResult.ts +32 -0
  278. package/src/models/RestoreResult.ts +27 -0
  279. package/src/models/RestoredItem.ts +25 -0
  280. package/src/modules/ABTest.ts +131 -0
  281. package/src/modules/Analytics.ts +227 -0
  282. package/src/modules/Survey.ts +126 -0
  283. package/src/modules/VirtualCurrency.ts +223 -0
  284. package/src/murmur.ts +102 -0
  285. package/src/notifications/LiveActivitiesManager.ts +140 -0
  286. package/src/notifications/NotificationScheduler.ts +292 -0
  287. package/src/notifications/PurchaseNotificationHandler.ts +67 -0
  288. package/src/notifications/useDeviceToken.ts +116 -0
  289. package/src/notifications/useLiveActivity.ts +125 -0
  290. package/src/review/ReviewAnalytics.ts +89 -0
  291. package/src/review/ReviewManager.ts +355 -0
  292. package/src/review/ReviewTimingManager.ts +265 -0
  293. package/src/survey-core/types.ts +244 -0
  294. package/src/types/ambient.d.ts +9 -0
  295. package/src/types.ts +680 -0
  296. package/src/utils/ApiClient.ts +163 -0
  297. package/src/utils/CSSConverter.ts +552 -0
  298. package/src/utils/animations.ts +232 -0
  299. package/src/utils/calendarIntegration.ts +113 -0
  300. package/src/utils/configurationResolver.ts +106 -0
  301. package/src/utils/deviceDetection.ts +83 -0
  302. package/src/utils/hapticFeedback.ts +55 -0
  303. package/src/utils/iconMapper.ts +102 -0
  304. package/src/utils/notificationPermissions.ts +48 -0
  305. package/src/utils/priceFormatting.ts +119 -0
  306. package/src/utils/shareSheet.ts +55 -0
  307. package/src/utils/subscriptionUpgrade.ts +183 -0
  308. package/src/valueUtils.ts +108 -0
  309. 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;