@onairos/react-native 3.1.1 โ†’ 3.1.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.
@@ -11,6 +11,7 @@ import {
11
11
  TouchableWithoutFeedback,
12
12
  SafeAreaView,
13
13
  Image,
14
+ Alert,
14
15
  } from 'react-native';
15
16
  import Icon from 'react-native-vector-icons/MaterialIcons';
16
17
  import { io, Socket } from 'socket.io-client';
@@ -34,10 +35,13 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
34
35
  const [socketConnected, setSocketConnected] = useState(false);
35
36
  const [trainingStatus, setTrainingStatus] = useState('Initializing...');
36
37
  const [hasError, setHasError] = useState(false);
38
+ const [errorCode, setErrorCode] = useState<string | null>(null);
37
39
  const [isTrainingComplete, setIsTrainingComplete] = useState(false);
38
40
  const [userTraits, setUserTraits] = useState<any>(null);
39
41
  const [inferenceResults, setInferenceResults] = useState<any>(null);
40
42
  const [internalProgress, setInternalProgress] = useState(0);
43
+ const [isEnochSDK, setIsEnochSDK] = useState(false); // Determine if using Enoch SDK
44
+ const [trainingResults, setTrainingResults] = useState<any>(null);
41
45
 
42
46
  // Use internal progress if available, otherwise fall back to prop progress
43
47
  const currentProgress = internalProgress > 0 ? internalProgress / 100 : progress;
@@ -52,36 +56,48 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
52
56
  name: username || 'mobile_user'
53
57
  };
54
58
 
55
- // Start Enoch training via API
56
- const startEnochTraining = async (socketId: string) => {
59
+ // Check if we should use Enoch SDK (based on modelKey or other criteria)
60
+ useEffect(() => {
61
+ // For now, default to clean training unless specified
62
+ // In real implementation, this would be determined by SDK type
63
+ setIsEnochSDK(modelKey?.includes('enoch') || false);
64
+ }, [modelKey]);
65
+
66
+ // Start training with new API schema
67
+ const startTraining = async (socketId: string) => {
57
68
  try {
58
69
  setTrainingStatus('Starting training...');
59
70
  setInternalProgress(10);
71
+ setHasError(false);
72
+ setErrorCode(null);
60
73
 
61
74
  if (!userToken) {
62
75
  throw new Error('No authentication token available');
63
76
  }
64
77
 
65
- console.log('๐Ÿš€ Starting Enoch training with socketId:', socketId);
66
- console.log('๐Ÿ” Production mode: Using live training API');
78
+ // Determine endpoint based on SDK type
79
+ const endpoint = isEnochSDK ? '/mobile-training/enoch' : '/mobile-training/clean';
80
+
81
+ console.log(`๐Ÿš€ Starting training with endpoint: ${endpoint}`);
82
+ console.log('๐Ÿ” Production mode: Using new training API schema');
67
83
 
68
- // Prepare user data for training
84
+ // Prepare training data in new format
69
85
  const trainingData = {
70
- socketId,
71
- username: userInfo?.username || userInfo?.name || username || 'mobile_user',
72
- email: userInfo?.email || null,
73
- modelKey: modelKey || null,
74
- userId: userInfo?.id || null
86
+ Info: {
87
+ username: userInfo?.username || userInfo?.name || username || 'mobile_user',
88
+ // Mock connected platforms - in real implementation this would come from props
89
+ connectedPlatforms: ['youtube', 'instagram', 'reddit'] // TODO: Get from actual connections
90
+ }
75
91
  };
76
92
 
77
93
  console.log('๐Ÿ“ค Sending training data:', trainingData);
78
94
 
79
- // Call the actual training API - backend confirmed this is working
80
- const response = await fetch('https://api2.onairos.uk/enoch/trainModel/mobile', {
95
+ // Call the new training API
96
+ const response = await fetch(`https://api2.onairos.uk${endpoint}`, {
81
97
  method: 'POST',
82
98
  headers: {
83
99
  'Content-Type': 'application/json',
84
- 'Authorization': `Bearer ${userToken || 'temp-token'}` // Backend has JWT auth working
100
+ 'Authorization': `Bearer ${userToken}` // JWT token authentication
85
101
  },
86
102
  body: JSON.stringify(trainingData)
87
103
  });
@@ -90,66 +106,142 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
90
106
 
91
107
  if (result.success) {
92
108
  console.log('๐Ÿš€ Training Started:', result.message);
93
- setTrainingStatus('Training model...');
109
+ setTrainingStatus('Training initiated...');
94
110
  setInternalProgress(20);
95
111
  } else {
96
112
  console.error('Training start failed:', result.error);
97
- setTrainingStatus(`Error: ${result.error}`);
98
- setHasError(true);
99
113
 
100
- // In production mode, don't fallback to simulation on API failure
101
- console.error('๐Ÿšจ Production mode: Training failed, not falling back to simulation');
114
+ // Handle specific error codes from new schema
115
+ if (result.code) {
116
+ setErrorCode(result.code);
117
+ handleTrainingError(result.code, result.error);
118
+ } else {
119
+ setTrainingStatus(`Error: ${result.error}`);
120
+ setHasError(true);
121
+ }
102
122
  }
103
123
 
104
124
  } catch (error) {
105
125
  console.error('Training start error:', error);
106
126
  setTrainingStatus(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
107
127
  setHasError(true);
108
-
109
- // In production mode, show the actual error
110
- console.error('๐Ÿšจ Production mode: Training failed with error:', error);
128
+ }
129
+ };
130
+
131
+ // Handle training errors with specific error codes
132
+ const handleTrainingError = (code: string, message: string) => {
133
+ console.error(`Training Error [${code}]:`, message);
134
+ setHasError(true);
135
+
136
+ switch (code) {
137
+ case 'PIN_REQUIRED':
138
+ setTrainingStatus('PIN setup required');
139
+ Alert.alert(
140
+ 'PIN Required',
141
+ 'Please set up your PIN first before training.',
142
+ [
143
+ { text: 'Cancel', style: 'cancel' },
144
+ { text: 'Set PIN', onPress: () => {
145
+ // TODO: Navigate to PIN setup
146
+ console.log('Navigate to PIN setup');
147
+ }}
148
+ ]
149
+ );
150
+ break;
151
+
152
+ case 'CONNECTIONS_REQUIRED':
153
+ setTrainingStatus('No connected accounts');
154
+ Alert.alert(
155
+ 'Connections Required',
156
+ 'Please connect at least one social media account before training.',
157
+ [
158
+ { text: 'Cancel', style: 'cancel' },
159
+ { text: 'Connect', onPress: () => {
160
+ // TODO: Navigate to connections
161
+ console.log('Navigate to connections');
162
+ }}
163
+ ]
164
+ );
165
+ break;
166
+
167
+ case 'INSUFFICIENT_DATA':
168
+ setTrainingStatus('Insufficient data for training');
169
+ Alert.alert(
170
+ 'Insufficient Data',
171
+ 'We need more interaction data to train your model effectively. Please use your connected platforms more and try again.',
172
+ [{ text: 'OK', style: 'default' }]
173
+ );
174
+ break;
175
+
176
+ case 'ENCRYPTION_REQUIRED':
177
+ setTrainingStatus('Model encryption failed');
178
+ Alert.alert(
179
+ 'Encryption Error',
180
+ 'Failed to encrypt your model. Please try again.',
181
+ [{ text: 'OK', style: 'default' }]
182
+ );
183
+ break;
184
+
185
+ default:
186
+ setTrainingStatus(`Error: ${message}`);
187
+ Alert.alert(
188
+ 'Training Error',
189
+ message || 'An unexpected error occurred during training.',
190
+ [{ text: 'OK', style: 'default' }]
191
+ );
111
192
  }
112
193
  };
113
194
 
114
195
  // Simulate training progress for test mode
115
196
  const simulateTraining = () => {
116
- console.log('๐Ÿงช Starting simulated training...');
117
- setTrainingStatus('Initializing training...');
118
- setInternalProgress(10);
197
+ console.log('๐Ÿงช Starting simulated training with new schema phases...');
198
+ setTrainingStatus('Validating Requirements');
199
+ setInternalProgress(5);
119
200
 
120
- // Fast training for test mode (shorter delays)
121
- const baseDelay = 800;
122
- const stages = [
123
- { progress: 20, status: 'Analyzing data patterns...', delay: baseDelay },
124
- { progress: 35, status: 'Building neural network...', delay: baseDelay },
125
- { progress: 50, status: 'Training model...', delay: baseDelay },
126
- { progress: 65, status: 'Optimizing parameters...', delay: baseDelay },
127
- { progress: 80, status: 'Running test inference...', delay: baseDelay },
128
- { progress: 95, status: 'Finalizing model...', delay: baseDelay },
129
- { progress: 100, status: 'Complete!', delay: 300 },
201
+ // Simulate all phases from the new schema
202
+ const phases = [
203
+ { progress: 10, status: 'Validating Requirements', delay: 1000 },
204
+ { progress: 25, status: 'Training Model', delay: 2000 },
205
+ { progress: 50, status: 'Training Model', delay: 2000 },
206
+ { progress: 65, status: 'Model Trained - Running Test Inference', delay: 1500 },
207
+ { progress: 80, status: 'Encrypting and Compressing Model', delay: 1000 },
208
+ { progress: 95, status: 'Uploading Encrypted Model to ARDrive', delay: 1500 },
209
+ { progress: 100, status: 'Complete!', delay: 500 },
130
210
  ];
131
211
 
132
- let currentStage = 0;
212
+ let currentPhase = 0;
133
213
 
134
214
  const progressInterval = setInterval(() => {
135
- if (currentStage < stages.length) {
136
- const stage = stages[currentStage];
137
- setInternalProgress(stage.progress);
138
- setTrainingStatus(stage.status);
215
+ if (currentPhase < phases.length) {
216
+ const phase = phases[currentPhase];
217
+ setInternalProgress(phase.progress);
218
+ setTrainingStatus(phase.status);
139
219
 
140
- if (stage.progress === 100) {
220
+ if (phase.progress === 100) {
141
221
  setIsTrainingComplete(true);
142
222
  clearInterval(progressInterval);
143
223
 
224
+ // Mock completion results
225
+ const mockResults = {
226
+ completed: true,
227
+ message: "Model trained, encrypted, and stored successfully",
228
+ storage: "ARDrive",
229
+ encryption: true,
230
+ inference: isEnochSDK, // Only Enoch SDK runs inference
231
+ modelUrl: `https://arweave.net/mock_tx_${Date.now()}`
232
+ };
233
+
234
+ setTrainingResults(mockResults);
235
+
144
236
  // Auto-complete after a short delay
145
237
  setTimeout(() => {
146
238
  onComplete && onComplete();
147
- }, 800);
239
+ }, 1000);
148
240
  }
149
241
 
150
- currentStage++;
242
+ currentPhase++;
151
243
  }
152
- }, stages[currentStage]?.delay || baseDelay);
244
+ }, phases[currentPhase]?.delay || 1000);
153
245
 
154
246
  // Cleanup interval on unmount
155
247
  return () => clearInterval(progressInterval);
@@ -162,16 +254,17 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
162
254
  console.log('Setting up socket connection for training...');
163
255
  console.log('๐Ÿง‘โ€๐Ÿ’ป User info available:', userInfo);
164
256
  console.log('๐Ÿงช Test mode:', test);
257
+ console.log('๐Ÿ”ง Using Enoch SDK:', isEnochSDK);
165
258
 
166
259
  // If test mode is enabled, use simulation instead of real API
167
260
  if (test) {
168
- console.log('๐Ÿงช Test mode enabled - Using simulated training');
261
+ console.log('๐Ÿงช Test mode enabled - Using simulated training with new schema');
169
262
  setSocketConnected(true);
170
263
  simulateTraining();
171
264
  return;
172
265
  }
173
266
 
174
- // Initialize real socket connection - backend confirmed this is working
267
+ // Initialize real socket connection
175
268
  try {
176
269
  console.log('๐Ÿ”Œ Production mode: Initializing real socket connection');
177
270
 
@@ -181,13 +274,13 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
181
274
  autoConnect: false
182
275
  });
183
276
 
184
- // Socket event listeners
277
+ // Socket event listeners for new schema
185
278
  socketRef.current.on('connect', () => {
186
279
  console.log('โœ… Socket connected for training');
187
280
  setSocketConnected(true);
188
281
  const socketId = socketRef.current?.id;
189
282
  if (socketId) {
190
- startEnochTraining(socketId);
283
+ startTraining(socketId);
191
284
  }
192
285
  });
193
286
 
@@ -201,42 +294,92 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
201
294
  setTrainingStatus('Connection failed');
202
295
  setHasError(true);
203
296
  });
297
+
298
+ // Handle training updates with new schema phases
299
+ socketRef.current.on('trainingUpdate', (data) => {
300
+ console.log('๐Ÿ“ก Training update received:', data);
301
+
302
+ if (data.error) {
303
+ if (data.code) {
304
+ setErrorCode(data.code);
305
+ handleTrainingError(data.code, data.error);
306
+ } else {
307
+ console.error('Training update error:', data.error);
308
+ setTrainingStatus(`Error: ${data.error}`);
309
+ setHasError(true);
310
+ }
311
+ } else {
312
+ // Handle different status phases
313
+ if (data.status) {
314
+ setTrainingStatus(data.status);
315
+
316
+ // Update progress based on phase
317
+ switch (data.status) {
318
+ case 'Validating Requirements':
319
+ setInternalProgress(10);
320
+ break;
321
+ case 'Training Model':
322
+ setInternalProgress(data.percent || 40);
323
+ break;
324
+ case 'Model Trained - Running Test Inference':
325
+ setInternalProgress(65);
326
+ break;
327
+ case 'Encrypting and Compressing Model':
328
+ setInternalProgress(80);
329
+ break;
330
+ case 'Uploading Encrypted Model to ARDrive':
331
+ setInternalProgress(90);
332
+ break;
333
+ default:
334
+ if (data.percent) {
335
+ setInternalProgress(data.percent);
336
+ }
337
+ }
338
+ }
339
+
340
+ if (data.progress) {
341
+ setInternalProgress(data.progress);
342
+ }
343
+ }
344
+ });
204
345
 
346
+ // Handle training completion with new schema
205
347
  socketRef.current.on('trainingCompleted', (data) => {
206
348
  console.log('โœ… Training Complete:', data);
207
- setTrainingStatus('Running test inference...');
208
- setInternalProgress(60);
349
+
350
+ if (data.completed && data.storage === 'ARDrive' && data.encryption === true) {
351
+ setTrainingStatus('Training completed successfully!');
352
+ setIsTrainingComplete(true);
353
+ setInternalProgress(100);
354
+ setTrainingResults(data);
355
+
356
+ // Display results
357
+ console.log('โœ… Model URL:', data.modelUrl);
358
+ console.log('โœ… Storage:', data.storage);
359
+ console.log('โœ… Encrypted:', data.encryption);
360
+ console.log('โœ… Inference:', data.inference);
361
+
362
+ // Auto-complete after a short delay
363
+ setTimeout(() => {
364
+ onComplete && onComplete();
365
+ }, 1500);
366
+ } else {
367
+ console.error('Training completion missing required fields:', data);
368
+ setTrainingStatus('Training completed with warnings');
369
+ setHasError(true);
370
+ }
209
371
  });
210
-
372
+
373
+ // Legacy handlers for backward compatibility
211
374
  socketRef.current.on('inferenceCompleted', (data) => {
212
375
  console.log('๐Ÿง  Inference Complete:', data);
213
- setTrainingStatus('Uploading to S3...');
214
- setInternalProgress(80);
215
376
  setUserTraits(data.traits);
216
377
  setInferenceResults(data.inferenceResults);
217
378
  });
218
379
 
219
380
  socketRef.current.on('modelStandby', (data) => {
220
- console.log('๐ŸŽ‰ All Complete:', data);
221
- setIsTrainingComplete(true);
222
- setTrainingStatus('Complete!');
223
- setInternalProgress(100);
224
-
225
- // Auto-complete after a short delay
226
- setTimeout(() => {
227
- onComplete && onComplete();
228
- }, 1500);
229
- });
230
-
231
- socketRef.current.on('trainingUpdate', (data) => {
232
- if (data.error) {
233
- console.error('Training update error:', data.error);
234
- setTrainingStatus(`Error: ${data.error}`);
235
- setHasError(true);
236
- } else if (data.progress) {
237
- setInternalProgress(data.progress);
238
- setTrainingStatus(data.status || 'Training in progress...');
239
- }
381
+ console.log('๐ŸŽ‰ Model Ready:', data);
382
+ // This is handled by trainingCompleted now
240
383
  });
241
384
 
242
385
  // Connect to socket
@@ -256,7 +399,46 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
256
399
  socketRef.current = null;
257
400
  }
258
401
  };
259
- }, [visible, userInfo, test]);
402
+ }, [visible, userInfo, test, isEnochSDK]);
403
+
404
+ // Render error details for specific error codes
405
+ const renderErrorDetails = () => {
406
+ if (!hasError || !errorCode) return null;
407
+
408
+ const errorDetails = {
409
+ 'PIN_REQUIRED': {
410
+ icon: 'lock',
411
+ title: 'PIN Setup Required',
412
+ description: 'Set up your PIN to secure your AI model'
413
+ },
414
+ 'CONNECTIONS_REQUIRED': {
415
+ icon: 'link',
416
+ title: 'Connect Accounts',
417
+ description: 'Connect at least one social media account'
418
+ },
419
+ 'INSUFFICIENT_DATA': {
420
+ icon: 'data-usage',
421
+ title: 'More Data Needed',
422
+ description: 'We need more interaction data to train effectively'
423
+ },
424
+ 'ENCRYPTION_REQUIRED': {
425
+ icon: 'security',
426
+ title: 'Encryption Failed',
427
+ description: 'Failed to encrypt your model securely'
428
+ }
429
+ };
430
+
431
+ const details = errorDetails[errorCode];
432
+ if (!details) return null;
433
+
434
+ return (
435
+ <View style={styles.errorDetailsContainer}>
436
+ <Icon name={details.icon} size={32} color="#FF6B6B" />
437
+ <Text style={styles.errorTitle}>{details.title}</Text>
438
+ <Text style={styles.errorDescription}>{details.description}</Text>
439
+ </View>
440
+ );
441
+ };
260
442
 
261
443
  return (
262
444
  <Modal
@@ -282,7 +464,9 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
282
464
  resizeMode="contain"
283
465
  />
284
466
 
285
- <Text style={styles.title}>Training Your AI</Text>
467
+ <Text style={styles.title}>
468
+ {isEnochSDK ? 'Training AI with Inference' : 'Training Your AI'}
469
+ </Text>
286
470
 
287
471
  <View style={styles.progressContainer}>
288
472
  <View style={styles.progressBar}>
@@ -307,6 +491,31 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
307
491
  </View>
308
492
  )}
309
493
 
494
+ {renderErrorDetails()}
495
+
496
+ {isTrainingComplete && trainingResults && (
497
+ <View style={styles.resultsContainer}>
498
+ <Icon name="check-circle" size={32} color="#4CAF50" />
499
+ <Text style={styles.resultsTitle}>Training Complete!</Text>
500
+ <View style={styles.resultsDetails}>
501
+ <View style={styles.resultItem}>
502
+ <Icon name="cloud-done" size={16} color="#666" />
503
+ <Text style={styles.resultText}>Storage: {trainingResults.storage}</Text>
504
+ </View>
505
+ <View style={styles.resultItem}>
506
+ <Icon name="security" size={16} color="#666" />
507
+ <Text style={styles.resultText}>Encrypted: {trainingResults.encryption ? 'Yes' : 'No'}</Text>
508
+ </View>
509
+ {trainingResults.inference && (
510
+ <View style={styles.resultItem}>
511
+ <Icon name="psychology" size={16} color="#666" />
512
+ <Text style={styles.resultText}>Inference: Completed</Text>
513
+ </View>
514
+ )}
515
+ </View>
516
+ </View>
517
+ )}
518
+
310
519
  <View style={styles.footer}>
311
520
  <TouchableOpacity
312
521
  style={styles.cancelButton}
@@ -390,6 +599,7 @@ const styles = StyleSheet.create({
390
599
  fontSize: 14,
391
600
  color: COLORS.text.secondary,
392
601
  marginBottom: 24,
602
+ textAlign: 'center',
393
603
  },
394
604
  progressContainer: {
395
605
  width: '100%',
@@ -466,11 +676,62 @@ const styles = StyleSheet.create({
466
676
  errorContainer: {
467
677
  flexDirection: 'row',
468
678
  alignItems: 'center',
469
- marginBottom: 24,
679
+ marginBottom: 16,
470
680
  },
471
681
  errorText: {
472
682
  fontSize: 14,
473
683
  color: '#FF6B6B',
474
684
  marginLeft: 8,
475
685
  },
686
+ errorDetailsContainer: {
687
+ alignItems: 'center',
688
+ marginBottom: 24,
689
+ padding: 16,
690
+ backgroundColor: '#FFF5F5',
691
+ borderRadius: 12,
692
+ borderWidth: 1,
693
+ borderColor: '#FFEBEE',
694
+ },
695
+ errorTitle: {
696
+ fontSize: 16,
697
+ fontWeight: '600',
698
+ color: '#FF6B6B',
699
+ marginTop: 8,
700
+ marginBottom: 4,
701
+ },
702
+ errorDescription: {
703
+ fontSize: 14,
704
+ color: '#666',
705
+ textAlign: 'center',
706
+ },
707
+ resultsContainer: {
708
+ alignItems: 'center',
709
+ marginBottom: 24,
710
+ padding: 16,
711
+ backgroundColor: '#F5F5F5',
712
+ borderRadius: 12,
713
+ borderWidth: 1,
714
+ borderColor: '#E0E0E0',
715
+ },
716
+ resultsTitle: {
717
+ fontSize: 16,
718
+ fontWeight: '600',
719
+ color: '#4CAF50',
720
+ marginTop: 8,
721
+ marginBottom: 12,
722
+ },
723
+ resultsDetails: {
724
+ alignItems: 'flex-start',
725
+ width: '100%',
726
+ },
727
+ resultItem: {
728
+ flexDirection: 'row',
729
+ alignItems: 'center',
730
+ marginBottom: 4,
731
+ },
732
+ resultText: {
733
+ fontSize: 14,
734
+ color: '#666',
735
+ marginLeft: 8,
736
+ },
476
737
  });
@@ -485,6 +485,8 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
485
485
  // Function to handle email submission
486
486
  const handleEmailSubmit = useCallback(async () => {
487
487
  console.log('๐Ÿš€ handleEmailSubmit called with email:', email);
488
+ console.log('๐Ÿงช testMode value:', testMode);
489
+ console.log('๐Ÿงช isTestMode computed:', isTestMode);
488
490
 
489
491
  try {
490
492
  if (!email || !email.trim()) {
@@ -503,9 +505,11 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
503
505
 
504
506
  console.log('๐Ÿ“ง Email validation passed, proceeding...');
505
507
 
506
- // For test mode, just proceed to verification step without API call
507
- if (isTestMode) {
508
- console.log('๐Ÿงช Test mode: Skipping API call, proceeding to verification');
508
+ // Check if we should skip API calls entirely (only for specific test scenarios)
509
+ const shouldSkipApiCalls = (typeof testMode === 'object' && testMode !== null && testMode.skipApiCalls === true);
510
+
511
+ if (shouldSkipApiCalls) {
512
+ console.log('๐Ÿงช Test mode with skipApiCalls: true - Skipping API call, proceeding to verification');
509
513
  setStep('verify');
510
514
  return;
511
515
  }
@@ -518,6 +522,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
518
522
 
519
523
  setIsVerifyingCode(true);
520
524
  console.log('๐Ÿ”„ Starting email verification process...');
525
+ console.log('๐Ÿ“ก Will make API call with testMode flag:', isTestMode);
521
526
 
522
527
  // Wrap the entire API call in a timeout to prevent hanging
523
528
  const timeoutPromise = new Promise((_, reject) => {
@@ -583,7 +588,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
583
588
  Alert.alert('Error', 'An unexpected error occurred. Please try again.');
584
589
  }
585
590
  }
586
- }, [email, isVerifyingCode, debug, testMode]);
591
+ }, [email, isVerifyingCode, debug, testMode, isTestMode]);
587
592
 
588
593
  // Function to handle verification code submission
589
594
  const handleVerificationSubmit = useCallback(async () => {
@@ -332,16 +332,20 @@ export const initiateNativeAuth = async (platform: string, username?: string): P
332
332
 
333
333
  // Configure Google Sign-In with better error handling
334
334
  try {
335
+ const youtubeConfig = PLATFORM_AUTH_CONFIG.youtube;
336
+ const webClientId = youtubeConfig.clientId || '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com';
337
+ const iosClientId = youtubeConfig.iosClientId || webClientId;
338
+
335
339
  await GoogleSignin.configure({
336
- webClientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com',
337
- iosClientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com',
340
+ webClientId: webClientId,
341
+ iosClientId: iosClientId,
338
342
  scopes: ['https://www.googleapis.com/auth/youtube.readonly'],
339
343
  offlineAccess: true,
340
344
  hostedDomain: '',
341
345
  forceCodeForRefreshToken: true,
342
346
  accountName: '',
343
347
  });
344
- console.log('โœ… Google Sign-In configured successfully');
348
+ console.log('โœ… Google Sign-In configured successfully with client ID:', webClientId.substring(0, 20) + '...');
345
349
  } catch (configError) {
346
350
  console.error('โŒ Google Sign-In configuration failed:', configError);
347
351
  throw new Error(`Google Sign-In configuration failed: ${configError.message || configError}`);
@@ -794,9 +798,12 @@ export const updateGoogleClientIds = (config: {
794
798
  PLATFORM_AUTH_CONFIG.youtube = {
795
799
  ...PLATFORM_AUTH_CONFIG.youtube,
796
800
  clientId: config.webClientId || PLATFORM_AUTH_CONFIG.youtube.clientId,
801
+ iosClientId: config.iosClientId || PLATFORM_AUTH_CONFIG.youtube.iosClientId,
797
802
  };
798
803
 
799
804
  console.log('โœ… Google client IDs updated successfully');
805
+ console.log(' - Web Client ID:', config.webClientId ? config.webClientId.substring(0, 20) + '...' : 'not provided');
806
+ console.log(' - iOS Client ID:', config.iosClientId ? config.iosClientId.substring(0, 20) + '...' : 'not provided');
800
807
  }
801
808
  };
802
809
 
@@ -211,6 +211,7 @@ export interface PlatformAuthConfig {
211
211
  authEndpoint: string;
212
212
  color: string;
213
213
  clientId?: string;
214
+ iosClientId?: string;
214
215
  redirectUri?: string;
215
216
  scope?: string;
216
217
  responseType?: string;
package/src/types.ts CHANGED
@@ -18,6 +18,7 @@ export interface TestModeOptions {
18
18
  // Optional tweaks
19
19
  fastTraining?: boolean; // Speed up training simulation
20
20
  skipRealConnections?: boolean; // Allow mock platform connections
21
+ skipApiCalls?: boolean; // Skip API calls entirely (for pure UI testing)
21
22
  }
22
23
 
23
24
  export interface ApiKeyConfig {
@@ -239,6 +240,7 @@ export interface PlatformAuthConfig {
239
240
  authEndpoint: string;
240
241
  color: string;
241
242
  clientId?: string;
243
+ iosClientId?: string;
242
244
  redirectUri?: string;
243
245
  scope?: string;
244
246
  responseType?: string;