@onairos/react-native 3.0.71 → 3.0.73

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 (65) hide show
  1. package/lib/commonjs/components/Onairos.js +294 -155
  2. package/lib/commonjs/components/Onairos.js.map +1 -1
  3. package/lib/commonjs/components/OnairosButton.js +1 -1
  4. package/lib/commonjs/components/OnairosButton.js.map +1 -1
  5. package/lib/commonjs/components/UniversalOnboarding.js +6 -6
  6. package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
  7. package/lib/commonjs/components/onboarding/OAuthWebView.js +188 -52
  8. package/lib/commonjs/components/onboarding/OAuthWebView.js.map +1 -1
  9. package/lib/commonjs/index.js +25 -440
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/services/apiKeyService.js +404 -0
  12. package/lib/commonjs/services/apiKeyService.js.map +1 -0
  13. package/lib/commonjs/services/platformAuthService.js +318 -113
  14. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  15. package/lib/commonjs/types/index.js +4 -0
  16. package/lib/commonjs/types.js +12 -0
  17. package/lib/commonjs/types.js.map +1 -1
  18. package/lib/commonjs/utils/programmaticFlow.js +117 -0
  19. package/lib/commonjs/utils/programmaticFlow.js.map +1 -0
  20. package/lib/module/components/Onairos.js +297 -158
  21. package/lib/module/components/Onairos.js.map +1 -1
  22. package/lib/module/components/OnairosButton.js +1 -1
  23. package/lib/module/components/OnairosButton.js.map +1 -1
  24. package/lib/module/components/UniversalOnboarding.js +6 -6
  25. package/lib/module/components/UniversalOnboarding.js.map +1 -1
  26. package/lib/module/components/onboarding/OAuthWebView.js +188 -52
  27. package/lib/module/components/onboarding/OAuthWebView.js.map +1 -1
  28. package/lib/module/index.js +17 -61
  29. package/lib/module/index.js.map +1 -1
  30. package/lib/module/services/apiKeyService.js +389 -0
  31. package/lib/module/services/apiKeyService.js.map +1 -0
  32. package/lib/module/services/platformAuthService.js +311 -111
  33. package/lib/module/services/platformAuthService.js.map +1 -1
  34. package/lib/module/types/index.js +1 -1
  35. package/lib/module/types.js +8 -0
  36. package/lib/module/types.js.map +1 -1
  37. package/lib/module/utils/programmaticFlow.js +111 -0
  38. package/lib/module/utils/programmaticFlow.js.map +1 -0
  39. package/lib/typescript/components/Onairos.d.ts +2 -29
  40. package/lib/typescript/components/Onairos.d.ts.map +1 -1
  41. package/lib/typescript/components/onboarding/OAuthWebView.d.ts.map +1 -1
  42. package/lib/typescript/index.d.ts +10 -39
  43. package/lib/typescript/index.d.ts.map +1 -1
  44. package/lib/typescript/services/apiKeyService.d.ts +66 -0
  45. package/lib/typescript/services/apiKeyService.d.ts.map +1 -0
  46. package/lib/typescript/services/platformAuthService.d.ts +26 -0
  47. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  48. package/lib/typescript/types/index.d.ts +144 -78
  49. package/lib/typescript/types/index.d.ts.map +1 -1
  50. package/lib/typescript/types.d.ts +92 -3
  51. package/lib/typescript/types.d.ts.map +1 -1
  52. package/lib/typescript/utils/programmaticFlow.d.ts +23 -0
  53. package/lib/typescript/utils/programmaticFlow.d.ts.map +1 -0
  54. package/package.json +1 -1
  55. package/src/components/Onairos.tsx +330 -207
  56. package/src/components/OnairosButton.tsx +1 -1
  57. package/src/components/UniversalOnboarding.tsx +6 -6
  58. package/src/components/onboarding/OAuthWebView.tsx +236 -71
  59. package/src/index.ts +25 -115
  60. package/src/services/apiKeyService.ts +401 -0
  61. package/src/services/platformAuthService.ts +363 -126
  62. package/src/types/index.d.ts +110 -0
  63. package/src/types/index.ts +148 -74
  64. package/src/types.ts +99 -3
  65. package/src/utils/programmaticFlow.ts +113 -0
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useImperativeHandle, useState, useCallback } from 'react';
1
+ import React, { forwardRef, useImperativeHandle, useState, useCallback, useEffect } from 'react';
2
2
  import {
3
3
  StyleSheet,
4
4
  View,
@@ -8,6 +8,7 @@ import {
8
8
  TextStyle,
9
9
  Platform,
10
10
  Image,
11
+ Alert,
11
12
  } from 'react-native';
12
13
  import { UniversalOnboarding } from './UniversalOnboarding';
13
14
  import { Overlay } from './Overlay';
@@ -15,255 +16,349 @@ import { COLORS } from '../constants';
15
16
  import { hasCredentials, getCredentials, deleteCredentials } from '../utils/secureStorage';
16
17
  import { onairosApi } from '../api';
17
18
  import { Portal } from '../utils/Portal';
18
- import { DataTier } from '../types';
19
+ import { DataTier, OnairosConfig, OnairosProps } from '../types';
20
+ import { initializeApiKey, isApiKeyInitialized, getApiConfig } from '../services/apiKeyService';
19
21
 
20
- interface OnairosProps {
21
- returnLink?: string;
22
- prefillUrl?: string;
23
- AppName: string;
24
- buttonType?: 'normal' | 'pill';
25
- requestData?: {
26
- [key: string]: DataTier;
27
- };
28
- buttonWidth?: number;
29
- buttonHeight?: number;
30
- hasStroke?: boolean;
31
- enabled?: boolean;
32
- buttonForm?: 'default' | 'connect';
33
- onRejection?: (error?: string) => void;
34
- onResolved?: (apiUrl: string, token: string, userData: any) => void;
35
- preCheck?: () => Promise<boolean>;
36
- color?: string;
37
- debug?: boolean;
38
- darkMode?: boolean;
39
- preferredPlatform?: string;
40
- testMode?: boolean;
41
- }
42
-
43
- export interface OnairosRef {
44
- trigger: () => Promise<void>;
45
- reset: () => Promise<void>;
46
- }
22
+ export const Onairos = forwardRef<any, OnairosProps>((props, ref) => {
23
+ const {
24
+ returnLink,
25
+ prefillUrl,
26
+ AppName,
27
+ buttonType = 'normal',
28
+ requestData,
29
+ buttonWidth = 200,
30
+ buttonHeight = 48,
31
+ hasStroke = false,
32
+ enabled = true,
33
+ buttonForm = 'default',
34
+ onRejection,
35
+ onResolved,
36
+ preCheck,
37
+ color = COLORS.primary,
38
+ debug = false,
39
+ darkMode = false,
40
+ preferredPlatform,
41
+ testMode = false,
42
+ // API Key props
43
+ apiKey,
44
+ environment = 'production',
45
+ enableLogging = true,
46
+ timeout = 30000,
47
+ retryAttempts = 3,
48
+ } = props;
47
49
 
48
- export const Onairos = forwardRef<OnairosRef, OnairosProps>(({
49
- returnLink,
50
- prefillUrl,
51
- AppName,
52
- buttonType = 'normal',
53
- requestData,
54
- buttonWidth = 180,
55
- buttonHeight,
56
- hasStroke = false,
57
- enabled = true,
58
- buttonForm = 'default',
59
- onRejection,
60
- onResolved,
61
- preCheck,
62
- color,
63
- debug = false,
64
- darkMode = false,
65
- preferredPlatform,
66
- testMode = false,
67
- }, ref) => {
68
- const [showOnboarding, setShowOnboarding] = useState(false);
69
50
  const [showOverlay, setShowOverlay] = useState(false);
70
- const [storedCredentials, setStoredCredentials] = useState<any>(null);
71
- const [isLoading, setIsLoading] = useState(false);
72
- const [isPressed, setIsPressed] = useState(false);
51
+ const [isInitialized, setIsInitialized] = useState(false);
52
+ const [initializationError, setInitializationError] = useState<string | null>(null);
53
+ const [isInitializing, setIsInitializing] = useState(false);
73
54
 
74
- // Expose methods for external control
75
- useImperativeHandle(ref, () => ({
76
- trigger: async () => {
77
- await handlePress();
78
- },
79
- reset: async () => {
80
- await deleteCredentials();
81
- }
82
- }));
55
+ // Initialize API key when component mounts or API key changes
56
+ useEffect(() => {
57
+ let isMounted = true;
83
58
 
84
- // Compute button text based on buttonForm
85
- const getButtonText = () => {
86
- if (buttonForm === 'connect') {
87
- return 'Connect with Onairos';
88
- } else {
89
- return 'Sign in with Onairos';
90
- }
91
- };
59
+ const initializeApi = async () => {
60
+ // Skip if already initializing
61
+ if (isInitializing) return;
62
+
63
+ setIsInitializing(true);
64
+ setInitializationError(null);
92
65
 
93
- // Calculate background color based on props and state
94
- const getBackgroundColor = (): string => {
95
- if (!enabled) {
96
- return darkMode ? '#3A3A3A' : '#e0e0e0';
97
- }
98
-
99
- if (isPressed) {
100
- return color ?
101
- (typeof color === 'string' ? `${color}80` : color) :
102
- (darkMode ? '#32323280' : '#f5f5f580');
103
- }
104
-
105
- return color || (darkMode ? '#2A2A2A' : '#ffffff');
106
- };
66
+ try {
67
+ console.log('🚀 Onairos SDK: Starting API key initialization...');
68
+
69
+ if (!apiKey) {
70
+ throw new Error('API key is required. Please provide a valid API key from your Onairos dashboard.');
71
+ }
107
72
 
108
- // Calculate text color based on background luminance
109
- const getTextColor = (): string => {
110
- if (!enabled) {
111
- return darkMode ? '#777777' : '#AAAAAA';
112
- }
113
-
114
- if (darkMode) {
115
- return '#FFFFFF';
73
+ const config: OnairosConfig = {
74
+ apiKey,
75
+ environment,
76
+ enableLogging,
77
+ timeout,
78
+ retryAttempts,
79
+ };
80
+
81
+ // Initialize the API key service
82
+ await initializeApiKey(config);
83
+
84
+ if (isMounted) {
85
+ setIsInitialized(true);
86
+ setInitializationError(null);
87
+
88
+ if (enableLogging) {
89
+ console.log('✅ Onairos SDK: API key initialization completed successfully');
90
+ console.log('📊 Configuration:', {
91
+ environment,
92
+ enableLogging,
93
+ timeout,
94
+ retryAttempts,
95
+ apiKeyLength: apiKey.length,
96
+ });
97
+ }
98
+ }
99
+ } catch (error) {
100
+ const errorMessage = error instanceof Error ? error.message : 'Unknown initialization error';
101
+
102
+ console.error('❌ Onairos SDK: API key initialization failed:', errorMessage);
103
+
104
+ if (isMounted) {
105
+ setIsInitialized(false);
106
+ setInitializationError(errorMessage);
107
+
108
+ // Show developer-friendly error
109
+ if (enableLogging) {
110
+ console.group('🔧 API Key Troubleshooting Guide');
111
+ console.log('1. Check that your API key is correct and not expired');
112
+ console.log('2. Verify you have the right permissions for your use case');
113
+ console.log('3. Ensure your environment setting matches your API key');
114
+ console.log('4. Check your internet connection');
115
+ console.log('5. Visit https://dashboard.onairos.uk to manage your API keys');
116
+ console.groupEnd();
117
+ }
118
+
119
+ // Call rejection callback if provided
120
+ if (onRejection) {
121
+ onRejection(`SDK initialization failed: ${errorMessage}`);
122
+ }
123
+ }
124
+ } finally {
125
+ if (isMounted) {
126
+ setIsInitializing(false);
127
+ }
128
+ }
129
+ };
130
+
131
+ // Only initialize if we have an API key and aren't already initialized correctly
132
+ if (apiKey && (!isApiKeyInitialized() || getApiConfig()?.apiKey !== apiKey)) {
133
+ initializeApi();
116
134
  }
117
-
118
- const bgColor = getBackgroundColor();
119
- // Simple luminance check - in a real app, this would use a proper algorithm
120
- return bgColor === '#ffffff' || bgColor === '#f5f5f580' || bgColor.includes('#f') ? '#000000' : '#FFFFFF';
121
- };
122
135
 
123
- const handlePress = async () => {
124
- if (!enabled || isLoading) return;
125
-
126
- setIsLoading(true);
127
-
136
+ return () => {
137
+ isMounted = false;
138
+ };
139
+ }, [apiKey, environment, enableLogging, timeout, retryAttempts, onRejection]);
140
+
141
+ // Imperative methods
142
+ useImperativeHandle(ref, () => ({
143
+ openOverlay: handleShowOverlay,
144
+ closeOverlay: handleCloseOverlay,
145
+ isInitialized: () => isInitialized,
146
+ getApiConfig: () => getApiConfig(),
147
+ }));
148
+
149
+ const handleShowOverlay = useCallback(async () => {
128
150
  try {
151
+ // Check if SDK is initialized
152
+ if (!isInitialized) {
153
+ const errorMessage = initializationError || 'SDK not initialized. Please check your API key.';
154
+ console.error('❌ Cannot open overlay: SDK not initialized');
155
+
156
+ if (onRejection) {
157
+ onRejection(errorMessage);
158
+ } else {
159
+ Alert.alert(
160
+ 'SDK Error',
161
+ errorMessage,
162
+ [{ text: 'OK' }]
163
+ );
164
+ }
165
+ return;
166
+ }
167
+
168
+ // Run pre-check if provided
129
169
  if (preCheck) {
130
- const shouldProceed = await preCheck();
131
- if (!shouldProceed) {
132
- onRejection?.('Precheck validation failed');
133
- setIsLoading(false);
170
+ if (enableLogging) {
171
+ console.log('🔍 Running pre-check validation...');
172
+ }
173
+
174
+ try {
175
+ const preCheckPassed = await preCheck();
176
+ if (!preCheckPassed) {
177
+ if (enableLogging) {
178
+ console.log('⛔ Pre-check failed, aborting overlay');
179
+ }
180
+ return;
181
+ }
182
+
183
+ if (enableLogging) {
184
+ console.log('✅ Pre-check passed');
185
+ }
186
+ } catch (preCheckError) {
187
+ console.error('❌ Pre-check error:', preCheckError);
188
+ if (onRejection) {
189
+ onRejection(`Pre-check failed: ${preCheckError.message}`);
190
+ }
134
191
  return;
135
192
  }
136
193
  }
137
194
 
138
- // Check if credentials exist
139
- const hasStoredCreds = await hasCredentials();
195
+ // Check for existing credentials
196
+ if (enableLogging) {
197
+ console.log('🔍 Checking for existing credentials...');
198
+ }
140
199
 
141
- if (hasStoredCreds) {
142
- // If credentials exist, fetch them and verify
143
- const credentials = await getCredentials();
144
-
145
- if (!credentials || !credentials.username) {
146
- // Invalid credentials, clear and start fresh
147
- await deleteCredentials();
148
- setShowOnboarding(true);
149
- setIsLoading(false);
150
- return;
200
+ const hasExistingCredentials = await hasCredentials();
201
+
202
+ if (hasExistingCredentials) {
203
+ if (enableLogging) {
204
+ console.log('🔑 Existing credentials found, attempting automatic resolution...');
151
205
  }
152
206
 
153
207
  try {
154
- // Validate credentials with server
155
- const isValid = await onairosApi.validateCredentials(credentials.username);
208
+ const credentials = await getCredentials();
156
209
 
157
- if (!isValid) {
158
- // Clear invalid credentials
159
- await deleteCredentials();
160
- setShowOnboarding(true);
161
- setIsLoading(false);
210
+ if (credentials && onResolved) {
211
+ // For existing users, we can resolve immediately with cached data
212
+ const mockApiUrl = 'https://api2.onairos.uk/user/data';
213
+ const mockToken = 'existing_user_token';
214
+
215
+ if (enableLogging) {
216
+ console.log('✅ Resolving with existing credentials');
217
+ }
218
+
219
+ onResolved(mockApiUrl, mockToken, credentials);
162
220
  return;
163
221
  }
164
-
165
- // Store and display overlay with valid credentials
166
- setStoredCredentials(credentials);
167
- setShowOverlay(true);
168
- } catch (validationError) {
169
- console.warn('Validation error, proceeding to onboarding:', validationError);
170
- setShowOnboarding(true);
222
+ } catch (credentialsError) {
223
+ console.warn('⚠️ Failed to retrieve existing credentials:', credentialsError);
224
+ // Continue with normal flow
171
225
  }
172
- } else {
173
- // If no credentials, show onboarding
174
- setShowOnboarding(true);
175
226
  }
227
+
228
+ if (enableLogging) {
229
+ console.log('🎨 Opening Onairos overlay...');
230
+ }
231
+
232
+ setShowOverlay(true);
176
233
  } catch (error) {
177
- console.error('Error during button press flow:', error);
178
- // Fall back to onboarding on error
179
- setShowOnboarding(true);
180
- onRejection?.(error instanceof Error ? error.message : 'Unknown error');
181
- } finally {
182
- setIsLoading(false);
234
+ console.error('Error opening overlay:', error);
235
+ if (onRejection) {
236
+ onRejection(`Failed to open overlay: ${error.message}`);
237
+ }
183
238
  }
184
- };
239
+ }, [isInitialized, initializationError, preCheck, enableLogging, onRejection, onResolved]);
185
240
 
186
- const handleOnboardingComplete = useCallback((apiUrl: string, token: string, data: any) => {
187
- setShowOnboarding(false);
188
- if (onResolved) {
189
- onResolved(apiUrl, token, data);
241
+ const handleCloseOverlay = useCallback((result?: any) => {
242
+ if (enableLogging) {
243
+ console.log('🔽 Closing Onairos overlay');
244
+ }
245
+
246
+ setShowOverlay(false);
247
+
248
+ if (result && onResolved) {
249
+ onResolved(result.apiUrl, result.token, result.userData);
190
250
  }
191
- }, [onResolved]);
251
+ }, [enableLogging, onResolved]);
192
252
 
193
- const handleOverlayResolved = useCallback((apiUrl: string, token: string, data: any) => {
253
+ const handleOverlayRejection = useCallback((error?: string) => {
254
+ if (enableLogging) {
255
+ console.log('❌ Overlay rejected:', error);
256
+ }
257
+
194
258
  setShowOverlay(false);
195
- if (onResolved) {
196
- onResolved(apiUrl, token, data);
259
+
260
+ if (onRejection) {
261
+ onRejection(error);
197
262
  }
198
- }, [onResolved]);
263
+ }, [enableLogging, onRejection]);
264
+
265
+ // Show error state if initialization failed
266
+ if (initializationError && !isInitializing) {
267
+ return (
268
+ <View style={styles.errorContainer}>
269
+ <TouchableOpacity
270
+ style={[styles.errorButton, { backgroundColor: '#FF6B6B' }]}
271
+ onPress={() => {
272
+ Alert.alert(
273
+ 'SDK Error',
274
+ `Onairos SDK initialization failed:\n\n${initializationError}\n\nPlease check your API key and try again.`,
275
+ [{ text: 'OK' }]
276
+ );
277
+ }}
278
+ >
279
+ <Text style={styles.errorButtonText}>⚠️ SDK Error</Text>
280
+ </TouchableOpacity>
281
+ </View>
282
+ );
283
+ }
284
+
285
+ // Show loading state while initializing
286
+ if (isInitializing || !isInitialized) {
287
+ return (
288
+ <View style={styles.loadingContainer}>
289
+ <TouchableOpacity
290
+ style={[styles.loadingButton, { backgroundColor: '#999' }]}
291
+ disabled={true}
292
+ >
293
+ <Text style={styles.loadingButtonText}>🔄 Initializing...</Text>
294
+ </TouchableOpacity>
295
+ </View>
296
+ );
297
+ }
199
298
 
200
- // Apply custom styling based on props
201
- const buttonStyle: ViewStyle[] = [
202
- styles.button,
203
- {
299
+ // Get button style based on type
300
+ const getButtonStyle = (): ViewStyle => {
301
+ const baseStyle: ViewStyle = {
204
302
  width: buttonWidth,
205
- height: buttonHeight || 48,
206
- backgroundColor: getBackgroundColor(),
207
- borderWidth: hasStroke ? 1 : 0,
208
- borderColor: darkMode ? '#555555' : '#000000',
209
- borderRadius: buttonType === 'pill' ? 24 : 8,
210
- },
211
- ];
303
+ height: buttonHeight,
304
+ backgroundColor: color,
305
+ borderRadius: buttonType === 'pill' ? buttonHeight / 2 : 8,
306
+ alignItems: 'center',
307
+ justifyContent: 'center',
308
+ flexDirection: 'row',
309
+ opacity: enabled ? 1 : 0.6,
310
+ };
311
+
312
+ if (hasStroke) {
313
+ baseStyle.borderWidth = 2;
314
+ baseStyle.borderColor = darkMode ? '#fff' : '#000';
315
+ }
316
+
317
+ return baseStyle;
318
+ };
212
319
 
213
- const textStyle: TextStyle = {
214
- ...styles.buttonText,
215
- color: getTextColor(),
216
- opacity: enabled ? 1 : 0.7,
320
+ const getButtonText = (): string => {
321
+ switch (buttonForm) {
322
+ case 'connect':
323
+ return 'Connect with Onairos';
324
+ default:
325
+ return 'Continue with Onairos';
326
+ }
217
327
  };
218
328
 
219
329
  return (
220
330
  <>
221
331
  <TouchableOpacity
222
- style={buttonStyle}
223
- onPress={handlePress}
224
- disabled={!enabled || isLoading}
225
- accessibilityLabel="Sign in with Onairos"
226
- onPressIn={() => setIsPressed(true)}
227
- onPressOut={() => setIsPressed(false)}
332
+ style={getButtonStyle()}
333
+ onPress={handleShowOverlay}
334
+ disabled={!enabled || isInitializing || !isInitialized}
335
+ activeOpacity={0.8}
228
336
  >
229
- <View style={styles.buttonContent}>
230
- <Image
231
- source={require('../assets/images/onairos_logo.png')}
232
- style={styles.logo}
233
- resizeMode="contain"
234
- />
235
- <Text style={textStyle}>{getButtonText()}</Text>
236
- </View>
337
+ <Image
338
+ source={require('../assets/images/onairos_logo.png')}
339
+ style={styles.logo}
340
+ resizeMode="contain"
341
+ />
342
+ <Text style={[styles.buttonText, { color: darkMode ? '#000' : '#fff' }]}>
343
+ {getButtonText()}
344
+ </Text>
237
345
  </TouchableOpacity>
238
346
 
239
- {/* Overlay and Onboarding components rendered outside the button */}
240
- {showOnboarding && (
241
- <UniversalOnboarding
242
- visible={showOnboarding}
243
- onClose={() => {
244
- setShowOnboarding(false);
245
- onRejection?.('User closed onboarding');
246
- }}
247
- AppName={AppName}
248
- requestData={requestData as any}
249
- returnLink={returnLink || ''}
250
- onComplete={handleOnboardingComplete}
251
- debug={debug}
252
- test={testMode}
253
- preferredPlatform={preferredPlatform}
254
- />
255
- )}
256
-
257
- {/* Overlay rendered through Portal to ensure it appears at root level */}
258
- {showOverlay && storedCredentials && (
347
+ {showOverlay && (
259
348
  <Portal>
260
- <Overlay
261
- data={requestData || {}}
262
- username={storedCredentials.username}
263
- modelKey={storedCredentials.userPin || ''}
264
- onResolved={handleOverlayResolved}
265
- appName={AppName}
266
- darkMode={darkMode}
349
+ <UniversalOnboarding
350
+ visible={showOverlay}
351
+ onClose={handleCloseOverlay}
352
+ AppName={AppName}
353
+ requestData={requestData || {}}
354
+ returnLink={returnLink}
355
+ prefillUrl={prefillUrl}
356
+ onComplete={handleCloseOverlay}
357
+ embedd={false}
358
+ debug={debug}
359
+ testMode={testMode}
360
+ preferredPlatform={preferredPlatform}
361
+ config={getApiConfig() || undefined}
267
362
  />
268
363
  </Portal>
269
364
  )}
@@ -299,4 +394,32 @@ const styles = StyleSheet.create({
299
394
  fontWeight: '600',
300
395
  textAlign: 'center',
301
396
  },
397
+ errorContainer: {
398
+ flex: 1,
399
+ justifyContent: 'center',
400
+ alignItems: 'center',
401
+ },
402
+ errorButton: {
403
+ padding: 16,
404
+ borderRadius: 8,
405
+ },
406
+ errorButtonText: {
407
+ fontSize: 16,
408
+ fontWeight: '600',
409
+ color: '#fff',
410
+ },
411
+ loadingContainer: {
412
+ flex: 1,
413
+ justifyContent: 'center',
414
+ alignItems: 'center',
415
+ },
416
+ loadingButton: {
417
+ padding: 16,
418
+ borderRadius: 8,
419
+ },
420
+ loadingButtonText: {
421
+ fontSize: 16,
422
+ fontWeight: '600',
423
+ color: '#fff',
424
+ },
302
425
  });
@@ -288,7 +288,7 @@ export const OnairosButton = forwardRef<OnairosButtonRef, OnairosButtonProps>(({
288
288
  returnLink={returnLink || ''}
289
289
  onComplete={handleOnboardingComplete}
290
290
  debug={debug}
291
- test={testMode}
291
+ testMode={testMode}
292
292
  preferredPlatform={preferredPlatform}
293
293
  />
294
294
  )}
@@ -56,7 +56,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
56
56
  onComplete,
57
57
  embedd = false,
58
58
  debug = false,
59
- test = false,
59
+ testMode = false,
60
60
  preferredPlatform,
61
61
  inferenceData,
62
62
  auto = false,
@@ -93,8 +93,8 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
93
93
 
94
94
 
95
95
  // Parse test mode options
96
- const testModeOptions = typeof test === 'object' ? test : {};
97
- const isTestMode = test === true || (typeof test === 'object' && test !== null);
96
+ const testModeOptions = typeof testMode === 'object' ? testMode : {} as any;
97
+ const isTestMode = testMode === true || (typeof testMode === 'object' && testMode !== null);
98
98
  const showTestControls = (debug || isTestMode) && requestData;
99
99
 
100
100
  // Simple 2-flow system
@@ -169,7 +169,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
169
169
  });
170
170
 
171
171
  // Pre-populate with mock connections in debug mode
172
- if (test || Platform.OS === 'web') {
172
+ if (testMode || Platform.OS === 'web') {
173
173
  setConnections({
174
174
  instagram: { userName: 'instagram_user', connected: true },
175
175
  youtube: { userName: 'youtube_user', connected: true },
@@ -573,7 +573,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
573
573
  Alert.alert('Error', 'An unexpected error occurred. Please try again.');
574
574
  }
575
575
  }
576
- }, [email, isVerifyingCode, debug, test]);
576
+ }, [email, isVerifyingCode, debug, testMode]);
577
577
 
578
578
  // Function to handle verification code submission
579
579
  const handleVerificationSubmit = useCallback(async () => {
@@ -1157,7 +1157,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
1157
1157
  <Text style={styles.privacyMessage}>
1158
1158
  None of your app data is shared with ANYONE
1159
1159
  </Text>
1160
- {(debug || test) && (
1160
+ {(debug || testMode) && (
1161
1161
  <Text style={styles.developmentNote}>
1162
1162
  🧪 Test Mode: You can proceed without connecting any platforms
1163
1163
  </Text>