@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,266 @@
1
+ /**
2
+ * OAuthButtonComponent - React Native
3
+ *
4
+ * Social login button with provider branding.
5
+ * Features:
6
+ * - Provider-specific branding (Google, Apple, Facebook)
7
+ * - Loading, success, and error states
8
+ * - Native bridge delegation for OAuth flow
9
+ */
10
+
11
+ import React, { useState, useCallback, useEffect, useMemo } from 'react';
12
+ import {
13
+ View,
14
+ Text,
15
+ TouchableOpacity,
16
+ StyleSheet,
17
+ ActivityIndicator,
18
+ NativeEventEmitter,
19
+ NativeModules,
20
+ Platform,
21
+ } from 'react-native';
22
+ import { PaywallComponent } from '../types';
23
+
24
+ type OAuthProvider = 'google' | 'apple' | 'facebook';
25
+ type ButtonState = 'idle' | 'loading' | 'success' | 'error';
26
+
27
+ interface OAuthButtonComponentProps {
28
+ component: PaywallComponent;
29
+ stateManager?: any;
30
+ isPreviewMode?: boolean;
31
+ onOAuthRequest?: (provider: OAuthProvider, componentId: string) => void;
32
+ }
33
+
34
+ // Provider branding
35
+ const PROVIDER_BRANDING: Record<OAuthProvider, { bg: string; text: string; icon: string }> = {
36
+ google: {
37
+ bg: '#FFFFFF',
38
+ text: '#1F1F1F',
39
+ icon: 'G',
40
+ },
41
+ apple: {
42
+ bg: '#000000',
43
+ text: '#FFFFFF',
44
+ icon: '',
45
+ },
46
+ facebook: {
47
+ bg: '#1877F2',
48
+ text: '#FFFFFF',
49
+ icon: 'f',
50
+ },
51
+ };
52
+
53
+ export const OAuthButtonComponent: React.FC<OAuthButtonComponentProps> = ({
54
+ component,
55
+ stateManager,
56
+ isPreviewMode = false,
57
+ onOAuthRequest,
58
+ }) => {
59
+ const props = component.properties as any;
60
+ const componentId = component.id;
61
+
62
+ // Configuration
63
+ const provider: OAuthProvider = props.provider || 'google';
64
+ const buttonText = props.buttonText || `Continue with ${provider.charAt(0).toUpperCase() + provider.slice(1)}`;
65
+ const showIcon = props.showIcon !== false;
66
+ const iconPosition = props.iconPosition || 'left';
67
+
68
+ const buttonStyle = props.buttonStyle || 'branded';
69
+ const customBackgroundColor = props.backgroundColor;
70
+ const customTextColor = props.textColor;
71
+ const borderColor = props.borderColor || '#D1D5DB';
72
+ const borderWidth = props.borderWidth || 1;
73
+ const borderRadius = props.borderRadius || 8;
74
+
75
+ const buttonHeight = props.buttonHeight || 48;
76
+ const iconSize = props.iconSize || 20;
77
+ const iconGap = props.iconGap || 12;
78
+
79
+ const fontSize = props.fontSize || 16;
80
+ const fontWeight = props.fontWeight || '500';
81
+
82
+ const loadingText = props.loadingText || 'Signing in...';
83
+ const showLoadingSpinner = props.showLoadingSpinner !== false;
84
+
85
+ const errorText = props.errorText || 'Sign in failed. Try again.';
86
+ const successText = props.successText || 'Signed in!';
87
+
88
+ const [buttonState, setButtonState] = useState<ButtonState>('idle');
89
+ const [error, setError] = useState<string | null>(null);
90
+
91
+ // Get provider branding
92
+ const branding = PROVIDER_BRANDING[provider];
93
+
94
+ // Compute button colors
95
+ const computedColors = useMemo(() => {
96
+ if (buttonStyle === 'branded') {
97
+ return {
98
+ bg: branding.bg,
99
+ text: branding.text,
100
+ border: branding.bg,
101
+ };
102
+ }
103
+ if (buttonStyle === 'outline') {
104
+ return {
105
+ bg: 'transparent',
106
+ text: customTextColor || '#1F2937',
107
+ border: borderColor,
108
+ };
109
+ }
110
+ return {
111
+ bg: customBackgroundColor || branding.bg,
112
+ text: customTextColor || branding.text,
113
+ border: borderColor,
114
+ };
115
+ }, [buttonStyle, branding, customBackgroundColor, customTextColor, borderColor]);
116
+
117
+ // Handle button press
118
+ const handlePress = useCallback(() => {
119
+ if (buttonState !== 'idle') return;
120
+
121
+ setButtonState('loading');
122
+ setError(null);
123
+
124
+ // Call native OAuth handler
125
+ if (onOAuthRequest) {
126
+ onOAuthRequest(provider, componentId);
127
+ }
128
+
129
+ // Simulate success in preview mode
130
+ if (isPreviewMode) {
131
+ setTimeout(() => {
132
+ setButtonState('success');
133
+ if (stateManager) {
134
+ stateManager.updateState(`element:${componentId}.authenticated`, true);
135
+ stateManager.updateState(`element:${componentId}.authResult`, {
136
+ userId: 'preview_user_123',
137
+ email: 'user@example.com',
138
+ displayName: 'Preview User',
139
+ });
140
+ }
141
+
142
+ // Reset after success
143
+ setTimeout(() => setButtonState('idle'), 2000);
144
+ }, 1500);
145
+ }
146
+ }, [buttonState, provider, componentId, isPreviewMode, onOAuthRequest, stateManager]);
147
+
148
+ // External method to set result (called by native bridge)
149
+ useEffect(() => {
150
+ if (stateManager) {
151
+ // Subscribe to auth result events
152
+ return stateManager.subscribeToState(
153
+ `element:${componentId}.authComplete`,
154
+ (result: { success: boolean; error?: string; data?: any }) => {
155
+ if (result.success) {
156
+ setButtonState('success');
157
+ stateManager.updateState(`element:${componentId}.authenticated`, true);
158
+ stateManager.updateState(`element:${componentId}.authResult`, result.data);
159
+ setTimeout(() => setButtonState('idle'), 2000);
160
+ } else {
161
+ setButtonState('error');
162
+ setError(result.error || errorText);
163
+ stateManager.updateState(`element:${componentId}.authenticated`, false);
164
+ stateManager.updateState(`element:${componentId}.authError`, result.error);
165
+ setTimeout(() => {
166
+ setButtonState('idle');
167
+ setError(null);
168
+ }, 3000);
169
+ }
170
+ }
171
+ );
172
+ }
173
+ }, [componentId, stateManager, errorText]);
174
+
175
+ // Get current display text
176
+ const currentText = useMemo(() => {
177
+ switch (buttonState) {
178
+ case 'loading':
179
+ return loadingText;
180
+ case 'success':
181
+ return successText;
182
+ case 'error':
183
+ return error || errorText;
184
+ default:
185
+ return buttonText;
186
+ }
187
+ }, [buttonState, loadingText, successText, errorText, error, buttonText]);
188
+
189
+ // Render provider icon
190
+ const renderIcon = () => {
191
+ if (!showIcon || buttonState !== 'idle') return null;
192
+
193
+ // Use text-based icons (in real app, use actual SVG or Icon components)
194
+ const iconText = provider === 'google' ? '🔵' : provider === 'apple' ? '🍎' : '📘';
195
+
196
+ return (
197
+ <Text style={{ fontSize: iconSize }}>
198
+ {iconText}
199
+ </Text>
200
+ );
201
+ };
202
+
203
+ return (
204
+ <TouchableOpacity
205
+ onPress={handlePress}
206
+ disabled={buttonState === 'loading' || buttonState === 'success'}
207
+ activeOpacity={0.8}
208
+ style={[
209
+ styles.button,
210
+ {
211
+ height: buttonHeight,
212
+ backgroundColor: computedColors.bg,
213
+ borderColor: buttonState === 'error' ? '#EF4444' : computedColors.border,
214
+ borderWidth,
215
+ borderRadius,
216
+ flexDirection: iconPosition === 'right' ? 'row-reverse' : 'row',
217
+ gap: iconGap,
218
+ opacity: buttonState === 'loading' ? 0.8 : 1,
219
+ },
220
+ ]}
221
+ >
222
+ {/* Loading Spinner */}
223
+ {buttonState === 'loading' && showLoadingSpinner && (
224
+ <ActivityIndicator size="small" color={computedColors.text} />
225
+ )}
226
+
227
+ {/* Success Check */}
228
+ {buttonState === 'success' && (
229
+ <Text style={{ fontSize: iconSize, color: '#10B981' }}>✓</Text>
230
+ )}
231
+
232
+ {/* Error Icon */}
233
+ {buttonState === 'error' && (
234
+ <Text style={{ fontSize: iconSize, color: '#EF4444' }}>⚠</Text>
235
+ )}
236
+
237
+ {/* Provider Icon */}
238
+ {buttonState === 'idle' && renderIcon()}
239
+
240
+ {/* Button Text */}
241
+ <Text
242
+ style={[
243
+ styles.buttonText,
244
+ {
245
+ fontSize,
246
+ fontWeight: fontWeight as any,
247
+ color: buttonState === 'error' ? '#EF4444' : computedColors.text,
248
+ },
249
+ ]}
250
+ >
251
+ {currentText}
252
+ </Text>
253
+ </TouchableOpacity>
254
+ );
255
+ };
256
+
257
+ const styles = StyleSheet.create({
258
+ button: {
259
+ width: '100%',
260
+ alignItems: 'center',
261
+ justifyContent: 'center',
262
+ },
263
+ buttonText: {
264
+ textAlign: 'center',
265
+ },
266
+ });
@@ -0,0 +1,366 @@
1
+ /**
2
+ * ProgressLoaderComponent - Native React Native implementation
3
+ * Supports 5 display modes: spinner, loading-text, bar, circular, loader-set
4
+ */
5
+
6
+ import React, { useEffect, useRef } from 'react';
7
+ import {
8
+ View,
9
+ Text,
10
+ StyleSheet,
11
+ Animated,
12
+ Easing,
13
+ ViewStyle,
14
+ } from 'react-native';
15
+ import Svg, { Circle, G } from 'react-native-svg';
16
+ import { PaywallComponent } from '../types';
17
+
18
+ interface ProgressLoaderComponentProps {
19
+ component: PaywallComponent;
20
+ stateManager?: any;
21
+ isPreviewMode?: boolean;
22
+ }
23
+
24
+ // Helper to extract color from fill value (handles gradients by taking first stop)
25
+ const getColorFromFill = (fill: any, fallback: string = '#3B82F6'): string => {
26
+ if (!fill) return fallback;
27
+ if (typeof fill === 'string') return fill;
28
+
29
+ if (typeof fill === 'object') {
30
+ if (fill.type === 'solid' && fill.color) return fill.color;
31
+ if (fill.type === 'gradient' && fill.stops?.length > 0) {
32
+ return fill.stops[0].color || fallback;
33
+ }
34
+ if (fill.color) return fill.color;
35
+ }
36
+
37
+ return fallback;
38
+ };
39
+
40
+ // Animated circle component for spinner
41
+ const AnimatedCircle = Animated.createAnimatedComponent(Circle);
42
+
43
+ export const ProgressLoaderComponent: React.FC<ProgressLoaderComponentProps> = ({
44
+ component,
45
+ stateManager,
46
+ isPreviewMode = false,
47
+ }) => {
48
+ const props = component.properties as any;
49
+ const displayMode = props.displayMode || 'circular';
50
+ const progress = props.progress ?? 65;
51
+ const animated = props.animated !== false;
52
+
53
+ // Spinner properties
54
+ const spinnerSize = props.spinnerSize ?? 48;
55
+ const spinnerThickness = props.spinnerThickness ?? 4;
56
+ const spinnerFill = getColorFromFill(props.spinnerFill, '#3B82F6');
57
+ const spinnerTrackFill = getColorFromFill(props.spinnerTrackFill, '#E5E7EB');
58
+
59
+ // Bar properties
60
+ const barHeight = props.barHeight ?? 8;
61
+ const barFill = getColorFromFill(props.barFill, '#3B82F6');
62
+ const barTrackFill = getColorFromFill(props.barTrackFill, '#E5E7EB');
63
+ const barBorderRadius = props.barBorderRadius ?? 4;
64
+
65
+ // Circular properties
66
+ const circularSize = props.circularSize ?? 80;
67
+ const circularThickness = props.circularThickness ?? 8;
68
+ const circularFill = getColorFromFill(props.circularFill, '#3B82F6');
69
+ const circularTrackFill = getColorFromFill(props.circularTrackFill, '#E5E7EB');
70
+ const showPercentage = props.showPercentage !== false;
71
+ const percentageFill = getColorFromFill(props.percentageFill, '#1F2937');
72
+ const percentageFontSize = props.percentageFontSize ?? 18;
73
+
74
+ // Loading text properties
75
+ const loadingText = props.loadingText || 'Loading...';
76
+ const loadingTextFill = getColorFromFill(props.loadingTextFill, '#1F2937');
77
+ const loadingTextSize = props.loadingTextSize ?? 16;
78
+
79
+ // Loader-set properties
80
+ const loaderCount = props.loaderCount ?? 3;
81
+ const loaders: { label: string; progress: number }[] = [];
82
+ for (let i = 1; i <= loaderCount; i++) {
83
+ loaders.push({
84
+ label: props[`loader${i}Label`] || `Step ${i}`,
85
+ progress: props[`loader${i}Progress`] ?? Math.min(100, progress + (i - 1) * 10),
86
+ });
87
+ }
88
+
89
+ // Animation values
90
+ const spinRotation = useRef(new Animated.Value(0)).current;
91
+ const progressAnim = useRef(new Animated.Value(0)).current;
92
+
93
+ // Spinner rotation animation
94
+ useEffect(() => {
95
+ if (displayMode === 'spinner' && animated) {
96
+ const animation = Animated.loop(
97
+ Animated.timing(spinRotation, {
98
+ toValue: 1,
99
+ duration: 1000,
100
+ easing: Easing.linear,
101
+ useNativeDriver: true,
102
+ })
103
+ );
104
+ animation.start();
105
+ return () => animation.stop();
106
+ }
107
+ }, [displayMode, animated, spinRotation]);
108
+
109
+ // Progress animation
110
+ useEffect(() => {
111
+ if (animated) {
112
+ Animated.timing(progressAnim, {
113
+ toValue: progress,
114
+ duration: 500,
115
+ easing: Easing.out(Easing.quad),
116
+ useNativeDriver: false,
117
+ }).start();
118
+ } else {
119
+ progressAnim.setValue(progress);
120
+ }
121
+ }, [progress, animated, progressAnim]);
122
+
123
+ // Render spinner mode
124
+ const renderSpinner = () => {
125
+ const rotation = spinRotation.interpolate({
126
+ inputRange: [0, 1],
127
+ outputRange: ['0deg', '360deg'],
128
+ });
129
+
130
+ const radius = (spinnerSize - spinnerThickness) / 2;
131
+ const circumference = 2 * Math.PI * radius;
132
+
133
+ return (
134
+ <View style={styles.centered}>
135
+ <Animated.View style={{ transform: [{ rotate: rotation }] }}>
136
+ <Svg width={spinnerSize} height={spinnerSize}>
137
+ <Circle
138
+ cx={spinnerSize / 2}
139
+ cy={spinnerSize / 2}
140
+ r={radius}
141
+ stroke={spinnerTrackFill}
142
+ strokeWidth={spinnerThickness}
143
+ fill="none"
144
+ />
145
+ <Circle
146
+ cx={spinnerSize / 2}
147
+ cy={spinnerSize / 2}
148
+ r={radius}
149
+ stroke={spinnerFill}
150
+ strokeWidth={spinnerThickness}
151
+ fill="none"
152
+ strokeDasharray={`${circumference * 0.25} ${circumference * 0.75}`}
153
+ strokeLinecap="round"
154
+ />
155
+ </Svg>
156
+ </Animated.View>
157
+ </View>
158
+ );
159
+ };
160
+
161
+ // Render loading text mode
162
+ const renderLoadingText = () => {
163
+ return (
164
+ <View style={styles.centered}>
165
+ <Text style={[styles.loadingText, { color: loadingTextFill, fontSize: loadingTextSize }]}>
166
+ {loadingText}
167
+ </Text>
168
+ </View>
169
+ );
170
+ };
171
+
172
+ // Render bar mode
173
+ const renderBar = () => {
174
+ const barWidth = progressAnim.interpolate({
175
+ inputRange: [0, 100],
176
+ outputRange: ['0%', '100%'],
177
+ extrapolate: 'clamp',
178
+ });
179
+
180
+ return (
181
+ <View style={styles.barContainer}>
182
+ <View
183
+ style={[
184
+ styles.barTrack,
185
+ {
186
+ height: barHeight,
187
+ backgroundColor: barTrackFill,
188
+ borderRadius: barBorderRadius,
189
+ },
190
+ ]}
191
+ >
192
+ <Animated.View
193
+ style={[
194
+ styles.barFill,
195
+ {
196
+ width: barWidth,
197
+ backgroundColor: barFill,
198
+ borderRadius: barBorderRadius,
199
+ },
200
+ ]}
201
+ />
202
+ </View>
203
+ </View>
204
+ );
205
+ };
206
+
207
+ // Render circular mode
208
+ const renderCircular = () => {
209
+ const radius = (circularSize - circularThickness) / 2;
210
+ const circumference = 2 * Math.PI * radius;
211
+
212
+ const strokeDashoffset = progressAnim.interpolate({
213
+ inputRange: [0, 100],
214
+ outputRange: [circumference, 0],
215
+ extrapolate: 'clamp',
216
+ });
217
+
218
+ return (
219
+ <View style={[styles.centered, { width: circularSize, height: circularSize }]}>
220
+ <Svg width={circularSize} height={circularSize}>
221
+ <G rotation="-90" origin={`${circularSize / 2}, ${circularSize / 2}`}>
222
+ <Circle
223
+ cx={circularSize / 2}
224
+ cy={circularSize / 2}
225
+ r={radius}
226
+ stroke={circularTrackFill}
227
+ strokeWidth={circularThickness}
228
+ fill="none"
229
+ />
230
+ <AnimatedCircle
231
+ cx={circularSize / 2}
232
+ cy={circularSize / 2}
233
+ r={radius}
234
+ stroke={circularFill}
235
+ strokeWidth={circularThickness}
236
+ fill="none"
237
+ strokeDasharray={circumference}
238
+ strokeDashoffset={strokeDashoffset}
239
+ strokeLinecap="round"
240
+ />
241
+ </G>
242
+ </Svg>
243
+ {showPercentage && (
244
+ <View style={StyleSheet.absoluteFill}>
245
+ <View style={styles.percentageContainer}>
246
+ <Text style={[styles.percentageText, { color: percentageFill, fontSize: percentageFontSize }]}>
247
+ {Math.round(progress)}%
248
+ </Text>
249
+ </View>
250
+ </View>
251
+ )}
252
+ </View>
253
+ );
254
+ };
255
+
256
+ // Render loader-set mode
257
+ const renderLoaderSet = () => {
258
+ return (
259
+ <View style={styles.loaderSetContainer}>
260
+ {loaders.map((loader, index) => (
261
+ <View key={index} style={styles.loaderSetItem}>
262
+ <View style={styles.loaderSetHeader}>
263
+ <Text style={styles.loaderSetLabel}>{loader.label}</Text>
264
+ <Text style={styles.loaderSetProgress}>{loader.progress}%</Text>
265
+ </View>
266
+ <View
267
+ style={[
268
+ styles.barTrack,
269
+ {
270
+ height: barHeight,
271
+ backgroundColor: barTrackFill,
272
+ borderRadius: barBorderRadius,
273
+ },
274
+ ]}
275
+ >
276
+ <View
277
+ style={[
278
+ styles.barFill,
279
+ {
280
+ width: `${loader.progress}%`,
281
+ backgroundColor: barFill,
282
+ borderRadius: barBorderRadius,
283
+ },
284
+ ]}
285
+ />
286
+ </View>
287
+ </View>
288
+ ))}
289
+ </View>
290
+ );
291
+ };
292
+
293
+ // Render based on display mode
294
+ const renderContent = () => {
295
+ switch (displayMode) {
296
+ case 'spinner':
297
+ return renderSpinner();
298
+ case 'loading-text':
299
+ return renderLoadingText();
300
+ case 'bar':
301
+ return renderBar();
302
+ case 'circular':
303
+ return renderCircular();
304
+ case 'loader-set':
305
+ return renderLoaderSet();
306
+ default:
307
+ return renderCircular();
308
+ }
309
+ };
310
+
311
+ return <View style={styles.container}>{renderContent()}</View>;
312
+ };
313
+
314
+ const styles = StyleSheet.create({
315
+ container: {
316
+ alignItems: 'center',
317
+ justifyContent: 'center',
318
+ },
319
+ centered: {
320
+ alignItems: 'center',
321
+ justifyContent: 'center',
322
+ },
323
+ loadingText: {
324
+ fontWeight: '500',
325
+ },
326
+ barContainer: {
327
+ width: '100%',
328
+ },
329
+ barTrack: {
330
+ width: '100%',
331
+ overflow: 'hidden',
332
+ },
333
+ barFill: {
334
+ height: '100%',
335
+ },
336
+ percentageContainer: {
337
+ flex: 1,
338
+ alignItems: 'center',
339
+ justifyContent: 'center',
340
+ },
341
+ percentageText: {
342
+ fontWeight: '600',
343
+ },
344
+ loaderSetContainer: {
345
+ width: '100%',
346
+ gap: 16,
347
+ },
348
+ loaderSetItem: {
349
+ width: '100%',
350
+ gap: 6,
351
+ },
352
+ loaderSetHeader: {
353
+ flexDirection: 'row',
354
+ justifyContent: 'space-between',
355
+ alignItems: 'center',
356
+ },
357
+ loaderSetLabel: {
358
+ fontSize: 14,
359
+ fontWeight: '500',
360
+ color: '#374151',
361
+ },
362
+ loaderSetProgress: {
363
+ fontSize: 12,
364
+ color: '#6B7280',
365
+ },
366
+ });