@onairos/react-native 3.0.54 → 3.0.56
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/lib/commonjs/components/TrainingModal.js +8 -14
- package/lib/commonjs/components/TrainingModal.js.map +1 -1
- package/lib/commonjs/components/UniversalOnboarding.js +217 -92
- package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
- package/lib/commonjs/services/platformAuthService.js +46 -19
- package/lib/commonjs/services/platformAuthService.js.map +1 -1
- package/lib/module/components/TrainingModal.js +9 -15
- package/lib/module/components/TrainingModal.js.map +1 -1
- package/lib/module/components/UniversalOnboarding.js +218 -93
- package/lib/module/components/UniversalOnboarding.js.map +1 -1
- package/lib/module/services/platformAuthService.js +46 -19
- package/lib/module/services/platformAuthService.js.map +1 -1
- package/lib/typescript/components/TrainingModal.d.ts.map +1 -1
- package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
- package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/components/TrainingModal.tsx +8 -15
- package/src/components/UniversalOnboarding.tsx +247 -93
- package/src/services/platformAuthService.ts +43 -15
|
@@ -22,11 +22,11 @@ import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
|
22
22
|
import { PlatformList } from './PlatformList';
|
|
23
23
|
import { PinInput } from './PinInput';
|
|
24
24
|
import { TrainingModal } from './TrainingModal';
|
|
25
|
+
import { DataRequestModal } from './DataRequestModal';
|
|
25
26
|
import { OAuthWebView } from './onboarding/OAuthWebView';
|
|
26
27
|
import { useConnections } from '../hooks/useConnections';
|
|
27
28
|
import { COLORS, DEEP_LINK_CONFIG } from '../constants';
|
|
28
29
|
import { initiateOAuth, initiateNativeAuth, hasNativeSDK, isOAuthCallback, testApiConnectivity, handleOAuthCallbackUrl, refreshYouTubeTokens, requestEmailVerification, verifyEmailCode, checkEmailVerificationStatus, disconnectPlatform } from '../services/platformAuthService';
|
|
29
|
-
import { EmailVerificationModal } from './EmailVerificationModal';
|
|
30
30
|
import type { UniversalOnboardingProps, ConnectionStatus } from '../types';
|
|
31
31
|
|
|
32
32
|
// Optional Opacity SDK imports with error handling
|
|
@@ -61,7 +61,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
61
61
|
auto = false,
|
|
62
62
|
partner,
|
|
63
63
|
}) => {
|
|
64
|
-
const [step, setStep] = useState<'email' | 'connect' | 'pin' | 'training' | 'oauth' | 'success'>('email');
|
|
64
|
+
const [step, setStep] = useState<'email' | 'verify' | 'dataRequest' | 'connect' | 'pin' | 'training' | 'oauth' | 'success'>('email');
|
|
65
65
|
const [connections, setConnections] = useState<ConnectionStatus>({});
|
|
66
66
|
const [pin, setPin] = useState<string>('');
|
|
67
67
|
const [selectedTier, setSelectedTier] = useState<'Small' | 'Medium' | 'Large'>('Medium');
|
|
@@ -77,7 +77,10 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
77
77
|
const [isConnectingPlatform, setIsConnectingPlatform] = useState<boolean>(false);
|
|
78
78
|
const [showLoginWebView, setShowLoginWebView] = useState<boolean>(false);
|
|
79
79
|
const [email, setEmail] = useState<string>('');
|
|
80
|
-
const [
|
|
80
|
+
const [verificationCode, setVerificationCode] = useState<string>('');
|
|
81
|
+
const [isVerifyingCode, setIsVerifyingCode] = useState<boolean>(false);
|
|
82
|
+
const [showDataRequestModal, setShowDataRequestModal] = useState<boolean>(false);
|
|
83
|
+
const [isExistingUser, setIsExistingUser] = useState<boolean>(false);
|
|
81
84
|
|
|
82
85
|
// Add refs for cleanup
|
|
83
86
|
const successTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -237,23 +240,12 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
237
240
|
|
|
238
241
|
console.log('✅ API connectivity confirmed');
|
|
239
242
|
|
|
240
|
-
// Instagram: Use Opacity SDK exclusively
|
|
243
|
+
// Instagram: Use Opacity SDK exclusively
|
|
241
244
|
if (platformId === 'instagram') {
|
|
242
245
|
// Check if Opacity SDK is available
|
|
243
246
|
if (!opacityInit || !OpacityEnvironment || !opacityGet) {
|
|
244
|
-
console.
|
|
245
|
-
|
|
246
|
-
const oauthUrl = await initiateOAuth(platformId, username, AppName);
|
|
247
|
-
|
|
248
|
-
if (oauthUrl) {
|
|
249
|
-
console.log(`✅ Received OAuth URL for ${platformId}:`, oauthUrl);
|
|
250
|
-
setCurrentPlatform(platformId);
|
|
251
|
-
setOauthUrl(oauthUrl);
|
|
252
|
-
setStep('oauth');
|
|
253
|
-
} else {
|
|
254
|
-
throw new Error(`Failed to get authorization URL for ${platformId}. Please try again.`);
|
|
255
|
-
}
|
|
256
|
-
return;
|
|
247
|
+
console.error('❌ Opacity SDK not available for Instagram');
|
|
248
|
+
throw new Error('Instagram connection requires the Opacity SDK. Please ensure @opacity-labs/react-native-opacity is properly installed and configured.');
|
|
257
249
|
}
|
|
258
250
|
|
|
259
251
|
console.log('🔌 Initializing Opacity SDK for Instagram...');
|
|
@@ -461,7 +453,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
461
453
|
}, []);
|
|
462
454
|
|
|
463
455
|
// Function to handle email submission
|
|
464
|
-
const handleEmailSubmit = useCallback(() => {
|
|
456
|
+
const handleEmailSubmit = useCallback(async () => {
|
|
465
457
|
if (!email.trim()) {
|
|
466
458
|
Alert.alert('Error', 'Please enter your email address');
|
|
467
459
|
return;
|
|
@@ -475,34 +467,60 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
475
467
|
}
|
|
476
468
|
|
|
477
469
|
console.log('📧 Email submitted:', email.trim());
|
|
478
|
-
|
|
470
|
+
|
|
471
|
+
// Request verification code
|
|
472
|
+
try {
|
|
473
|
+
const result = await requestEmailVerification(email.trim());
|
|
474
|
+
if (result.success) {
|
|
475
|
+
console.log('✅ Verification code requested');
|
|
476
|
+
setStep('verify');
|
|
477
|
+
} else {
|
|
478
|
+
Alert.alert('Error', result.error || 'Failed to send verification code');
|
|
479
|
+
}
|
|
480
|
+
} catch (error) {
|
|
481
|
+
console.error('❌ Error requesting verification:', error);
|
|
482
|
+
Alert.alert('Error', 'Failed to send verification code');
|
|
483
|
+
}
|
|
479
484
|
}, [email]);
|
|
480
485
|
|
|
481
|
-
// Function to handle
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
486
|
+
// Function to handle verification code submission
|
|
487
|
+
const handleVerificationSubmit = useCallback(async () => {
|
|
488
|
+
if (!verificationCode.trim() || verificationCode.trim().length !== 6) {
|
|
489
|
+
Alert.alert('Error', 'Please enter a 6-digit verification code');
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
485
492
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
493
|
+
setIsVerifyingCode(true);
|
|
494
|
+
|
|
495
|
+
try {
|
|
496
|
+
const result = await verifyEmailCode(email.trim(), verificationCode.trim());
|
|
497
|
+
|
|
498
|
+
if (result.success) {
|
|
499
|
+
console.log('✅ Email verification successful');
|
|
500
|
+
|
|
501
|
+
// For now, always treat as new users for testing (can be updated later)
|
|
502
|
+
// In production, this would check if user exists in database
|
|
503
|
+
const existingUser = false; // TODO: Check backend for existing user
|
|
504
|
+
setIsExistingUser(existingUser);
|
|
505
|
+
|
|
506
|
+
if (existingUser) {
|
|
507
|
+
console.log('Existing user detected, showing data request modal');
|
|
508
|
+
setShowDataRequestModal(true);
|
|
509
|
+
} else {
|
|
510
|
+
console.log('New user, proceeding to platform connection');
|
|
511
|
+
setUsername(email.split('@')[0]); // Use email prefix as username
|
|
512
|
+
setStep('connect');
|
|
513
|
+
}
|
|
514
|
+
} else {
|
|
515
|
+
Alert.alert('Verification Failed', result.error || 'Invalid verification code');
|
|
516
|
+
}
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error('❌ Error verifying code:', error);
|
|
519
|
+
Alert.alert('Error', 'Failed to verify code');
|
|
520
|
+
} finally {
|
|
521
|
+
setIsVerifyingCode(false);
|
|
497
522
|
}
|
|
498
|
-
}, [onComplete]);
|
|
499
|
-
|
|
500
|
-
// Function to handle email verification failure
|
|
501
|
-
const handleEmailVerificationFailed = useCallback((error: string) => {
|
|
502
|
-
console.error('❌ Email verification failed:', error);
|
|
503
|
-
setShowEmailVerification(false);
|
|
504
|
-
Alert.alert('Verification Failed', error);
|
|
505
|
-
}, []);
|
|
523
|
+
}, [email, verificationCode, onComplete]);
|
|
506
524
|
|
|
507
525
|
const handlePinSubmit = useCallback(async (userPin: string) => {
|
|
508
526
|
setPin(userPin);
|
|
@@ -537,39 +555,54 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
537
555
|
} catch (error) {
|
|
538
556
|
console.error('Failed to save session data:', error);
|
|
539
557
|
}
|
|
558
|
+
}, [connections, selectedTier, platformToggles, username, AppName, auto, inferenceData, partner]);
|
|
559
|
+
|
|
560
|
+
const handleTrainingComplete = useCallback(() => {
|
|
561
|
+
console.log('🎉 Training completed successfully');
|
|
540
562
|
|
|
541
|
-
//
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
if
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
563
|
+
// Prepare completion data
|
|
564
|
+
const completionData = {
|
|
565
|
+
pin,
|
|
566
|
+
connections,
|
|
567
|
+
platformToggles,
|
|
568
|
+
selectedTier,
|
|
569
|
+
tierData: requestData?.[selectedTier],
|
|
570
|
+
sessionSaved: true,
|
|
571
|
+
// Add inference data if auto mode is enabled
|
|
572
|
+
...(auto && inferenceData && { inferenceData }),
|
|
573
|
+
// Add partner info for special partners
|
|
574
|
+
...(partner && { partner: partner === 'couplebible' ? 'CoupleBible' : partner }),
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
console.log('Completion data prepared:', completionData);
|
|
578
|
+
|
|
579
|
+
// Close the modal first
|
|
580
|
+
handleClose();
|
|
581
|
+
|
|
582
|
+
// Then call the completion callback
|
|
583
|
+
setTimeout(() => {
|
|
584
|
+
onComplete('https://api2.onairos.uk', 'dummy-token', completionData);
|
|
585
|
+
}, 100);
|
|
586
|
+
}, [pin, connections, platformToggles, selectedTier, requestData, auto, inferenceData, partner, handleClose, onComplete]);
|
|
587
|
+
|
|
588
|
+
const handleDataRequestAccept = useCallback(() => {
|
|
589
|
+
console.log('Data request accepted for existing user');
|
|
590
|
+
setShowDataRequestModal(false);
|
|
591
|
+
|
|
592
|
+
// Complete onboarding for existing user
|
|
593
|
+
onComplete('https://api2.onairos.uk', 'existing-session-token', {
|
|
594
|
+
existingAccount: true,
|
|
595
|
+
email: email.trim(),
|
|
596
|
+
dataRequestAccepted: true,
|
|
597
|
+
requestData,
|
|
598
|
+
});
|
|
599
|
+
}, [email, onComplete, requestData]);
|
|
600
|
+
|
|
601
|
+
const handleDataRequestDecline = useCallback(() => {
|
|
602
|
+
console.log('Data request declined');
|
|
603
|
+
setShowDataRequestModal(false);
|
|
604
|
+
handleClose();
|
|
605
|
+
}, [handleClose]);
|
|
573
606
|
|
|
574
607
|
const canProceedToPin = useCallback(() => {
|
|
575
608
|
// Check if at least one platform is toggled on
|
|
@@ -665,6 +698,81 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
665
698
|
</View>
|
|
666
699
|
)}
|
|
667
700
|
|
|
701
|
+
{step === 'verify' && (
|
|
702
|
+
<View style={styles.emailInputContainer}>
|
|
703
|
+
<View style={styles.emailHeader}>
|
|
704
|
+
<View style={styles.onairosIcon}>
|
|
705
|
+
<Image
|
|
706
|
+
source={require('../assets/images/onairos_logo.png')}
|
|
707
|
+
style={styles.onairosLogo}
|
|
708
|
+
resizeMode="contain"
|
|
709
|
+
/>
|
|
710
|
+
</View>
|
|
711
|
+
<Text style={styles.emailTitle}>Enter Verification Code</Text>
|
|
712
|
+
<Text style={styles.emailSubtitle}>
|
|
713
|
+
We've sent a 6-digit code to {email}
|
|
714
|
+
</Text>
|
|
715
|
+
<Text style={styles.developmentNote}>
|
|
716
|
+
🔍 Development Mode: Any 6-digit code will work
|
|
717
|
+
</Text>
|
|
718
|
+
</View>
|
|
719
|
+
|
|
720
|
+
<View style={styles.emailInputSection}>
|
|
721
|
+
<View style={styles.codeInputContainer}>
|
|
722
|
+
{[0, 1, 2, 3, 4, 5].map((index) => (
|
|
723
|
+
<TextInput
|
|
724
|
+
key={index}
|
|
725
|
+
style={[
|
|
726
|
+
styles.codeDigit,
|
|
727
|
+
verificationCode.length === index && styles.codeDigitActive
|
|
728
|
+
]}
|
|
729
|
+
value={verificationCode[index] || ''}
|
|
730
|
+
onChangeText={(text) => {
|
|
731
|
+
if (text.length <= 1 && /^\d*$/.test(text)) {
|
|
732
|
+
const newCode = verificationCode.split('');
|
|
733
|
+
newCode[index] = text;
|
|
734
|
+
const updatedCode = newCode.join('').slice(0, 6);
|
|
735
|
+
setVerificationCode(updatedCode);
|
|
736
|
+
|
|
737
|
+
// Auto-focus next input
|
|
738
|
+
if (text && index < 5) {
|
|
739
|
+
// Focus next input (would need refs for actual implementation)
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}}
|
|
743
|
+
keyboardType="number-pad"
|
|
744
|
+
maxLength={1}
|
|
745
|
+
textAlign="center"
|
|
746
|
+
autoFocus={index === 0}
|
|
747
|
+
/>
|
|
748
|
+
))}
|
|
749
|
+
</View>
|
|
750
|
+
|
|
751
|
+
<TouchableOpacity
|
|
752
|
+
style={[
|
|
753
|
+
styles.emailSubmitButton,
|
|
754
|
+
(verificationCode.length !== 6 || isVerifyingCode) && styles.emailSubmitButtonDisabled
|
|
755
|
+
]}
|
|
756
|
+
onPress={handleVerificationSubmit}
|
|
757
|
+
disabled={verificationCode.length !== 6 || isVerifyingCode}
|
|
758
|
+
>
|
|
759
|
+
{isVerifyingCode ? (
|
|
760
|
+
<ActivityIndicator size="small" color="#fff" />
|
|
761
|
+
) : (
|
|
762
|
+
<Text style={styles.emailSubmitButtonText}>Verify</Text>
|
|
763
|
+
)}
|
|
764
|
+
</TouchableOpacity>
|
|
765
|
+
|
|
766
|
+
<TouchableOpacity
|
|
767
|
+
style={styles.backButton}
|
|
768
|
+
onPress={() => setStep('email')}
|
|
769
|
+
>
|
|
770
|
+
<Text style={styles.backButtonText}>← Back to email</Text>
|
|
771
|
+
</TouchableOpacity>
|
|
772
|
+
</View>
|
|
773
|
+
</View>
|
|
774
|
+
)}
|
|
775
|
+
|
|
668
776
|
{step === 'connect' && (
|
|
669
777
|
<>
|
|
670
778
|
{/* Header with app icon and arrow to Onairos icon */}
|
|
@@ -812,15 +920,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
812
920
|
progress={training.progress}
|
|
813
921
|
eta={training.eta}
|
|
814
922
|
onCancel={handleClose}
|
|
815
|
-
onComplete={
|
|
816
|
-
onComplete('https://api2.onairos.uk', 'dummy-token', {
|
|
817
|
-
pin,
|
|
818
|
-
connections,
|
|
819
|
-
platformToggles,
|
|
820
|
-
selectedTier,
|
|
821
|
-
tierData: requestData?.[selectedTier],
|
|
822
|
-
});
|
|
823
|
-
}}
|
|
923
|
+
onComplete={handleTrainingComplete}
|
|
824
924
|
modelKey="onairosTrainingModel"
|
|
825
925
|
username={username}
|
|
826
926
|
/>
|
|
@@ -839,22 +939,38 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
839
939
|
/>
|
|
840
940
|
)}
|
|
841
941
|
|
|
842
|
-
|
|
843
|
-
{showEmailVerification && (
|
|
844
|
-
<EmailVerificationModal
|
|
845
|
-
visible={showEmailVerification}
|
|
846
|
-
email={email.trim()}
|
|
847
|
-
onClose={() => setShowEmailVerification(false)}
|
|
848
|
-
onVerificationComplete={handleEmailVerificationComplete}
|
|
849
|
-
onVerificationFailed={handleEmailVerificationFailed}
|
|
850
|
-
/>
|
|
851
|
-
)}
|
|
942
|
+
|
|
852
943
|
|
|
853
944
|
</SafeAreaView>
|
|
854
945
|
</Animated.View>
|
|
855
946
|
</TouchableWithoutFeedback>
|
|
856
947
|
</View>
|
|
857
948
|
</TouchableWithoutFeedback>
|
|
949
|
+
|
|
950
|
+
{/* Data Request Modal for existing users */}
|
|
951
|
+
{showDataRequestModal && requestData && (
|
|
952
|
+
<DataRequestModal
|
|
953
|
+
visible={showDataRequestModal}
|
|
954
|
+
onClose={handleDataRequestDecline}
|
|
955
|
+
onAccept={handleDataRequestAccept}
|
|
956
|
+
requestData={{
|
|
957
|
+
// Convert DataTier format to expected format
|
|
958
|
+
Small: {
|
|
959
|
+
description: requestData.Small?.descriptions || 'Basic data access',
|
|
960
|
+
type: requestData.Small?.type || 'basic'
|
|
961
|
+
},
|
|
962
|
+
Medium: {
|
|
963
|
+
description: requestData.Medium?.descriptions || 'Standard data access',
|
|
964
|
+
type: requestData.Medium?.type || 'standard'
|
|
965
|
+
},
|
|
966
|
+
Large: {
|
|
967
|
+
description: requestData.Large?.descriptions || 'Full data access',
|
|
968
|
+
type: requestData.Large?.type || 'full'
|
|
969
|
+
}
|
|
970
|
+
}}
|
|
971
|
+
AppName={AppName}
|
|
972
|
+
/>
|
|
973
|
+
)}
|
|
858
974
|
</Modal>
|
|
859
975
|
);
|
|
860
976
|
};
|
|
@@ -1146,4 +1262,42 @@ const styles = StyleSheet.create({
|
|
|
1146
1262
|
fontSize: 16,
|
|
1147
1263
|
fontWeight: '600',
|
|
1148
1264
|
},
|
|
1265
|
+
// Verification code styles
|
|
1266
|
+
developmentNote: {
|
|
1267
|
+
fontSize: 14,
|
|
1268
|
+
color: '#FF9800',
|
|
1269
|
+
textAlign: 'center',
|
|
1270
|
+
marginTop: 8,
|
|
1271
|
+
backgroundColor: '#FFF3E0',
|
|
1272
|
+
padding: 8,
|
|
1273
|
+
borderRadius: 4,
|
|
1274
|
+
},
|
|
1275
|
+
codeInputContainer: {
|
|
1276
|
+
flexDirection: 'row',
|
|
1277
|
+
justifyContent: 'space-between',
|
|
1278
|
+
marginBottom: 24,
|
|
1279
|
+
paddingHorizontal: 20,
|
|
1280
|
+
},
|
|
1281
|
+
codeDigit: {
|
|
1282
|
+
width: 45,
|
|
1283
|
+
height: 55,
|
|
1284
|
+
borderWidth: 2,
|
|
1285
|
+
borderColor: '#ddd',
|
|
1286
|
+
borderRadius: 8,
|
|
1287
|
+
fontSize: 24,
|
|
1288
|
+
fontWeight: '600',
|
|
1289
|
+
color: '#000',
|
|
1290
|
+
backgroundColor: '#fff',
|
|
1291
|
+
},
|
|
1292
|
+
codeDigitActive: {
|
|
1293
|
+
borderColor: '#4CAF50',
|
|
1294
|
+
},
|
|
1295
|
+
backButton: {
|
|
1296
|
+
paddingVertical: 12,
|
|
1297
|
+
alignItems: 'center',
|
|
1298
|
+
},
|
|
1299
|
+
backButtonText: {
|
|
1300
|
+
color: '#666',
|
|
1301
|
+
fontSize: 16,
|
|
1302
|
+
},
|
|
1149
1303
|
});
|
|
@@ -220,23 +220,51 @@ export const initiateNativeAuth = async (platform: string, username?: string): P
|
|
|
220
220
|
throw new Error('Google Sign-In SDK not installed. Please install @react-native-google-signin/google-signin');
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
// Configure Google Sign-In
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
223
|
+
// Configure Google Sign-In with better error handling
|
|
224
|
+
try {
|
|
225
|
+
await GoogleSignin.configure({
|
|
226
|
+
webClientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com',
|
|
227
|
+
iosClientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com',
|
|
228
|
+
scopes: ['https://www.googleapis.com/auth/youtube.readonly'],
|
|
229
|
+
offlineAccess: true,
|
|
230
|
+
hostedDomain: '',
|
|
231
|
+
forceCodeForRefreshToken: true,
|
|
232
|
+
accountName: '',
|
|
233
|
+
});
|
|
234
|
+
console.log('✅ Google Sign-In configured successfully');
|
|
235
|
+
} catch (configError) {
|
|
236
|
+
console.error('❌ Google Sign-In configuration failed:', configError);
|
|
237
|
+
throw new Error(`Google Sign-In configuration failed: ${configError.message || configError}`);
|
|
238
|
+
}
|
|
233
239
|
|
|
234
|
-
|
|
235
|
-
|
|
240
|
+
// Check if Google Play Services are available (Android only)
|
|
241
|
+
try {
|
|
242
|
+
await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });
|
|
243
|
+
console.log('✅ Google Play Services available');
|
|
244
|
+
} catch (playServicesError) {
|
|
245
|
+
console.error('❌ Google Play Services error:', playServicesError);
|
|
246
|
+
throw new Error(`Google Play Services not available: ${playServicesError.message || playServicesError}`);
|
|
247
|
+
}
|
|
236
248
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
249
|
+
// Sign in with Google
|
|
250
|
+
let userInfo;
|
|
251
|
+
try {
|
|
252
|
+
userInfo = await GoogleSignin.signIn();
|
|
253
|
+
console.log('✅ Google Sign-In successful:', userInfo.user?.email);
|
|
254
|
+
} catch (signInError) {
|
|
255
|
+
console.error('❌ Google Sign-In failed:', signInError);
|
|
256
|
+
|
|
257
|
+
// Handle specific error codes
|
|
258
|
+
if (signInError.code === statusCodes.SIGN_IN_CANCELLED) {
|
|
259
|
+
throw new Error('Google Sign-In was cancelled by user');
|
|
260
|
+
} else if (signInError.code === statusCodes.IN_PROGRESS) {
|
|
261
|
+
throw new Error('Google Sign-In already in progress');
|
|
262
|
+
} else if (signInError.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
|
|
263
|
+
throw new Error('Google Play Services not available or outdated');
|
|
264
|
+
} else {
|
|
265
|
+
throw new Error(`Google Sign-In failed: ${signInError.message || signInError}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
240
268
|
|
|
241
269
|
// Get access token for API calls
|
|
242
270
|
const tokens = await GoogleSignin.getTokens();
|