@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,275 @@
1
+ "use strict";
2
+ /**
3
+ * PaywallView - Main React Native component for rendering paywalls
4
+ *
5
+ * Credentials and API base URL are resolved from BillDogConfiguration.
6
+ * Pass an `apiKey` prop only for backward compatibility.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.PaywallView = void 0;
43
+ const react_1 = __importStar(require("react"));
44
+ const react_native_1 = require("react-native");
45
+ const ComponentRenderer_1 = require("./ComponentRenderer");
46
+ const PaywallStateManager_1 = require("./PaywallStateManager");
47
+ const usePaywall_1 = require("./hooks/usePaywall");
48
+ const deviceDetection_1 = require("./utils/deviceDetection");
49
+ const LayoutEngine_1 = require("./LayoutEngine");
50
+ const LocalizationResolver_1 = require("./LocalizationResolver");
51
+ const PaywallView = ({ paywallIdentifier, configuration: providedConfiguration, apiKey, onButtonPress, onDismiss, LoadingComponent, ErrorComponent, }) => {
52
+ var _a, _b;
53
+ const [deviceCategory, setDeviceCategory] = (0, react_1.useState)((0, deviceDetection_1.detectDeviceCategory)());
54
+ // ── ELEMENT STATE ────────────────────────────────────────────────────────────
55
+ // Components have always called a `stateManager` prop (updateState /
56
+ // subscribeToState / getState / setState) but nothing ever supplied one, and every
57
+ // call site optional-chains — so the whole cross-component state protocol was a
58
+ // silent no-op. Supplying the shared manager here activates it for the tree.
59
+ //
60
+ // Reset DURING FIRST RENDER, not in an effect: React runs child effects BEFORE
61
+ // parent effects, so clearing on mount would wipe the subscriptions the children
62
+ // just registered and leave every component permanently deaf. (Inverted from the
63
+ // native SDKs, where the parent clears first and children subscribe after.) The ref
64
+ // guard keeps it once-per-presentation and StrictMode-safe.
65
+ const stateResetRef = (0, react_1.useRef)(false);
66
+ if (!stateResetRef.current) {
67
+ PaywallStateManager_1.paywallStateManager.clearAll();
68
+ stateResetRef.current = true;
69
+ }
70
+ (0, react_1.useEffect)(() => () => PaywallStateManager_1.paywallStateManager.clearAll(), []);
71
+ // Fetch paywall if identifier is provided
72
+ const { configuration, loading, error, refetch } = (0, usePaywall_1.usePaywall)({
73
+ paywallIdentifier,
74
+ apiKey,
75
+ skipFetch: !!providedConfiguration,
76
+ });
77
+ // Listen for orientation changes
78
+ (0, react_1.useEffect)(() => {
79
+ const subscription = react_native_1.Dimensions.addEventListener('change', () => {
80
+ const newCategory = (0, deviceDetection_1.detectDeviceCategory)();
81
+ if (newCategory !== deviceCategory) {
82
+ setDeviceCategory(newCategory);
83
+ if (!providedConfiguration) {
84
+ refetch();
85
+ }
86
+ }
87
+ });
88
+ return () => subscription === null || subscription === void 0 ? void 0 : subscription.remove();
89
+ }, [deviceCategory, providedConfiguration]);
90
+ // Use provided configuration or fetched configuration
91
+ const activeConfiguration = providedConfiguration || configuration;
92
+ /**
93
+ * Unwrap nested configuration structure
94
+ */
95
+ const unwrappedConfig = (0, react_1.useMemo)(() => {
96
+ var _a, _b, _c;
97
+ if (!activeConfiguration)
98
+ return null;
99
+ const config = activeConfiguration;
100
+ if (config.configuration && typeof config.configuration === 'object') {
101
+ const deviceConfig = config.configuration[deviceCategory] ||
102
+ config.configuration['phone_portrait'] ||
103
+ Object.values(config.configuration)[0];
104
+ if (deviceConfig && typeof deviceConfig === 'object') {
105
+ return {
106
+ ...deviceConfig,
107
+ wallConfigTheme: (_a = config.wallConfig) === null || _a === void 0 ? void 0 : _a.themeConfig,
108
+ };
109
+ }
110
+ }
111
+ if (((_b = config[deviceCategory]) === null || _b === void 0 ? void 0 : _b.theme) || ((_c = config[deviceCategory]) === null || _c === void 0 ? void 0 : _c.components)) {
112
+ return config[deviceCategory];
113
+ }
114
+ return config;
115
+ }, [activeConfiguration, deviceCategory]);
116
+ /**
117
+ * Comprehensive theme resolution
118
+ */
119
+ const resolvedTheme = (0, react_1.useMemo)(() => {
120
+ var _a, _b, _c, _d;
121
+ if (!unwrappedConfig)
122
+ return undefined;
123
+ const config = unwrappedConfig;
124
+ if ((_a = config.theme) === null || _a === void 0 ? void 0 : _a.resolvedColors) {
125
+ return {
126
+ ...config.theme,
127
+ themingMode: config.theme.themingMode || 'full',
128
+ };
129
+ }
130
+ if ((_b = config.wallConfigTheme) === null || _b === void 0 ? void 0 : _b.resolvedColors) {
131
+ return config.wallConfigTheme;
132
+ }
133
+ if ((_c = config.structured) === null || _c === void 0 ? void 0 : _c.themes) {
134
+ const structuredTheme = config.structured.themes[deviceCategory] ||
135
+ config.structured.themes['phone_portrait'];
136
+ if (structuredTheme === null || structuredTheme === void 0 ? void 0 : structuredTheme.resolvedColors) {
137
+ return structuredTheme;
138
+ }
139
+ }
140
+ if ((_d = config.theme) === null || _d === void 0 ? void 0 : _d.colors) {
141
+ return {
142
+ primaryColor: config.theme.colors.primary,
143
+ secondaryColor: config.theme.colors.secondary,
144
+ backgroundColor: config.theme.colors.background,
145
+ textColor: config.theme.colors.foreground || config.theme.colors.text,
146
+ cardColor: config.theme.colors.card,
147
+ mutedColor: config.theme.colors.muted,
148
+ borderColor: config.theme.colors.border,
149
+ accentColor: config.theme.colors.accent,
150
+ resolvedColors: config.theme.colors,
151
+ themingMode: config.theme.themingMode || 'full',
152
+ };
153
+ }
154
+ return config.theme;
155
+ }, [unwrappedConfig, deviceCategory]);
156
+ // Initialize ThemeColorResolver with resolved theme colors
157
+ (0, react_1.useEffect)(() => {
158
+ if (resolvedTheme) {
159
+ const themeForResolver = {};
160
+ if (resolvedTheme.primaryColor)
161
+ themeForResolver.primaryColor = resolvedTheme.primaryColor;
162
+ if (resolvedTheme.secondaryColor)
163
+ themeForResolver.secondaryColor = resolvedTheme.secondaryColor;
164
+ if (resolvedTheme.backgroundColor)
165
+ themeForResolver.backgroundColor = resolvedTheme.backgroundColor;
166
+ if (resolvedTheme.textColor)
167
+ themeForResolver.textColor = resolvedTheme.textColor;
168
+ if (resolvedTheme.cardColor)
169
+ themeForResolver.cardColor = resolvedTheme.cardColor;
170
+ if (resolvedTheme.mutedColor)
171
+ themeForResolver.mutedColor = resolvedTheme.mutedColor;
172
+ if (resolvedTheme.borderColor)
173
+ themeForResolver.borderColor = resolvedTheme.borderColor;
174
+ if (resolvedTheme.accentColor)
175
+ themeForResolver.accentColor = resolvedTheme.accentColor;
176
+ if (resolvedTheme.resolvedColors) {
177
+ Object.entries(resolvedTheme.resolvedColors).forEach(([key, value]) => {
178
+ if (typeof value === 'string') {
179
+ themeForResolver[key] = value;
180
+ }
181
+ });
182
+ }
183
+ LayoutEngine_1.ThemeColorResolver.setTheme(themeForResolver);
184
+ console.log('[PaywallView] Theme initialized:', themeForResolver);
185
+ }
186
+ else {
187
+ LayoutEngine_1.ThemeColorResolver.setTheme(null);
188
+ }
189
+ }, [resolvedTheme]);
190
+ if (loading && !activeConfiguration) {
191
+ if (LoadingComponent) {
192
+ return <LoadingComponent />;
193
+ }
194
+ return (<react_native_1.View style={styles.centered}>
195
+ <react_native_1.ActivityIndicator size="large" color="#007AFF"/>
196
+ </react_native_1.View>);
197
+ }
198
+ if (error && !activeConfiguration) {
199
+ if (ErrorComponent) {
200
+ return <ErrorComponent error={error}/>;
201
+ }
202
+ return <react_native_1.View style={styles.centered}/>;
203
+ }
204
+ if (!activeConfiguration) {
205
+ return null;
206
+ }
207
+ const backgroundColor = (resolvedTheme === null || resolvedTheme === void 0 ? void 0 : resolvedTheme.backgroundColor) ||
208
+ ((_a = resolvedTheme === null || resolvedTheme === void 0 ? void 0 : resolvedTheme.resolvedColors) === null || _a === void 0 ? void 0 : _a.background) ||
209
+ '#FFFFFF';
210
+ // Layout direction — RTL when the dashboard set an explicit
211
+ // `theme.direction: 'rtl'` override, or when the effective locale's
212
+ // language is right-to-left. Mirrors Android `PaywallScreen.kt:431-458`.
213
+ // Applied as a Yoga `direction` style so flex layout mirrors for the
214
+ // whole component tree (row reverses, start/end flip).
215
+ const layoutDirection = (() => {
216
+ var _a, _b;
217
+ const explicit = (_b = (_a = unwrappedConfig === null || unwrappedConfig === void 0 ? void 0 : unwrappedConfig.theme) === null || _a === void 0 ? void 0 : _a.direction) !== null && _b !== void 0 ? _b : resolvedTheme === null || resolvedTheme === void 0 ? void 0 : resolvedTheme.direction;
218
+ if (explicit === 'rtl')
219
+ return 'rtl';
220
+ if (explicit === 'ltr')
221
+ return 'ltr';
222
+ const baseLocale = activeConfiguration === null || activeConfiguration === void 0 ? void 0 : activeConfiguration.baseLocale;
223
+ const effectiveLocale = LocalizationResolver_1.LocalizationResolver.getEffectiveLocale();
224
+ // A non-base effective locale wins; otherwise fall back to baseLocale.
225
+ const localeForDirection = effectiveLocale && effectiveLocale !== baseLocale
226
+ ? effectiveLocale
227
+ : (baseLocale || effectiveLocale);
228
+ return LocalizationResolver_1.LocalizationResolver.isRtlLocale(localeForDirection) ? 'rtl' : 'ltr';
229
+ })();
230
+ const components = (unwrappedConfig === null || unwrappedConfig === void 0 ? void 0 : unwrappedConfig.components) || (activeConfiguration === null || activeConfiguration === void 0 ? void 0 : activeConfiguration.components) || [];
231
+ const absoluteComponents = components.filter((c) => c.absolutePosition);
232
+ const relativeComponents = components.filter((c) => !c.absolutePosition);
233
+ const screenLayout = (_b = unwrappedConfig === null || unwrappedConfig === void 0 ? void 0 : unwrappedConfig.theme) === null || _b === void 0 ? void 0 : _b.screenLayout;
234
+ const contentPadding = (screenLayout === null || screenLayout === void 0 ? void 0 : screenLayout.padding) || { top: 16, right: 16, bottom: 16, left: 16 };
235
+ return (<react_native_1.View style={[styles.container, { backgroundColor, direction: layoutDirection }]}>
236
+ <react_native_1.ScrollView style={styles.scrollView} contentContainerStyle={[
237
+ styles.content,
238
+ {
239
+ paddingTop: contentPadding.top,
240
+ paddingRight: contentPadding.right,
241
+ paddingBottom: contentPadding.bottom,
242
+ paddingLeft: contentPadding.left,
243
+ gap: (screenLayout === null || screenLayout === void 0 ? void 0 : screenLayout.gap) || 16,
244
+ }
245
+ ]}>
246
+ {relativeComponents.map((component) => (<ComponentRenderer_1.ComponentRenderer key={component.id} stateManager={PaywallStateManager_1.paywallStateManager} component={component} configuration={activeConfiguration} theme={resolvedTheme} onButtonPress={onButtonPress}/>))}
247
+ </react_native_1.ScrollView>
248
+
249
+ {absoluteComponents.length > 0 && (<react_native_1.View style={styles.absoluteContainer} pointerEvents="box-none">
250
+ {absoluteComponents.map((component) => (<ComponentRenderer_1.ComponentRenderer key={component.id} stateManager={PaywallStateManager_1.paywallStateManager} component={component} configuration={activeConfiguration} theme={resolvedTheme} onButtonPress={onButtonPress}/>))}
251
+ </react_native_1.View>)}
252
+ </react_native_1.View>);
253
+ };
254
+ exports.PaywallView = PaywallView;
255
+ const styles = react_native_1.StyleSheet.create({
256
+ container: {
257
+ flex: 1,
258
+ position: 'relative',
259
+ },
260
+ scrollView: {
261
+ flex: 1,
262
+ },
263
+ content: {
264
+ flexGrow: 1,
265
+ },
266
+ centered: {
267
+ flex: 1,
268
+ justifyContent: 'center',
269
+ alignItems: 'center',
270
+ },
271
+ absoluteContainer: {
272
+ ...react_native_1.StyleSheet.absoluteFillObject,
273
+ zIndex: 10,
274
+ },
275
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Product Variable Provider for BillDog React Native SDK
3
+ *
4
+ * Generates variable maps from packages for use in VariableResolver.
5
+ * Enables {{product.price}}, {{product.period}}, {{selectedProduct.*}}, etc.
6
+ *
7
+ * Port of Android's ProductVariableProvider.kt with 1:1 variable key parity.
8
+ */
9
+ export interface PackageInput {
10
+ identifier: string;
11
+ packageType?: string;
12
+ productId?: string;
13
+ price?: string;
14
+ rawPrice?: number;
15
+ currencyCode?: string;
16
+ title?: string;
17
+ description?: string;
18
+ subscriptionPeriod?: {
19
+ value: number;
20
+ unit: string;
21
+ } | null;
22
+ freeTrialPeriod?: {
23
+ value: number;
24
+ unit: string;
25
+ } | null;
26
+ introductoryPrice?: {
27
+ price: number;
28
+ priceString: string;
29
+ period?: {
30
+ value: number;
31
+ unit: string;
32
+ };
33
+ } | null;
34
+ isSubscription?: boolean;
35
+ [key: string]: any;
36
+ }
37
+ export interface TrialEligibilityInput {
38
+ isEligible: boolean;
39
+ hasUsedTrial?: boolean;
40
+ [key: string]: any;
41
+ }
42
+ export declare class ProductVariableProvider {
43
+ private packages;
44
+ private trialEligibility?;
45
+ constructor(packages: PackageInput[], trialEligibility?: Record<string, TrialEligibilityInput>);
46
+ /** Generate variable map for a specific product index */
47
+ getVariablesForProduct(index: number): Record<string, any>;
48
+ /** Generate variable map for a specific package (40+ keys) */
49
+ getVariablesForPackage(pkg: PackageInput): Record<string, any>;
50
+ /** Generate variables for selected product with "selectedProduct." prefix */
51
+ getSelectedProductVariables(selectedIndex: number): Record<string, any>;
52
+ /** Generate all product variables with index prefix (products.0.*, products.1.*, etc.) */
53
+ getAllProductVariables(): Record<string, any>;
54
+ /** Generate convenience aliases (primary, secondary, monthly, annual, etc.) */
55
+ getConvenienceVariables(): Record<string, any>;
56
+ /** Create provider from an offering */
57
+ static fromOffering(offering: {
58
+ packages: PackageInput[];
59
+ }, trialEligibility?: Record<string, TrialEligibilityInput>): ProductVariableProvider;
60
+ }
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ /**
3
+ * Product Variable Provider for BillDog React Native SDK
4
+ *
5
+ * Generates variable maps from packages for use in VariableResolver.
6
+ * Enables {{product.price}}, {{product.period}}, {{selectedProduct.*}}, etc.
7
+ *
8
+ * Port of Android's ProductVariableProvider.kt with 1:1 variable key parity.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.ProductVariableProvider = void 0;
12
+ class ProductVariableProvider {
13
+ constructor(packages, trialEligibility) {
14
+ this.packages = packages;
15
+ this.trialEligibility = trialEligibility;
16
+ }
17
+ /** Generate variable map for a specific product index */
18
+ getVariablesForProduct(index) {
19
+ const pkg = this.packages[index];
20
+ if (!pkg)
21
+ return {};
22
+ return this.getVariablesForPackage(pkg);
23
+ }
24
+ /** Generate variable map for a specific package (40+ keys) */
25
+ getVariablesForPackage(pkg) {
26
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
27
+ const eligibility = (_a = this.trialEligibility) === null || _a === void 0 ? void 0 : _a[(_b = pkg.productId) !== null && _b !== void 0 ? _b : pkg.identifier];
28
+ const vars = {};
29
+ // Basic
30
+ vars['product.identifier'] = pkg.identifier;
31
+ vars['product.packageType'] = (_c = pkg.packageType) !== null && _c !== void 0 ? _c : 'custom';
32
+ const hasProduct = pkg.price !== undefined || pkg.rawPrice !== undefined;
33
+ if (hasProduct) {
34
+ vars['product.price'] = (_d = pkg.price) !== null && _d !== void 0 ? _d : '';
35
+ vars['product.rawPrice'] = (_e = pkg.rawPrice) !== null && _e !== void 0 ? _e : 0;
36
+ vars['product.currencyCode'] = (_f = pkg.currencyCode) !== null && _f !== void 0 ? _f : 'USD';
37
+ vars['product.currencySymbol'] = getCurrencySymbol((_g = pkg.currencyCode) !== null && _g !== void 0 ? _g : 'USD');
38
+ vars['product.title'] = (_h = pkg.title) !== null && _h !== void 0 ? _h : '';
39
+ vars['product.description'] = (_j = pkg.description) !== null && _j !== void 0 ? _j : '';
40
+ // Period
41
+ const period = pkg.subscriptionPeriod;
42
+ if (period) {
43
+ vars['product.period'] = toDisplayString(period);
44
+ vars['product.periodShort'] = toShortString(period);
45
+ vars['product.periodDays'] = totalDays(period);
46
+ vars['product.periodUnit'] = period.unit;
47
+ vars['product.periodValue'] = period.value;
48
+ }
49
+ // Calculated prices
50
+ const raw = (_k = pkg.rawPrice) !== null && _k !== void 0 ? _k : 0;
51
+ const days = period ? totalDays(period) : 0;
52
+ const daily = days > 0 ? raw / days : raw;
53
+ const weekly = days > 0 ? (raw / days) * 7 : raw;
54
+ const monthly = days > 0 ? raw / (days / 30) : raw;
55
+ const yearly = days > 0 ? (raw / days) * 365 : raw;
56
+ const cc = (_l = pkg.currencyCode) !== null && _l !== void 0 ? _l : 'USD';
57
+ vars['product.dailyPrice'] = formatPrice(daily, cc);
58
+ vars['product.weeklyPrice'] = formatPrice(weekly, cc);
59
+ vars['product.monthlyPrice'] = formatPrice(monthly, cc);
60
+ vars['product.yearlyPrice'] = formatPrice(yearly, cc);
61
+ vars['product.rawDailyPrice'] = daily;
62
+ vars['product.rawWeeklyPrice'] = weekly;
63
+ vars['product.rawMonthlyPrice'] = monthly;
64
+ vars['product.rawYearlyPrice'] = yearly;
65
+ // Trial
66
+ const trial = pkg.freeTrialPeriod;
67
+ vars['product.hasFreeTrial'] = trial != null;
68
+ if (trial) {
69
+ vars['product.trialPeriod'] = toDisplayString(trial);
70
+ vars['product.trialPeriodDays'] = totalDays(trial);
71
+ vars['product.trialPeriodText'] = formatTrialPeriod(trial);
72
+ }
73
+ else {
74
+ vars['product.trialPeriod'] = '';
75
+ vars['product.trialPeriodDays'] = 0;
76
+ vars['product.trialPeriodText'] = '';
77
+ }
78
+ vars['product.isTrialEligible'] = (_m = eligibility === null || eligibility === void 0 ? void 0 : eligibility.isEligible) !== null && _m !== void 0 ? _m : (trial != null);
79
+ // Intro offer
80
+ const intro = pkg.introductoryPrice;
81
+ vars['product.hasIntroOffer'] = intro != null;
82
+ if (intro) {
83
+ vars['product.introPrice'] = intro.priceString;
84
+ vars['product.introPriceRaw'] = intro.price;
85
+ vars['product.introPeriod'] = intro.period ? toDisplayString(intro.period) : '';
86
+ }
87
+ else {
88
+ vars['product.introPrice'] = '';
89
+ vars['product.introPriceRaw'] = 0;
90
+ vars['product.introPeriod'] = '';
91
+ }
92
+ // User eligibility
93
+ const isEligibleForIntro = (_o = eligibility === null || eligibility === void 0 ? void 0 : eligibility.isEligible) !== null && _o !== void 0 ? _o : (trial != null || intro != null);
94
+ const hasUsedTrial = (_p = eligibility === null || eligibility === void 0 ? void 0 : eligibility.hasUsedTrial) !== null && _p !== void 0 ? _p : false;
95
+ vars['user.isEligibleForIntro'] = isEligibleForIntro;
96
+ vars['user.hasUsedTrial'] = hasUsedTrial;
97
+ vars['user.isEligibleForPromo'] = false;
98
+ // Offer summary
99
+ const hasAnyOffer = trial != null || intro != null;
100
+ vars['offer.hasAnyOffer'] = hasAnyOffer;
101
+ vars['offer.type'] = trial ? 'free_trial' : intro ? 'intro' : 'none';
102
+ vars['product.isSubscription'] = (_q = pkg.isSubscription) !== null && _q !== void 0 ? _q : (period != null);
103
+ }
104
+ else {
105
+ // Fallback values
106
+ vars['product.price'] = '';
107
+ vars['product.rawPrice'] = 0;
108
+ vars['product.currencyCode'] = 'USD';
109
+ vars['product.currencySymbol'] = '$';
110
+ vars['product.title'] = '';
111
+ vars['product.description'] = '';
112
+ vars['product.period'] = '';
113
+ vars['product.periodShort'] = '';
114
+ vars['product.periodDays'] = 0;
115
+ vars['product.hasFreeTrial'] = false;
116
+ vars['product.isTrialEligible'] = false;
117
+ vars['product.hasIntroOffer'] = false;
118
+ vars['product.isSubscription'] = false;
119
+ }
120
+ return vars;
121
+ }
122
+ /** Generate variables for selected product with "selectedProduct." prefix */
123
+ getSelectedProductVariables(selectedIndex) {
124
+ const productVars = this.getVariablesForProduct(selectedIndex);
125
+ const result = {};
126
+ for (const [key, value] of Object.entries(productVars)) {
127
+ result[key.replace('product.', 'selectedProduct.')] = value;
128
+ }
129
+ return result;
130
+ }
131
+ /** Generate all product variables with index prefix (products.0.*, products.1.*, etc.) */
132
+ getAllProductVariables() {
133
+ const vars = {};
134
+ this.packages.forEach((pkg, index) => {
135
+ const pkgVars = this.getVariablesForPackage(pkg);
136
+ for (const [key, value] of Object.entries(pkgVars)) {
137
+ const productKey = key.startsWith('product.') ? key.slice(8) : key;
138
+ vars[`products.${index}.${productKey}`] = value;
139
+ }
140
+ });
141
+ return vars;
142
+ }
143
+ /** Generate convenience aliases (primary, secondary, monthly, annual, etc.) */
144
+ getConvenienceVariables() {
145
+ var _a;
146
+ const vars = {};
147
+ // Primary = first package
148
+ if (this.packages[0]) {
149
+ for (const [key, value] of Object.entries(this.getVariablesForPackage(this.packages[0]))) {
150
+ const k = key.startsWith('product.') ? key.slice(8) : key;
151
+ vars[`primary.${k}`] = value;
152
+ }
153
+ }
154
+ // Secondary = second package
155
+ if (this.packages[1]) {
156
+ for (const [key, value] of Object.entries(this.getVariablesForPackage(this.packages[1]))) {
157
+ const k = key.startsWith('product.') ? key.slice(8) : key;
158
+ vars[`secondary.${k}`] = value;
159
+ }
160
+ }
161
+ // Named packages by type
162
+ for (const pkg of this.packages) {
163
+ const typeKey = (_a = pkg.packageType) !== null && _a !== void 0 ? _a : 'custom';
164
+ for (const [key, value] of Object.entries(this.getVariablesForPackage(pkg))) {
165
+ const k = key.startsWith('product.') ? key.slice(8) : key;
166
+ vars[`${typeKey}.${k}`] = value;
167
+ }
168
+ }
169
+ return vars;
170
+ }
171
+ /** Create provider from an offering */
172
+ static fromOffering(offering, trialEligibility) {
173
+ return new ProductVariableProvider(offering.packages, trialEligibility);
174
+ }
175
+ }
176
+ exports.ProductVariableProvider = ProductVariableProvider;
177
+ // Helpers
178
+ function getCurrencySymbol(currencyCode) {
179
+ try {
180
+ return (0).toLocaleString(undefined, {
181
+ style: 'currency', currency: currencyCode, minimumFractionDigits: 0, maximumFractionDigits: 0,
182
+ }).replace(/\d/g, '').trim() || '$';
183
+ }
184
+ catch {
185
+ return '$';
186
+ }
187
+ }
188
+ function formatPrice(price, currencyCode) {
189
+ try {
190
+ return new Intl.NumberFormat(undefined, { style: 'currency', currency: currencyCode }).format(price);
191
+ }
192
+ catch {
193
+ return `$${price.toFixed(2)}`;
194
+ }
195
+ }
196
+ function totalDays(period) {
197
+ switch (period.unit) {
198
+ case 'day': return period.value;
199
+ case 'week': return period.value * 7;
200
+ case 'month': return period.value * 30;
201
+ case 'year': return period.value * 365;
202
+ default: return period.value;
203
+ }
204
+ }
205
+ function toDisplayString(period) {
206
+ const u = period.unit;
207
+ const v = period.value;
208
+ const unitStr = v === 1
209
+ ? (u === 'day' ? 'day' : u === 'week' ? 'week' : u === 'month' ? 'month' : 'year')
210
+ : (u === 'day' ? 'days' : u === 'week' ? 'weeks' : u === 'month' ? 'months' : 'years');
211
+ return `${v} ${unitStr}`;
212
+ }
213
+ function toShortString(period) {
214
+ switch (period.unit) {
215
+ case 'day': return '/day';
216
+ case 'week': return '/wk';
217
+ case 'month': return '/mo';
218
+ case 'year': return '/yr';
219
+ default: return `/${period.unit}`;
220
+ }
221
+ }
222
+ function formatTrialPeriod(period) {
223
+ const days = totalDays(period);
224
+ if (days === 1)
225
+ return '1-day free trial';
226
+ if (days === 7)
227
+ return '7-day free trial';
228
+ if (days === 14)
229
+ return '2-week free trial';
230
+ if (days === 30 || days === 31)
231
+ return '1-month free trial';
232
+ if (days < 7)
233
+ return `${days}-day free trial`;
234
+ if (days % 7 === 0)
235
+ return `${days / 7}-week free trial`;
236
+ if (days % 30 === 0)
237
+ return `${days / 30}-month free trial`;
238
+ return `${days}-day free trial`;
239
+ }