@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.
- package/PaywallSdkReactNative.podspec +2 -2
- package/ios/HeliumSwiftInterface.swift +163 -76
- package/ios/RCTHeliumBridge.m +5 -0
- package/lib/commonjs/handlers/revenuecat.js +7 -3
- package/lib/commonjs/handlers/revenuecat.js.map +1 -1
- package/lib/commonjs/index.js +2 -14
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native-interface.js +133 -182
- package/lib/commonjs/native-interface.js.map +1 -1
- package/lib/commonjs/types.js +4 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/handlers/revenuecat.js +8 -3
- package/lib/module/handlers/revenuecat.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/native-interface.js +132 -178
- package/lib/module/native-interface.js.map +1 -1
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/commonjs/src/handlers/revenuecat.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +2 -2
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/native-interface.d.ts +3 -21
- package/lib/typescript/commonjs/src/native-interface.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/types.d.ts +107 -6
- package/lib/typescript/commonjs/src/types.d.ts.map +1 -1
- package/lib/typescript/module/src/handlers/revenuecat.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +2 -2
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/native-interface.d.ts +3 -21
- package/lib/typescript/module/src/native-interface.d.ts.map +1 -1
- package/lib/typescript/module/src/types.d.ts +107 -6
- package/lib/typescript/module/src/types.d.ts.map +1 -1
- package/package.json +1 -9
- package/src/handlers/revenuecat.ts +7 -3
- package/src/index.ts +10 -3
- package/src/native-interface.tsx +172 -191
- package/src/types.ts +147 -9
package/src/native-interface.tsx
CHANGED
|
@@ -1,190 +1,112 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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:
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
75
|
+
// Set up paywall event handlers listener
|
|
165
76
|
heliumEventEmitter.addListener(
|
|
166
|
-
'
|
|
167
|
-
|
|
168
|
-
|
|
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
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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:
|
|
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
|
-
|
|
157
|
+
let paywallEventHandlers: PaywallEventHandlers | undefined;
|
|
158
|
+
let presentOnFallback: (() => void) | undefined;
|
|
232
159
|
export const presentUpsell = ({
|
|
233
160
|
triggerName,
|
|
234
161
|
onFallback,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
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
|
-
|
|
303
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
173
|
+
purchaseConfig?: HeliumPurchaseConfig;
|
|
37
174
|
/** Callback for receiving all Helium paywall events. */
|
|
38
|
-
onHeliumPaywallEvent: (event:
|
|
175
|
+
onHeliumPaywallEvent: (event: HeliumPaywallEvent) => void;
|
|
39
176
|
|
|
40
177
|
// Optional configurations
|
|
41
|
-
|
|
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
|
-
|
|
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>;
|