@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,293 @@
1
+ "use strict";
2
+ /**
3
+ * PaywallCache - Smart caching layer for React Native
4
+ * Uses AsyncStorage for metadata and RNFS for asset caching
5
+ * Supports server-provided cache configuration
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.PaywallCache = exports.DEFAULT_CACHE_CONFIG = void 0;
12
+ const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
13
+ const react_native_fs_1 = __importDefault(require("react-native-fs"));
14
+ exports.DEFAULT_CACHE_CONFIG = {
15
+ enabled: true,
16
+ maxCacheAge: 604800000, // 7 days
17
+ backgroundUpdateEnabled: true,
18
+ preloadAssets: true,
19
+ };
20
+ // Track background updates in progress
21
+ const backgroundUpdateInProgress = new Set();
22
+ class PaywallCache {
23
+ constructor(config = {}) {
24
+ this.fallbackCache = new Map();
25
+ this.config = { ...exports.DEFAULT_CACHE_CONFIG, ...config };
26
+ this.cacheDir = `${react_native_fs_1.default.CachesDirectoryPath}/billdog_paywalls`;
27
+ // Create cache directory
28
+ react_native_fs_1.default.mkdir(this.cacheDir).catch(() => {
29
+ // Directory might already exist
30
+ });
31
+ }
32
+ // MARK: - Configuration
33
+ /**
34
+ * Update cache configuration from server response
35
+ */
36
+ configureFromServer(serverConfig) {
37
+ this.config = { ...this.config, ...serverConfig };
38
+ console.log('[PaywallCache] Configured from server:', this.config);
39
+ }
40
+ /**
41
+ * Get current cache configuration
42
+ */
43
+ getConfig() {
44
+ return { ...this.config };
45
+ }
46
+ // MARK: - Paywall Caching
47
+ async get(identifier) {
48
+ if (!this.config.enabled) {
49
+ // Still return fallback if available
50
+ return this.fallbackCache.get(identifier) || null;
51
+ }
52
+ try {
53
+ const key = `paywall:${identifier}`;
54
+ const cachedJson = await async_storage_1.default.getItem(key);
55
+ if (!cachedJson) {
56
+ console.log(`[PaywallCache] MISS: ${identifier}`);
57
+ // Return fallback if available
58
+ return this.fallbackCache.get(identifier) || null;
59
+ }
60
+ const cached = JSON.parse(cachedJson);
61
+ // Check expiration (unless it's a fallback)
62
+ if (!cached.isFallback && Date.now() > cached.expiresAt) {
63
+ console.log(`[PaywallCache] EXPIRED: ${identifier}`);
64
+ await this.delete(identifier);
65
+ // Return fallback if available
66
+ return this.fallbackCache.get(identifier) || null;
67
+ }
68
+ console.log(`[PaywallCache] HIT: ${identifier} (v${cached.version})${cached.isFallback ? ' (fallback)' : ''}`);
69
+ return cached;
70
+ }
71
+ catch (error) {
72
+ console.error(`[PaywallCache] ERROR reading cache:`, error);
73
+ return this.fallbackCache.get(identifier) || null;
74
+ }
75
+ }
76
+ async set(identifier, paywall) {
77
+ if (!this.config.enabled && !paywall.isFallback)
78
+ return;
79
+ try {
80
+ // Calculate expiration using server-provided maxCacheAge
81
+ const cacheEntry = {
82
+ ...paywall,
83
+ cachedAt: Date.now(),
84
+ expiresAt: Date.now() + this.config.maxCacheAge,
85
+ };
86
+ const key = `paywall:${identifier}`;
87
+ const cachedJson = JSON.stringify(cacheEntry);
88
+ await async_storage_1.default.setItem(key, cachedJson);
89
+ console.log(`[PaywallCache] CACHED: ${identifier} (v${paywall.version}) TTL: ${this.config.maxCacheAge / 1000}s`);
90
+ }
91
+ catch (error) {
92
+ console.error(`[PaywallCache] ERROR writing cache:`, error);
93
+ }
94
+ }
95
+ /**
96
+ * Set a fallback paywall that will never be cleared
97
+ */
98
+ async setFallback(identifier, paywall) {
99
+ const fallbackPaywall = {
100
+ ...paywall,
101
+ isFallback: true,
102
+ expiresAt: Date.now() + (365 * 24 * 60 * 60 * 1000), // 1 year
103
+ };
104
+ // Store in memory for fast access
105
+ this.fallbackCache.set(identifier, fallbackPaywall);
106
+ // Also persist to storage
107
+ await this.set(identifier, fallbackPaywall);
108
+ console.log(`[PaywallCache] FALLBACK SET: ${identifier}`);
109
+ }
110
+ async delete(identifier, includeFallback = false) {
111
+ try {
112
+ // Check if it's a fallback and skip if not forcing
113
+ if (!includeFallback) {
114
+ const key = `paywall:${identifier}`;
115
+ const cachedJson = await async_storage_1.default.getItem(key);
116
+ if (cachedJson) {
117
+ const cached = JSON.parse(cachedJson);
118
+ if (cached.isFallback) {
119
+ console.log(`[PaywallCache] SKIP DELETE FALLBACK: ${identifier}`);
120
+ return;
121
+ }
122
+ }
123
+ }
124
+ const key = `paywall:${identifier}`;
125
+ await async_storage_1.default.removeItem(key);
126
+ if (includeFallback) {
127
+ this.fallbackCache.delete(identifier);
128
+ }
129
+ console.log(`[PaywallCache] DELETED: ${identifier}`);
130
+ }
131
+ catch (error) {
132
+ console.error(`[PaywallCache] ERROR deleting cache:`, error);
133
+ }
134
+ }
135
+ // MARK: - Background Updates
136
+ /**
137
+ * Check if background update is needed and trigger if enabled
138
+ */
139
+ shouldTriggerBackgroundUpdate(identifier) {
140
+ if (!this.config.backgroundUpdateEnabled)
141
+ return false;
142
+ if (backgroundUpdateInProgress.has(identifier))
143
+ return false;
144
+ return true;
145
+ }
146
+ markBackgroundUpdateStarted(identifier) {
147
+ backgroundUpdateInProgress.add(identifier);
148
+ }
149
+ markBackgroundUpdateCompleted(identifier) {
150
+ backgroundUpdateInProgress.delete(identifier);
151
+ }
152
+ // MARK: - Asset Caching
153
+ async cacheAsset(url) {
154
+ if (!this.config.preloadAssets)
155
+ return null;
156
+ try {
157
+ const fileName = url.split('/').pop() || 'asset';
158
+ const localPath = `${this.cacheDir}/${fileName}`;
159
+ // Check if already cached
160
+ const exists = await react_native_fs_1.default.exists(localPath);
161
+ if (exists) {
162
+ console.log(`[PaywallCache] Asset already cached: ${fileName}`);
163
+ return localPath;
164
+ }
165
+ // Download and cache
166
+ const result = await react_native_fs_1.default.downloadFile({
167
+ fromUrl: url,
168
+ toFile: localPath,
169
+ }).promise;
170
+ if (result.statusCode === 200) {
171
+ console.log(`[PaywallCache] Asset cached: ${fileName}`);
172
+ return localPath;
173
+ }
174
+ else {
175
+ console.error(`[PaywallCache] Asset download failed: ${result.statusCode}`);
176
+ return null;
177
+ }
178
+ }
179
+ catch (error) {
180
+ console.error(`[PaywallCache] Asset caching error:`, error);
181
+ return null;
182
+ }
183
+ }
184
+ /**
185
+ * Preload all assets from a paywall configuration
186
+ */
187
+ async preloadPaywallAssets(configuration) {
188
+ if (!this.config.preloadAssets)
189
+ return;
190
+ const assetUrls = this.extractAssetUrls(configuration);
191
+ console.log(`[PaywallCache] Preloading ${assetUrls.length} assets`);
192
+ await Promise.all(assetUrls.map(url => this.cacheAsset(url)));
193
+ }
194
+ extractAssetUrls(config) {
195
+ const urls = [];
196
+ const traverse = (obj) => {
197
+ if (!obj || typeof obj !== 'object')
198
+ return;
199
+ if (typeof obj.src === 'string' && obj.src.startsWith('http')) {
200
+ urls.push(obj.src);
201
+ }
202
+ if (typeof obj.imageUrl === 'string' && obj.imageUrl.startsWith('http')) {
203
+ urls.push(obj.imageUrl);
204
+ }
205
+ if (typeof obj.backgroundImage === 'string' && obj.backgroundImage.startsWith('http')) {
206
+ urls.push(obj.backgroundImage);
207
+ }
208
+ Object.values(obj).forEach(traverse);
209
+ };
210
+ traverse(config);
211
+ return [...new Set(urls)]; // Remove duplicates
212
+ }
213
+ async getCachedAssetPath(url) {
214
+ try {
215
+ const fileName = url.split('/').pop() || 'asset';
216
+ const localPath = `${this.cacheDir}/${fileName}`;
217
+ const exists = await react_native_fs_1.default.exists(localPath);
218
+ return exists ? localPath : null;
219
+ }
220
+ catch (error) {
221
+ console.error(`[PaywallCache] Error checking cached asset:`, error);
222
+ return null;
223
+ }
224
+ }
225
+ // MARK: - Cache Management
226
+ /**
227
+ * Clear regular cache (preserves fallbacks)
228
+ */
229
+ async clearAll() {
230
+ try {
231
+ // Clear AsyncStorage keys (skip fallbacks)
232
+ const keys = await async_storage_1.default.getAllKeys();
233
+ const paywallKeys = keys.filter((key) => key.startsWith('paywall:'));
234
+ for (const key of paywallKeys) {
235
+ const cachedJson = await async_storage_1.default.getItem(key);
236
+ if (cachedJson) {
237
+ const cached = JSON.parse(cachedJson);
238
+ if (!cached.isFallback) {
239
+ await async_storage_1.default.removeItem(key);
240
+ }
241
+ }
242
+ }
243
+ // Clear file cache
244
+ const files = await react_native_fs_1.default.readDir(this.cacheDir);
245
+ await Promise.all(files.map((file) => react_native_fs_1.default.unlink(file.path)));
246
+ console.log('[PaywallCache] Cache cleared (fallbacks preserved)');
247
+ }
248
+ catch (error) {
249
+ console.error('[PaywallCache] Error clearing cache:', error);
250
+ }
251
+ }
252
+ /**
253
+ * Clear all cache including fallbacks
254
+ */
255
+ async clearAllIncludingFallbacks() {
256
+ try {
257
+ const keys = await async_storage_1.default.getAllKeys();
258
+ const paywallKeys = keys.filter((key) => key.startsWith('paywall:'));
259
+ await async_storage_1.default.multiRemove(paywallKeys);
260
+ const files = await react_native_fs_1.default.readDir(this.cacheDir);
261
+ await Promise.all(files.map((file) => react_native_fs_1.default.unlink(file.path)));
262
+ this.fallbackCache.clear();
263
+ console.log('[PaywallCache] All cache cleared including fallbacks');
264
+ }
265
+ catch (error) {
266
+ console.error('[PaywallCache] Error clearing cache:', error);
267
+ }
268
+ }
269
+ async getStats() {
270
+ try {
271
+ const keys = await async_storage_1.default.getAllKeys();
272
+ const paywallKeys = keys.filter((key) => key.startsWith('paywall:'));
273
+ const files = await react_native_fs_1.default.readDir(this.cacheDir);
274
+ const assetCount = files.length;
275
+ return {
276
+ paywallCount: paywallKeys.length,
277
+ assetCount,
278
+ fallbackCount: this.fallbackCache.size,
279
+ config: this.config,
280
+ };
281
+ }
282
+ catch (error) {
283
+ console.error('[PaywallCache] Error getting stats:', error);
284
+ return {
285
+ paywallCount: 0,
286
+ assetCount: 0,
287
+ fallbackCount: 0,
288
+ config: this.config,
289
+ };
290
+ }
291
+ }
292
+ }
293
+ exports.PaywallCache = PaywallCache;
@@ -0,0 +1,62 @@
1
+ export type StateListener = (value: any) => void;
2
+ export type Unsubscribe = () => void;
3
+ export declare class PaywallStateManager {
4
+ /**
5
+ * Paywall-wide key holding the live product selection.
6
+ *
7
+ * Deliberately NOT `element:{id}.selectedIndex` — that form is scoped to a single
8
+ * products component, while Dynamic Values need one well-known key to react to. The
9
+ * name matches the variable key the DV bag emits (`products.selectedIndex`), so an
10
+ * authored condition and the state driving it are spelled identically.
11
+ *
12
+ * Parity: Android `PaywallStateManager.SELECTED_INDEX_KEY`, iOS
13
+ * `PaywallView.selectedIndexStateKey`.
14
+ */
15
+ static readonly SELECTED_INDEX_KEY = "products.selectedIndex";
16
+ /** Shared instance. Native uses a singleton (Kotlin `object` / Swift `.shared`); the
17
+ * class stays exported so tests can build an isolated store. */
18
+ static readonly shared: PaywallStateManager;
19
+ private state;
20
+ private listeners;
21
+ /** Read the current value, or `defaultValue` when unset. */
22
+ getState<T = any>(key: string, defaultValue?: T): T | undefined;
23
+ /**
24
+ * Write a value and notify subscribers.
25
+ *
26
+ * The structural-equality guard is load-bearing, not an optimisation: components
27
+ * write their state in an effect keyed on that same state, so an unconditional
28
+ * notify would loop (write → notify → setState → effect → write). Android relies on
29
+ * the identical guard.
30
+ */
31
+ setState(key: string, value: any): void;
32
+ /** Alias for {@link setState} — the name 53 existing call sites use (web-builder parity). */
33
+ updateState(key: string, value: any): void;
34
+ /** Alias for {@link getState} — used by the component test suite. */
35
+ getValue<T = any>(key: string, defaultValue?: T): T | undefined;
36
+ /** Alias for {@link setState} — used by the component test suite. */
37
+ setValue(key: string, value: any): void;
38
+ /**
39
+ * Subscribe to a key. Returns an unsubscribe function — components return it
40
+ * directly from `useEffect`, so it MUST be callable and idempotent.
41
+ */
42
+ subscribe(key: string, listener: StateListener): Unsubscribe;
43
+ /** Alias for {@link subscribe} — the name 14 existing call sites use. */
44
+ subscribeToState(key: string, listener: StateListener): Unsubscribe;
45
+ /**
46
+ * Clear all state AND subscriptions, for a fresh paywall presentation.
47
+ *
48
+ * ORDERING TRAP (React-specific, inverted from the native SDKs): child effects run
49
+ * BEFORE parent effects, so calling this from a mount effect in PaywallView would
50
+ * wipe the subscriptions its children registered moments earlier, leaving every
51
+ * component permanently deaf. PaywallView therefore resets during its first render —
52
+ * before any child mounts. See the ref-guarded reset there.
53
+ */
54
+ clearAll(): void;
55
+ /** Clear state for one component (`element:{componentId}.*`). */
56
+ clearComponent(componentId: string): void;
57
+ /** Snapshot of all state (debugging / diagnostics). */
58
+ getAllState(): Record<string, any>;
59
+ private notify;
60
+ }
61
+ /** Shared instance — the one PaywallView threads through the component tree. */
62
+ export declare const paywallStateManager: PaywallStateManager;
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.paywallStateManager = exports.PaywallStateManager = void 0;
4
+ /**
5
+ * PaywallStateManager — element state for React Native paywall components.
6
+ *
7
+ * Parity with `io.billdog.compose.ui.PaywallStateManager` (Android) and
8
+ * `PaywallStateManager.shared` (iOS): a key→value store with per-key subscriptions,
9
+ * following the `element:{componentId}.{property}` convention, plus the paywall-wide
10
+ * `products.selectedIndex` key that drives selection-reactive Dynamic Values.
11
+ *
12
+ * WHY THIS EXISTS: 27 RN component files already called a `stateManager` prop
13
+ * (`updateState`, `subscribeToState`, `getState`, `setState`, `subscribe`) — but
14
+ * nothing ever PROVIDED one, and every call site optional-chains
15
+ * (`stateManager?.updateState?.(...)`). So the entire cross-component state protocol
16
+ * was dead code that failed silently: toggles could not drive visibility, tabs could
17
+ * not be driven externally, and product selection was invisible outside the products
18
+ * component. This is the missing implementation, not a new abstraction.
19
+ *
20
+ * Usage:
21
+ * PaywallStateManager.shared.setState('element:slider_1.currentValue', 75);
22
+ * const v = PaywallStateManager.shared.getState<number>('element:slider_1.currentValue');
23
+ */
24
+ const valueUtils_1 = require("./valueUtils");
25
+ class PaywallStateManager {
26
+ constructor() {
27
+ this.state = new Map();
28
+ this.listeners = new Map();
29
+ }
30
+ /** Read the current value, or `defaultValue` when unset. */
31
+ getState(key, defaultValue) {
32
+ const v = this.state.get(key);
33
+ return v === undefined ? defaultValue : v;
34
+ }
35
+ /**
36
+ * Write a value and notify subscribers.
37
+ *
38
+ * The structural-equality guard is load-bearing, not an optimisation: components
39
+ * write their state in an effect keyed on that same state, so an unconditional
40
+ * notify would loop (write → notify → setState → effect → write). Android relies on
41
+ * the identical guard.
42
+ */
43
+ setState(key, value) {
44
+ if (this.state.has(key) && (0, valueUtils_1.isDeepEqual)(this.state.get(key), value))
45
+ return;
46
+ this.state.set(key, value);
47
+ this.notify(key, value);
48
+ }
49
+ /** Alias for {@link setState} — the name 53 existing call sites use (web-builder parity). */
50
+ updateState(key, value) {
51
+ this.setState(key, value);
52
+ }
53
+ /** Alias for {@link getState} — used by the component test suite. */
54
+ getValue(key, defaultValue) {
55
+ return this.getState(key, defaultValue);
56
+ }
57
+ /** Alias for {@link setState} — used by the component test suite. */
58
+ setValue(key, value) {
59
+ this.setState(key, value);
60
+ }
61
+ /**
62
+ * Subscribe to a key. Returns an unsubscribe function — components return it
63
+ * directly from `useEffect`, so it MUST be callable and idempotent.
64
+ */
65
+ subscribe(key, listener) {
66
+ let set = this.listeners.get(key);
67
+ if (!set) {
68
+ set = new Set();
69
+ this.listeners.set(key, set);
70
+ }
71
+ set.add(listener);
72
+ return () => {
73
+ const current = this.listeners.get(key);
74
+ if (!current)
75
+ return;
76
+ current.delete(listener);
77
+ if (current.size === 0)
78
+ this.listeners.delete(key);
79
+ };
80
+ }
81
+ /** Alias for {@link subscribe} — the name 14 existing call sites use. */
82
+ subscribeToState(key, listener) {
83
+ return this.subscribe(key, listener);
84
+ }
85
+ /**
86
+ * Clear all state AND subscriptions, for a fresh paywall presentation.
87
+ *
88
+ * ORDERING TRAP (React-specific, inverted from the native SDKs): child effects run
89
+ * BEFORE parent effects, so calling this from a mount effect in PaywallView would
90
+ * wipe the subscriptions its children registered moments earlier, leaving every
91
+ * component permanently deaf. PaywallView therefore resets during its first render —
92
+ * before any child mounts. See the ref-guarded reset there.
93
+ */
94
+ clearAll() {
95
+ this.state.clear();
96
+ this.listeners.clear();
97
+ }
98
+ /** Clear state for one component (`element:{componentId}.*`). */
99
+ clearComponent(componentId) {
100
+ const prefix = `element:${componentId}.`;
101
+ for (const key of Array.from(this.state.keys())) {
102
+ if (key.startsWith(prefix)) {
103
+ this.state.delete(key);
104
+ this.listeners.delete(key);
105
+ }
106
+ }
107
+ }
108
+ /** Snapshot of all state (debugging / diagnostics). */
109
+ getAllState() {
110
+ return Object.fromEntries(this.state);
111
+ }
112
+ notify(key, value) {
113
+ const set = this.listeners.get(key);
114
+ if (!set)
115
+ return;
116
+ // Copy before iterating: a listener may unsubscribe (or subscribe) while running,
117
+ // which would otherwise mutate the Set mid-iteration.
118
+ for (const listener of Array.from(set)) {
119
+ try {
120
+ listener(value);
121
+ }
122
+ catch (err) {
123
+ // One misbehaving component must not take down the paywall render or starve
124
+ // the remaining listeners of this update.
125
+ console.warn(`[BillDog] state listener for "${key}" threw:`, err);
126
+ }
127
+ }
128
+ }
129
+ }
130
+ exports.PaywallStateManager = PaywallStateManager;
131
+ /**
132
+ * Paywall-wide key holding the live product selection.
133
+ *
134
+ * Deliberately NOT `element:{id}.selectedIndex` — that form is scoped to a single
135
+ * products component, while Dynamic Values need one well-known key to react to. The
136
+ * name matches the variable key the DV bag emits (`products.selectedIndex`), so an
137
+ * authored condition and the state driving it are spelled identically.
138
+ *
139
+ * Parity: Android `PaywallStateManager.SELECTED_INDEX_KEY`, iOS
140
+ * `PaywallView.selectedIndexStateKey`.
141
+ */
142
+ PaywallStateManager.SELECTED_INDEX_KEY = 'products.selectedIndex';
143
+ /** Shared instance. Native uses a singleton (Kotlin `object` / Swift `.shared`); the
144
+ * class stays exported so tests can build an isolated store. */
145
+ PaywallStateManager.shared = new PaywallStateManager();
146
+ /** Shared instance — the one PaywallView threads through the component tree. */
147
+ exports.paywallStateManager = PaywallStateManager.shared;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * PaywallView - Main React Native component for rendering paywalls
3
+ *
4
+ * Credentials and API base URL are resolved from BillDogConfiguration.
5
+ * Pass an `apiKey` prop only for backward compatibility.
6
+ */
7
+ import React from 'react';
8
+ import { PaywallConfiguration } from './types';
9
+ interface PaywallViewProps {
10
+ /**
11
+ * Paywall identifier to fetch from API
12
+ */
13
+ paywallIdentifier?: string;
14
+ /**
15
+ * Direct configuration (skips API fetch)
16
+ */
17
+ configuration?: PaywallConfiguration;
18
+ /**
19
+ * @deprecated Use BillDogConfiguration.configure({ apiKey }) instead
20
+ */
21
+ apiKey?: string;
22
+ /**
23
+ * Called when user interacts with buttons
24
+ */
25
+ onButtonPress?: (componentId: string, action: string) => void;
26
+ /**
27
+ * Called when paywall is dismissed
28
+ */
29
+ onDismiss?: () => void;
30
+ /**
31
+ * Loading component (optional)
32
+ */
33
+ LoadingComponent?: React.ComponentType;
34
+ /**
35
+ * Error component (optional)
36
+ */
37
+ ErrorComponent?: React.ComponentType<{
38
+ error: Error;
39
+ }>;
40
+ }
41
+ export declare const PaywallView: React.FC<PaywallViewProps>;
42
+ export {};