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

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 +90 -76
  3. package/ios/RCTHeliumBridge.m +1 -0
  4. package/lib/commonjs/handlers/revenuecat.js +1 -1
  5. package/lib/commonjs/handlers/revenuecat.js.map +1 -1
  6. package/lib/commonjs/index.js +0 -18
  7. package/lib/commonjs/index.js.map +1 -1
  8. package/lib/commonjs/native-interface.js +81 -165
  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 +1 -1
  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 +81 -161
  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 +2 -21
  24. package/lib/typescript/commonjs/src/native-interface.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/src/types.d.ts +102 -3
  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 +2 -21
  31. package/lib/typescript/module/src/native-interface.d.ts.map +1 -1
  32. package/lib/typescript/module/src/types.d.ts +102 -3
  33. package/lib/typescript/module/src/types.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/src/handlers/revenuecat.ts +0 -1
  36. package/src/index.ts +9 -3
  37. package/src/native-interface.tsx +105 -172
  38. package/src/types.ts +142 -5
@@ -1,128 +1,42 @@
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
40
  const purchaseHandler = {
127
41
  makePurchase: config.purchaseConfig.makePurchase,
128
42
  restorePurchases: config.purchaseConfig.restorePurchases,
@@ -131,36 +45,36 @@ export const initialize = async (config: HeliumConfig) => {
131
45
  // Update download status to inProgress
132
46
  updateDownloadStatus('inProgress');
133
47
 
48
+ // Ensure these don't get added more than once
49
+ heliumEventEmitter.removeAllListeners('helium_paywall_event');
50
+ heliumEventEmitter.removeAllListeners('paywallEventHandlers');
51
+ heliumEventEmitter.removeAllListeners('helium_make_purchase');
52
+ heliumEventEmitter.removeAllListeners('helium_restore_purchases');
53
+
134
54
  // Set up event listeners
135
55
  heliumEventEmitter.addListener(
136
56
  'helium_paywall_event',
137
- (event: any) => {
57
+ (event: HeliumPaywallEvent) => {
138
58
  // Handle download status events
139
59
  if (event.type === 'paywallsDownloadSuccess') {
140
60
  updateDownloadStatus('success');
141
61
  } else if (event.type === 'paywallsDownloadError') {
142
62
  updateDownloadStatus('failed');
143
63
  }
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
- }
158
64
 
159
65
  // Forward all events to the callback provided in config
160
66
  config.onHeliumPaywallEvent(event);
161
67
  }
162
68
  );
163
69
 
70
+ // Set up paywall event handlers listener
71
+ heliumEventEmitter.addListener(
72
+ 'paywallEventHandlers',
73
+ (event: HeliumPaywallEvent) => {
74
+ callPaywallEventHandlers(event);
75
+ }
76
+ );
77
+
164
78
  // Set up purchase event listener using the determined handler
165
79
  heliumEventEmitter.addListener(
166
80
  'helium_make_purchase',
@@ -169,7 +83,7 @@ export const initialize = async (config: HeliumConfig) => {
169
83
  HeliumBridge.handlePurchaseResponse({
170
84
  transactionId: event.transactionId,
171
85
  status: result.status,
172
- error: result.error
86
+ error: result.error,
173
87
  });
174
88
  }
175
89
  );
@@ -181,7 +95,7 @@ export const initialize = async (config: HeliumConfig) => {
181
95
  const success = await purchaseHandler.restorePurchases();
182
96
  HeliumBridge.handleRestoreResponse({
183
97
  transactionId: event.transactionId,
184
- status: success ? 'restored' : 'failed'
98
+ status: success ? 'restored' : 'failed',
185
99
  });
186
100
  }
187
101
  );
@@ -212,30 +126,30 @@ export const initialize = async (config: HeliumConfig) => {
212
126
  HeliumBridge.initialize(
213
127
  {
214
128
  apiKey: config.apiKey,
215
- fallbackPaywall: viewTag,
216
- triggers: config.triggers || [],
217
129
  customUserId: config.customUserId || null,
218
130
  customAPIEndpoint: config.customAPIEndpoint || null,
219
- customUserTraits: config.customUserTraits == null ? {} : config.customUserTraits,
131
+ customUserTraits:
132
+ config.customUserTraits == null ? {} : config.customUserTraits,
220
133
  revenueCatAppUserId: config.revenueCatAppUserId,
221
134
  fallbackBundleUrlString: fallbackBundleUrlString,
222
135
  fallbackBundleString: fallbackBundleString,
136
+ paywallLoadingConfig: config.paywallLoadingConfig,
223
137
  },
224
138
  {}
225
139
  );
226
-
140
+
227
141
  // Mark as initialized after successful initialization
228
142
  isInitialized = true;
229
143
  };
230
144
 
231
- // Update the other methods to be synchronous
145
+ let paywallEventHandlers: PaywallEventHandlers | undefined;
146
+ let presentOnFallback: (() => void) | undefined;
232
147
  export const presentUpsell = ({
233
148
  triggerName,
234
149
  onFallback,
235
- }: {
236
- triggerName: string;
237
- onFallback?: () => void;
238
- }) => {
150
+ eventHandlers,
151
+ customPaywallTraits,
152
+ }: PresentUpsellParams) => {
239
153
  HeliumBridge.canPresentUpsell(
240
154
  triggerName,
241
155
  (canPresent: boolean, reason: string) => {
@@ -249,9 +163,13 @@ export const presentUpsell = ({
249
163
  }
250
164
 
251
165
  try {
252
- HeliumBridge.presentUpsell(triggerName);
166
+ paywallEventHandlers = eventHandlers;
167
+ presentOnFallback = onFallback;
168
+ HeliumBridge.presentUpsell(triggerName, customPaywallTraits || null);
253
169
  } catch (error) {
254
170
  console.log('[Helium] Present error', error);
171
+ paywallEventHandlers = undefined;
172
+ presentOnFallback = undefined;
255
173
  onFallback?.();
256
174
  HeliumBridge.fallbackOpenOrCloseEvent(triggerName, true, 'presented');
257
175
  }
@@ -259,6 +177,60 @@ export const presentUpsell = ({
259
177
  );
260
178
  };
261
179
 
180
+ function callPaywallEventHandlers(event: HeliumPaywallEvent) {
181
+ if (paywallEventHandlers) {
182
+ switch (event.type) {
183
+ case 'paywallOpen':
184
+ paywallEventHandlers?.onOpen?.({
185
+ type: 'paywallOpen',
186
+ triggerName: event.triggerName ?? 'unknown',
187
+ paywallName: event.paywallName ?? 'unknown',
188
+ isSecondTry: event.isSecondTry ?? false,
189
+ viewType: 'presented',
190
+ });
191
+ break;
192
+ case 'paywallClose':
193
+ paywallEventHandlers?.onClose?.({
194
+ type: 'paywallClose',
195
+ triggerName: event.triggerName ?? 'unknown',
196
+ paywallName: event.paywallName ?? 'unknown',
197
+ isSecondTry: event.isSecondTry ?? false,
198
+ });
199
+ if (!event.isSecondTry) {
200
+ paywallEventHandlers = undefined;
201
+ }
202
+ presentOnFallback = undefined;
203
+ break;
204
+ case 'paywallDismissed':
205
+ paywallEventHandlers?.onDismissed?.({
206
+ type: 'paywallDismissed',
207
+ triggerName: event.triggerName ?? 'unknown',
208
+ paywallName: event.paywallName ?? 'unknown',
209
+ isSecondTry: event.isSecondTry ?? false,
210
+ });
211
+ break;
212
+ case 'purchaseSucceeded':
213
+ paywallEventHandlers?.onPurchaseSucceeded?.({
214
+ type: 'purchaseSucceeded',
215
+ productId: event.productId ?? 'unknown',
216
+ triggerName: event.triggerName ?? 'unknown',
217
+ paywallName: event.paywallName ?? 'unknown',
218
+ isSecondTry: event.isSecondTry ?? false,
219
+ });
220
+ break;
221
+ case 'paywallSkipped':
222
+ paywallEventHandlers = undefined;
223
+ presentOnFallback = undefined;
224
+ break;
225
+ case 'paywallOpenFailed':
226
+ paywallEventHandlers = undefined;
227
+ presentOnFallback?.();
228
+ presentOnFallback = undefined;
229
+ break;
230
+ }
231
+ }
232
+ }
233
+
262
234
  export const hideUpsell = () => {
263
235
  HeliumBridge.hideUpsell();
264
236
  };
@@ -295,52 +267,13 @@ export const handleDeepLink = async (url: string | null): Promise<boolean> => {
295
267
  console.log('[Helium] Handled deep link:', handled);
296
268
  resolve(handled);
297
269
  });
270
+ } else {
271
+ resolve(false);
298
272
  }
299
273
  });
300
274
  };
301
275
 
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} />;
341
- };
342
-
343
276
  export const HELIUM_CTA_NAMES = {
344
277
  SCHEDULE_CALL: 'schedule_call',
345
278
  SUBSCRIBE_BUTTON: 'subscribe_button',
346
- }
279
+ };
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
 
@@ -28,6 +37,133 @@ export function createCustomPurchaseConfig(callbacks: {
28
37
  };
29
38
  }
30
39
 
40
+ export type TriggerLoadingConfig = {
41
+ /** Whether to show loading state for this trigger. Set to nil to use the global `useLoadingState` setting. */
42
+ useLoadingState?: boolean;
43
+ /** Maximum seconds to show loading for this trigger. Set to nil to use the global `loadingBudget` setting. */
44
+ loadingBudget?: number;
45
+ };
46
+
47
+ export type HeliumPaywallLoadingConfig = {
48
+ /**
49
+ * Whether to show a loading state while fetching paywall configuration.
50
+ * When true, shows a loading view for up to `loadingBudget` seconds before falling back.
51
+ * Default: true
52
+ */
53
+ useLoadingState?: boolean;
54
+ /**
55
+ * Maximum time (in seconds) to show the loading state before displaying fallback.
56
+ * After this timeout, the fallback view will be shown even if the paywall is still downloading.
57
+ * Default: 2.0 seconds
58
+ */
59
+ loadingBudget?: number;
60
+ /**
61
+ * Optional per-trigger loading configuration overrides.
62
+ * Use this to customize loading behavior for specific triggers.
63
+ * Keys are trigger names, values are TriggerLoadingConfig instances.
64
+ * Example: Disable loading for "onboarding" trigger while keeping it for others.
65
+ */
66
+ perTriggerLoadingConfig?: Record<string, TriggerLoadingConfig>;
67
+ };
68
+
69
+ // Event handler types for per-presentation event handling
70
+ export interface PaywallEventHandlers {
71
+ onOpen?: (event: PaywallOpenEvent) => void;
72
+ onClose?: (event: PaywallCloseEvent) => void;
73
+ onDismissed?: (event: PaywallDismissedEvent) => void;
74
+ onPurchaseSucceeded?: (event: PurchaseSucceededEvent) => void;
75
+ }
76
+
77
+ // Typed event interfaces
78
+ export interface PaywallOpenEvent {
79
+ type: 'paywallOpen';
80
+ triggerName: string;
81
+ paywallName: string;
82
+ isSecondTry: boolean;
83
+ viewType?: 'presented' | 'embedded' | 'triggered';
84
+ }
85
+
86
+ export interface PaywallCloseEvent {
87
+ type: 'paywallClose';
88
+ triggerName: string;
89
+ paywallName: string;
90
+ isSecondTry: boolean;
91
+ }
92
+
93
+ export interface PaywallDismissedEvent {
94
+ type: 'paywallDismissed';
95
+ triggerName: string;
96
+ paywallName: string;
97
+ isSecondTry: boolean;
98
+ }
99
+
100
+ export interface PurchaseSucceededEvent {
101
+ type: 'purchaseSucceeded';
102
+ productId: string;
103
+ triggerName: string;
104
+ paywallName: string;
105
+ isSecondTry: boolean;
106
+ }
107
+
108
+ export type HeliumPaywallEvent = {
109
+ type:
110
+ | 'paywallOpen'
111
+ | 'paywallClose'
112
+ | 'paywallDismissed'
113
+ | 'paywallOpenFailed'
114
+ | 'paywallSkipped'
115
+ | 'paywallButtonPressed'
116
+ | 'productSelected'
117
+ | 'purchasePressed'
118
+ | 'purchaseSucceeded'
119
+ | 'purchaseCancelled'
120
+ | 'purchaseFailed'
121
+ | 'purchaseRestored'
122
+ | 'purchaseRestoreFailed'
123
+ | 'purchasePending'
124
+ | 'initializeStart'
125
+ | 'paywallsDownloadSuccess'
126
+ | 'paywallsDownloadError'
127
+ | 'paywallWebViewRendered';
128
+ triggerName?: string;
129
+ paywallName?: string;
130
+ /**
131
+ * @deprecated Use `paywallName` instead.
132
+ */
133
+ paywallTemplateName?: string;
134
+ productId?: string;
135
+ /**
136
+ * @deprecated Use `productId` instead.
137
+ */
138
+ productKey?: string;
139
+ ctaName?: string;
140
+ paywallDownloadTimeTakenMS?: number;
141
+ templateDownloadTimeTakenMS?: number;
142
+ imagesDownloadTimeTakenMS?: number;
143
+ stylesDownloadTimeTakenMS?: number;
144
+ fontsDownloadTimeTakenMS?: number;
145
+ bundleDownloadTimeMS?: number;
146
+ dismissAll?: boolean;
147
+ isSecondTry?: boolean;
148
+ error?: string;
149
+ /**
150
+ * @deprecated Use `error` instead.
151
+ */
152
+ errorDescription?: string;
153
+ /**
154
+ * Unix timestamp in seconds
155
+ */
156
+ timestamp?: number;
157
+ };
158
+
159
+ export type PresentUpsellParams = {
160
+ triggerName: string;
161
+ /** Optional. This will be called when paywall fails to show due to an unsuccessful paywall download or if an invalid trigger is provided. */
162
+ onFallback?: () => void;
163
+ eventHandlers?: PaywallEventHandlers;
164
+ customPaywallTraits?: Record<string, any>;
165
+ };
166
+
31
167
  // --- Main Helium Configuration ---
32
168
  export interface HeliumConfig {
33
169
  /** Your Helium API Key */
@@ -35,12 +171,13 @@ export interface HeliumConfig {
35
171
  /** Configuration for handling purchases. Can be custom functions or a pre-built handler config. */
36
172
  purchaseConfig: HeliumPurchaseConfig;
37
173
  /** Callback for receiving all Helium paywall events. */
38
- onHeliumPaywallEvent: (event: any) => void; // Still mandatory
174
+ onHeliumPaywallEvent: (event: HeliumPaywallEvent) => void;
39
175
 
40
176
  // Optional configurations
41
- fallbackView?: number;
177
+ /** 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
178
  fallbackBundle?: object;
43
- triggers?: string[];
179
+ /** Configure loading behavior for paywalls that are mid-download. */
180
+ paywallLoadingConfig?: HeliumPaywallLoadingConfig;
44
181
  customUserId?: string;
45
182
  customAPIEndpoint?: string;
46
183
  customUserTraits?: Record<string, any>;