@tryheliumai/paywall-sdk-react-native 0.2.22 → 3.0.5

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 (38) hide show
  1. package/PaywallSdkReactNative.podspec +2 -2
  2. package/ios/HeliumSwiftInterface.swift +163 -76
  3. package/ios/RCTHeliumBridge.m +5 -0
  4. package/lib/commonjs/handlers/revenuecat.js +7 -3
  5. package/lib/commonjs/handlers/revenuecat.js.map +1 -1
  6. package/lib/commonjs/index.js +2 -14
  7. package/lib/commonjs/index.js.map +1 -1
  8. package/lib/commonjs/native-interface.js +133 -182
  9. package/lib/commonjs/native-interface.js.map +1 -1
  10. package/lib/commonjs/types.js +4 -0
  11. package/lib/commonjs/types.js.map +1 -1
  12. package/lib/module/handlers/revenuecat.js +8 -3
  13. package/lib/module/handlers/revenuecat.js.map +1 -1
  14. package/lib/module/index.js +1 -1
  15. package/lib/module/index.js.map +1 -1
  16. package/lib/module/native-interface.js +132 -178
  17. package/lib/module/native-interface.js.map +1 -1
  18. package/lib/module/types.js +4 -0
  19. package/lib/module/types.js.map +1 -1
  20. package/lib/typescript/commonjs/src/handlers/revenuecat.d.ts.map +1 -1
  21. package/lib/typescript/commonjs/src/index.d.ts +2 -2
  22. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/src/native-interface.d.ts +3 -21
  24. package/lib/typescript/commonjs/src/native-interface.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/src/types.d.ts +107 -6
  26. package/lib/typescript/commonjs/src/types.d.ts.map +1 -1
  27. package/lib/typescript/module/src/handlers/revenuecat.d.ts.map +1 -1
  28. package/lib/typescript/module/src/index.d.ts +2 -2
  29. package/lib/typescript/module/src/index.d.ts.map +1 -1
  30. package/lib/typescript/module/src/native-interface.d.ts +3 -21
  31. package/lib/typescript/module/src/native-interface.d.ts.map +1 -1
  32. package/lib/typescript/module/src/types.d.ts +107 -6
  33. package/lib/typescript/module/src/types.d.ts.map +1 -1
  34. package/package.json +1 -9
  35. package/src/handlers/revenuecat.ts +7 -3
  36. package/src/index.ts +10 -3
  37. package/src/native-interface.tsx +172 -191
  38. package/src/types.ts +147 -9
@@ -1,190 +1,112 @@
1
- import { findNodeHandle, NativeModules, View, NativeEventEmitter, requireNativeComponent } from 'react-native';
2
- import React, { createRef, useEffect, useState, createContext, useContext } from 'react';
3
- import type { HeliumConfig, HeliumUpsellViewProps, HeliumDownloadStatus, PaywallInfo } from './types';
1
+ import {
2
+ NativeModules,
3
+ NativeEventEmitter,
4
+ requireNativeComponent,
5
+ } from 'react-native';
6
+ import type {
7
+ HeliumConfig,
8
+ HeliumUpsellViewProps,
9
+ HeliumDownloadStatus,
10
+ PaywallInfo,
11
+ PresentUpsellParams,
12
+ PaywallEventHandlers,
13
+ HeliumPaywallEvent,
14
+ } from './types';
4
15
 
5
16
  const { HeliumBridge } = NativeModules;
6
17
  const heliumEventEmitter = new NativeEventEmitter(HeliumBridge);
7
18
 
8
19
  // Register the native component once at module level
9
- // This ensures it's only registered once, even during hot reloading
10
- export const NativeHeliumUpsellView = requireNativeComponent<HeliumUpsellViewProps>('HeliumUpsellView');
20
+ export const NativeHeliumUpsellView =
21
+ requireNativeComponent<HeliumUpsellViewProps>('HeliumUpsellView');
11
22
 
12
- let isProviderMounted = false;
13
23
  // Add a flag to track if initialization has occurred
14
24
  let isInitialized = false;
15
- // Add a promise to track when the provider is mounted
16
- let providerMountedPromise: Promise<void>;
17
- let resolveProviderMounted: () => void;
18
-
19
- // Initialize the promise
20
- providerMountedPromise = new Promise<void>((resolve) => {
21
- resolveProviderMounted = resolve;
22
- // If provider is already mounted, resolve immediately
23
- if (isProviderMounted) {
24
- resolve();
25
- }
26
- });
27
-
28
25
  // Add module-level download status tracking
29
26
  let globalDownloadStatus: HeliumDownloadStatus = 'notStarted';
30
27
  export const getDownloadStatus = () => globalDownloadStatus;
31
28
 
32
- // Create a context for the download status
33
- interface HeliumContextType {
34
- downloadStatus: HeliumDownloadStatus;
35
- setDownloadStatus: (status: HeliumDownloadStatus) => void;
36
- }
37
-
38
- const HeliumContext = createContext<HeliumContextType | undefined>(undefined);
39
-
40
- // Update the setter ref to also update global status
41
- let setDownloadStatusRef: ((status: HeliumDownloadStatus) => void) | null = null;
42
29
  const updateDownloadStatus = (status: HeliumDownloadStatus) => {
43
30
  globalDownloadStatus = status;
44
- setDownloadStatusRef?.(status);
45
- };
46
-
47
- // Hook to use the Helium context
48
- export const useHelium = () => {
49
- const context = useContext(HeliumContext);
50
- if (!context) {
51
- throw new Error('useHelium must be used within a HeliumProvider');
52
- }
53
- return context;
54
31
  };
55
32
 
56
- interface HeliumProviderProps {
57
- children: React.ReactNode;
58
- fallbackView?: React.ComponentType;
59
- }
60
-
61
- // Create a ref to store the fallback view reference
62
- const fallbackRef = createRef<View>();
63
- // Store a reference to the fallback view component
64
- let FallbackViewComponent: React.ComponentType | null = null;
65
-
66
- // Provider component to be rendered at the app root
67
- export const HeliumProvider = ({ children, fallbackView }: HeliumProviderProps) => {
68
- // TODO - deprecate fallbackView (and maybe HeliumProvider too?)
69
- if (fallbackView) {
70
- console.warn('HeliumProvider: fallbackView is deprecated. Use onFallback passed to presentUpsell instead.');
71
- }
72
- const FallbackView = (() => null);
73
- // Add state for download status
74
- const [downloadStatus, setDownloadStatus] = useState<HeliumDownloadStatus>('notStarted');
75
-
76
- // Store the setter in the ref so it can be accessed outside of components
77
- useEffect(() => {
78
- setDownloadStatusRef = setDownloadStatus;
79
- // Store the fallback view component for later use
80
- FallbackViewComponent = FallbackView;
81
- }, [setDownloadStatus, FallbackView]);
82
-
83
- useEffect(() => {
84
- isProviderMounted = true;
85
- // Resolve the promise when the provider is mounted
86
- resolveProviderMounted();
87
- return () => {
88
- isProviderMounted = false;
89
- setDownloadStatusRef = null;
90
- };
91
- }, []);
92
-
93
- return (
94
- <HeliumContext.Provider value={{ downloadStatus, setDownloadStatus }}>
95
- <View
96
- ref={fallbackRef}
97
- collapsable={false}
98
- style={{
99
- display: 'none'
100
- }}
101
- >
102
- <FallbackView />
103
- </View>
104
- {children}
105
- </HeliumContext.Provider>
106
- );
107
- };
108
-
109
- // Update initialize to accept full config
110
33
  export const initialize = async (config: HeliumConfig) => {
111
34
  // Early return if already initialized
112
35
  if (isInitialized) {
36
+ console.log('[Helium] Already initialized, skipping...');
113
37
  return;
114
38
  }
115
39
 
116
- // Wait for the provider to be mounted if it's not already
117
- if (!isProviderMounted) {
118
- await providerMountedPromise;
119
- }
120
-
121
- const viewTag = findNodeHandle(fallbackRef.current);
122
- if (!viewTag) {
123
- throw new Error('Failed to get fallback view reference. Make sure HeliumProvider is mounted with a fallback view.');
124
- }
125
-
126
- const purchaseHandler = {
127
- makePurchase: config.purchaseConfig.makePurchase,
128
- restorePurchases: config.purchaseConfig.restorePurchases,
129
- };
40
+ const purchaseHandler = config.purchaseConfig
41
+ ? {
42
+ makePurchase: config.purchaseConfig.makePurchase,
43
+ restorePurchases: config.purchaseConfig.restorePurchases,
44
+ }
45
+ : null;
130
46
 
131
47
  // Update download status to inProgress
132
48
  updateDownloadStatus('inProgress');
133
49
 
50
+ // Ensure these don't get added more than once
51
+ heliumEventEmitter.removeAllListeners('helium_paywall_event');
52
+ heliumEventEmitter.removeAllListeners('paywallEventHandlers');
53
+ heliumEventEmitter.removeAllListeners('helium_make_purchase');
54
+ heliumEventEmitter.removeAllListeners('helium_restore_purchases');
55
+
134
56
  // Set up event listeners
135
57
  heliumEventEmitter.addListener(
136
58
  'helium_paywall_event',
137
- (event: any) => {
59
+ (event: HeliumPaywallEvent) => {
138
60
  // Handle download status events
139
61
  if (event.type === 'paywallsDownloadSuccess') {
140
62
  updateDownloadStatus('success');
141
63
  } else if (event.type === 'paywallsDownloadError') {
142
64
  updateDownloadStatus('failed');
143
65
  }
144
- // Handle fallback view visibility
145
- else if (event.type === 'paywallOpen' && event.paywallTemplateName === 'Fallback') {
146
- if (fallbackRef.current) {
147
- fallbackRef.current.setNativeProps({
148
- style: { display: 'flex' }
149
- });
150
- }
151
- } else if (event.type === 'paywallClose' && event.paywallTemplateName === 'Fallback') {
152
- if (fallbackRef.current) {
153
- fallbackRef.current.setNativeProps({
154
- style: { display: 'none' }
155
- });
156
- }
157
- }
66
+
67
+ // Handle internal event logic first
68
+ handlePaywallEvent(event);
158
69
 
159
70
  // Forward all events to the callback provided in config
160
71
  config.onHeliumPaywallEvent(event);
161
72
  }
162
73
  );
163
74
 
164
- // Set up purchase event listener using the determined handler
75
+ // Set up paywall event handlers listener
165
76
  heliumEventEmitter.addListener(
166
- 'helium_make_purchase',
167
- async (event: { productId: string; transactionId: string }) => {
168
- const result = await purchaseHandler.makePurchase(event.productId);
169
- HeliumBridge.handlePurchaseResponse({
170
- transactionId: event.transactionId,
171
- status: result.status,
172
- error: result.error
173
- });
77
+ 'paywallEventHandlers',
78
+ (event: HeliumPaywallEvent) => {
79
+ callPaywallEventHandlers(event);
174
80
  }
175
81
  );
176
82
 
177
- // Set up restore purchases event listener using the determined handler
178
- heliumEventEmitter.addListener(
179
- 'helium_restore_purchases',
180
- async (event: { transactionId: string }) => {
181
- const success = await purchaseHandler.restorePurchases();
182
- HeliumBridge.handleRestoreResponse({
183
- transactionId: event.transactionId,
184
- status: success ? 'restored' : 'failed'
185
- });
186
- }
187
- );
83
+ // Set up purchase event listeners only if we have a purchase handler
84
+ if (purchaseHandler) {
85
+ // Set up purchase event listener using the determined handler
86
+ heliumEventEmitter.addListener(
87
+ 'helium_make_purchase',
88
+ async (event: { productId: string; transactionId: string }) => {
89
+ const result = await purchaseHandler.makePurchase(event.productId);
90
+ HeliumBridge.handlePurchaseResponse({
91
+ transactionId: event.transactionId,
92
+ status: result.status,
93
+ error: result.error,
94
+ });
95
+ }
96
+ );
97
+
98
+ // Set up restore purchases event listener using the determined handler
99
+ heliumEventEmitter.addListener(
100
+ 'helium_restore_purchases',
101
+ async (event: { transactionId: string }) => {
102
+ const success = await purchaseHandler.restorePurchases();
103
+ HeliumBridge.handleRestoreResponse({
104
+ transactionId: event.transactionId,
105
+ status: success ? 'restored' : 'failed',
106
+ });
107
+ }
108
+ );
109
+ }
188
110
 
189
111
  let fallbackBundleUrlString;
190
112
  let fallbackBundleString;
@@ -212,30 +134,34 @@ export const initialize = async (config: HeliumConfig) => {
212
134
  HeliumBridge.initialize(
213
135
  {
214
136
  apiKey: config.apiKey,
215
- fallbackPaywall: viewTag,
216
- triggers: config.triggers || [],
217
137
  customUserId: config.customUserId || null,
218
138
  customAPIEndpoint: config.customAPIEndpoint || null,
219
- customUserTraits: config.customUserTraits == null ? {} : config.customUserTraits,
139
+ customUserTraits: convertBooleansToMarkers(
140
+ config.customUserTraits == null ? {} : config.customUserTraits
141
+ ),
220
142
  revenueCatAppUserId: config.revenueCatAppUserId,
221
143
  fallbackBundleUrlString: fallbackBundleUrlString,
222
144
  fallbackBundleString: fallbackBundleString,
145
+ paywallLoadingConfig: convertBooleansToMarkers(
146
+ config.paywallLoadingConfig
147
+ ),
148
+ useDefaultDelegate: !config.purchaseConfig,
223
149
  },
224
150
  {}
225
151
  );
226
-
152
+
227
153
  // Mark as initialized after successful initialization
228
154
  isInitialized = true;
229
155
  };
230
156
 
231
- // Update the other methods to be synchronous
157
+ let paywallEventHandlers: PaywallEventHandlers | undefined;
158
+ let presentOnFallback: (() => void) | undefined;
232
159
  export const presentUpsell = ({
233
160
  triggerName,
234
161
  onFallback,
235
- }: {
236
- triggerName: string;
237
- onFallback?: () => void;
238
- }) => {
162
+ eventHandlers,
163
+ customPaywallTraits,
164
+ }: PresentUpsellParams) => {
239
165
  HeliumBridge.canPresentUpsell(
240
166
  triggerName,
241
167
  (canPresent: boolean, reason: string) => {
@@ -249,9 +175,16 @@ export const presentUpsell = ({
249
175
  }
250
176
 
251
177
  try {
252
- HeliumBridge.presentUpsell(triggerName);
178
+ paywallEventHandlers = eventHandlers;
179
+ presentOnFallback = onFallback;
180
+ HeliumBridge.presentUpsell(
181
+ triggerName,
182
+ convertBooleansToMarkers(customPaywallTraits) || null
183
+ );
253
184
  } catch (error) {
254
185
  console.log('[Helium] Present error', error);
186
+ paywallEventHandlers = undefined;
187
+ presentOnFallback = undefined;
255
188
  onFallback?.();
256
189
  HeliumBridge.fallbackOpenOrCloseEvent(triggerName, true, 'presented');
257
190
  }
@@ -259,6 +192,67 @@ export const presentUpsell = ({
259
192
  );
260
193
  };
261
194
 
195
+ function callPaywallEventHandlers(event: HeliumPaywallEvent) {
196
+ if (paywallEventHandlers) {
197
+ switch (event.type) {
198
+ case 'paywallOpen':
199
+ paywallEventHandlers?.onOpen?.({
200
+ type: 'paywallOpen',
201
+ triggerName: event.triggerName ?? 'unknown',
202
+ paywallName: event.paywallName ?? 'unknown',
203
+ isSecondTry: event.isSecondTry ?? false,
204
+ viewType: 'presented',
205
+ });
206
+ break;
207
+ case 'paywallClose':
208
+ paywallEventHandlers?.onClose?.({
209
+ type: 'paywallClose',
210
+ triggerName: event.triggerName ?? 'unknown',
211
+ paywallName: event.paywallName ?? 'unknown',
212
+ isSecondTry: event.isSecondTry ?? false,
213
+ });
214
+ break;
215
+ case 'paywallDismissed':
216
+ paywallEventHandlers?.onDismissed?.({
217
+ type: 'paywallDismissed',
218
+ triggerName: event.triggerName ?? 'unknown',
219
+ paywallName: event.paywallName ?? 'unknown',
220
+ isSecondTry: event.isSecondTry ?? false,
221
+ });
222
+ break;
223
+ case 'purchaseSucceeded':
224
+ paywallEventHandlers?.onPurchaseSucceeded?.({
225
+ type: 'purchaseSucceeded',
226
+ productId: event.productId ?? 'unknown',
227
+ triggerName: event.triggerName ?? 'unknown',
228
+ paywallName: event.paywallName ?? 'unknown',
229
+ isSecondTry: event.isSecondTry ?? false,
230
+ });
231
+ break;
232
+ }
233
+ }
234
+ }
235
+
236
+ function handlePaywallEvent(event: HeliumPaywallEvent) {
237
+ switch (event.type) {
238
+ case 'paywallClose':
239
+ if (!event.isSecondTry) {
240
+ paywallEventHandlers = undefined;
241
+ }
242
+ presentOnFallback = undefined;
243
+ break;
244
+ case 'paywallSkipped':
245
+ paywallEventHandlers = undefined;
246
+ presentOnFallback = undefined;
247
+ break;
248
+ case 'paywallOpenFailed':
249
+ paywallEventHandlers = undefined;
250
+ presentOnFallback?.();
251
+ presentOnFallback = undefined;
252
+ break;
253
+ }
254
+ }
255
+
262
256
  export const hideUpsell = () => {
263
257
  HeliumBridge.hideUpsell();
264
258
  };
@@ -295,52 +289,39 @@ export const handleDeepLink = async (url: string | null): Promise<boolean> => {
295
289
  console.log('[Helium] Handled deep link:', handled);
296
290
  resolve(handled);
297
291
  });
292
+ } else {
293
+ resolve(false);
298
294
  }
299
295
  });
300
296
  };
301
297
 
302
- // Update the UpsellView component to handle the style prop
303
- export const UpsellView: React.FC<HeliumUpsellViewProps & {
304
- fallbackViewProps?: Record<string, any>;
305
- fallbackViewWrapperStyles?: Record<string, any>;
306
- }> = ({ trigger, fallbackViewProps, fallbackViewWrapperStyles }) => {
307
- const { downloadStatus } = useHelium();
308
-
309
- const showFallback = downloadStatus === 'notStarted' ||
310
- downloadStatus === 'inProgress' ||
311
- downloadStatus === 'failed';
312
-
313
- useEffect(() => {
314
- if (showFallback && FallbackViewComponent) {
315
- HeliumBridge.fallbackOpenOrCloseEvent(trigger, true, 'embedded');
316
- }
317
- return () => {
318
- if (showFallback && FallbackViewComponent) {
319
- HeliumBridge.fallbackOpenOrCloseEvent(trigger, false, 'embedded');
320
- }
321
- };
322
- }, [showFallback, trigger]);
323
-
324
- // If download status is notStarted or inProgress, we haven't fully initialized yet
325
- // In this case, we should render the fallback view
326
- if (showFallback) {
327
- // If we have a fallback view component, render it
328
- if (FallbackViewComponent) {
329
- return (
330
- <View style={fallbackViewWrapperStyles}>
331
- <FallbackViewComponent {...fallbackViewProps} />
332
- </View>
333
- );
334
- }
335
-
336
- return null;
337
- }
338
-
339
- // Use NativeHeliumUpsellView directly
340
- return <NativeHeliumUpsellView trigger={trigger} />;
298
+ export const setRevenueCatAppUserId = (rcAppUserId: string) => {
299
+ HeliumBridge.setRevenueCatAppUserId(rcAppUserId);
341
300
  };
342
301
 
343
302
  export const HELIUM_CTA_NAMES = {
344
303
  SCHEDULE_CALL: 'schedule_call',
345
304
  SUBSCRIBE_BUTTON: 'subscribe_button',
305
+ };
306
+
307
+ function convertBooleansToMarkers(
308
+ input: Record<string, any> | undefined
309
+ ): Record<string, any> | undefined {
310
+ if (!input) return undefined;
311
+
312
+ const result: Record<string, any> = {};
313
+ for (const [key, value] of Object.entries(input)) {
314
+ result[key] = convertValueBooleansToMarkers(value);
315
+ }
316
+ return result;
317
+ }
318
+ function convertValueBooleansToMarkers(value: any): any {
319
+ if (typeof value === 'boolean') {
320
+ return value ? '__helium_rn_bool_true__' : '__helium_rn_bool_false__';
321
+ } else if (value && typeof value === 'object' && !Array.isArray(value)) {
322
+ return convertBooleansToMarkers(value);
323
+ } else if (value && Array.isArray(value)) {
324
+ return value.map(convertValueBooleansToMarkers);
325
+ }
326
+ return value;
346
327
  }
package/src/types.ts CHANGED
@@ -1,9 +1,18 @@
1
- export type HeliumTransactionStatus = 'purchased' | 'failed' | 'cancelled' | 'pending' | 'restored';
1
+ export type HeliumTransactionStatus =
2
+ | 'purchased'
3
+ | 'failed'
4
+ | 'cancelled'
5
+ | 'pending'
6
+ | 'restored';
2
7
  export type HeliumPurchaseResult = {
3
8
  status: HeliumTransactionStatus;
4
9
  error?: string; // Optional error message
5
10
  };
6
- export type HeliumDownloadStatus = 'success' | 'failed' | 'inProgress' | 'notStarted';
11
+ export type HeliumDownloadStatus =
12
+ | 'success'
13
+ | 'failed'
14
+ | 'inProgress'
15
+ | 'notStarted';
7
16
 
8
17
  // --- Purchase Configuration Types ---
9
18
 
@@ -12,9 +21,6 @@ export type HeliumDownloadStatus = 'success' | 'failed' | 'inProgress' | 'notSta
12
21
  export interface HeliumPurchaseConfig {
13
22
  makePurchase: (productId: string) => Promise<HeliumPurchaseResult>;
14
23
  restorePurchases: () => Promise<boolean>;
15
-
16
- /** Optional RevenueCat API Key. If not provided, RevenueCat must be configured elsewhere. */
17
- apiKey?: string;
18
24
  }
19
25
 
20
26
  // Helper function for creating Custom Purchase Config
@@ -28,19 +34,151 @@ export function createCustomPurchaseConfig(callbacks: {
28
34
  };
29
35
  }
30
36
 
37
+ export type TriggerLoadingConfig = {
38
+ /** Whether to show loading state for this trigger. Set to nil to use the global `useLoadingState` setting. */
39
+ useLoadingState?: boolean;
40
+ /** Maximum seconds to show loading for this trigger. Set to nil to use the global `loadingBudget` setting. */
41
+ loadingBudget?: number;
42
+ };
43
+
44
+ export type HeliumPaywallLoadingConfig = {
45
+ /**
46
+ * Whether to show a loading state while fetching paywall configuration.
47
+ * When true, shows a loading view for up to `loadingBudget` seconds before falling back.
48
+ * Default: true
49
+ */
50
+ useLoadingState?: boolean;
51
+ /**
52
+ * Maximum time (in seconds) to show the loading state before displaying fallback.
53
+ * After this timeout, the fallback view will be shown even if the paywall is still downloading.
54
+ * Default: 2.0 seconds
55
+ */
56
+ loadingBudget?: number;
57
+ /**
58
+ * Optional per-trigger loading configuration overrides.
59
+ * Use this to customize loading behavior for specific triggers.
60
+ * Keys are trigger names, values are TriggerLoadingConfig instances.
61
+ * Example: Disable loading for "onboarding" trigger while keeping it for others.
62
+ */
63
+ perTriggerLoadingConfig?: Record<string, TriggerLoadingConfig>;
64
+ };
65
+
66
+ // Event handler types for per-presentation event handling
67
+ export interface PaywallEventHandlers {
68
+ onOpen?: (event: PaywallOpenEvent) => void;
69
+ onClose?: (event: PaywallCloseEvent) => void;
70
+ onDismissed?: (event: PaywallDismissedEvent) => void;
71
+ onPurchaseSucceeded?: (event: PurchaseSucceededEvent) => void;
72
+ }
73
+
74
+ // Typed event interfaces
75
+ export interface PaywallOpenEvent {
76
+ type: 'paywallOpen';
77
+ triggerName: string;
78
+ paywallName: string;
79
+ isSecondTry: boolean;
80
+ viewType?: 'presented' | 'embedded' | 'triggered';
81
+ }
82
+
83
+ export interface PaywallCloseEvent {
84
+ type: 'paywallClose';
85
+ triggerName: string;
86
+ paywallName: string;
87
+ isSecondTry: boolean;
88
+ }
89
+
90
+ export interface PaywallDismissedEvent {
91
+ type: 'paywallDismissed';
92
+ triggerName: string;
93
+ paywallName: string;
94
+ isSecondTry: boolean;
95
+ }
96
+
97
+ export interface PurchaseSucceededEvent {
98
+ type: 'purchaseSucceeded';
99
+ productId: string;
100
+ triggerName: string;
101
+ paywallName: string;
102
+ isSecondTry: boolean;
103
+ }
104
+
105
+ export type HeliumPaywallEvent = {
106
+ type:
107
+ | 'paywallOpen'
108
+ | 'paywallClose'
109
+ | 'paywallDismissed'
110
+ | 'paywallOpenFailed'
111
+ | 'paywallSkipped'
112
+ | 'paywallButtonPressed'
113
+ | 'productSelected'
114
+ | 'purchasePressed'
115
+ | 'purchaseSucceeded'
116
+ | 'purchaseCancelled'
117
+ | 'purchaseFailed'
118
+ | 'purchaseRestored'
119
+ | 'purchaseRestoreFailed'
120
+ | 'purchasePending'
121
+ | 'initializeStart'
122
+ | 'paywallsDownloadSuccess'
123
+ | 'paywallsDownloadError'
124
+ | 'paywallWebViewRendered';
125
+ triggerName?: string;
126
+ paywallName?: string;
127
+ /**
128
+ * @deprecated Use `paywallName` instead.
129
+ */
130
+ paywallTemplateName?: string;
131
+ productId?: string;
132
+ /**
133
+ * @deprecated Use `productId` instead.
134
+ */
135
+ productKey?: string;
136
+ buttonName?: string;
137
+ /**
138
+ * @deprecated Use `buttonName` instead.
139
+ */
140
+ ctaName?: string;
141
+ paywallDownloadTimeTakenMS?: number;
142
+ templateDownloadTimeTakenMS?: number;
143
+ imagesDownloadTimeTakenMS?: number;
144
+ stylesDownloadTimeTakenMS?: number;
145
+ fontsDownloadTimeTakenMS?: number;
146
+ bundleDownloadTimeMS?: number;
147
+ dismissAll?: boolean;
148
+ isSecondTry?: boolean;
149
+ error?: string;
150
+ /**
151
+ * @deprecated Use `error` instead.
152
+ */
153
+ errorDescription?: string;
154
+ /**
155
+ * Unix timestamp in seconds
156
+ */
157
+ timestamp?: number;
158
+ };
159
+
160
+ export type PresentUpsellParams = {
161
+ triggerName: string;
162
+ /** Optional. This will be called when paywall fails to show due to an unsuccessful paywall download or if an invalid trigger is provided. */
163
+ onFallback?: () => void;
164
+ eventHandlers?: PaywallEventHandlers;
165
+ customPaywallTraits?: Record<string, any>;
166
+ };
167
+
31
168
  // --- Main Helium Configuration ---
32
169
  export interface HeliumConfig {
33
170
  /** Your Helium API Key */
34
171
  apiKey: string;
35
172
  /** Configuration for handling purchases. Can be custom functions or a pre-built handler config. */
36
- purchaseConfig: HeliumPurchaseConfig;
173
+ purchaseConfig?: HeliumPurchaseConfig;
37
174
  /** Callback for receiving all Helium paywall events. */
38
- onHeliumPaywallEvent: (event: any) => void; // Still mandatory
175
+ onHeliumPaywallEvent: (event: HeliumPaywallEvent) => void;
39
176
 
40
177
  // Optional configurations
41
- fallbackView?: number;
178
+ /** Fallback bundle in the rare situation that paywall is not ready to be shown. Highly recommended. See docs at https://docs.tryhelium.com/guides/fallback-bundle#react-native */
42
179
  fallbackBundle?: object;
43
- triggers?: string[];
180
+ /** Configure loading behavior for paywalls that are mid-download. */
181
+ paywallLoadingConfig?: HeliumPaywallLoadingConfig;
44
182
  customUserId?: string;
45
183
  customAPIEndpoint?: string;
46
184
  customUserTraits?: Record<string, any>;