@onairos/react-native 3.1.16 → 3.1.17

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 (198) hide show
  1. package/README.md +404 -0
  2. package/lib/commonjs/assets/images/Checkbox.svg +3 -3
  3. package/lib/commonjs/assets/images/EnochE.svg +19 -19
  4. package/lib/commonjs/assets/images/Personalityprofile.svg +3 -3
  5. package/lib/commonjs/assets/images/Personalitytraits.svg +3 -3
  6. package/lib/commonjs/assets/images/Userpreferences.svg +3 -3
  7. package/lib/commonjs/assets/images/arrow.svg +20 -20
  8. package/lib/commonjs/assets/images/basicproficon.svg +43 -43
  9. package/lib/commonjs/assets/images/basicprofile.svg +3 -3
  10. package/lib/commonjs/assets/images/checkmark.svg +4 -4
  11. package/lib/commonjs/assets/images/contentanalysis.svg +3 -3
  12. package/lib/commonjs/assets/images/contenticon.svg +23 -23
  13. package/lib/commonjs/assets/images/personalityicon.svg +18 -18
  14. package/lib/commonjs/assets/images/x-close.svg +3 -3
  15. package/lib/commonjs/components/OnairosButton.js +290 -0
  16. package/lib/commonjs/components/OnairosButton.js.map +1 -0
  17. package/lib/commonjs/components/OnairosSignInButton.js +30 -8
  18. package/lib/commonjs/components/OnairosSignInButton.js.map +1 -1
  19. package/lib/commonjs/components/UniversalOnboarding.js +4 -4
  20. package/lib/commonjs/config/api.js +2 -2
  21. package/lib/commonjs/hooks/useConnections.js +6 -6
  22. package/lib/commonjs/hooks/useUserConnections.js +10 -10
  23. package/lib/commonjs/index.js +9 -10
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/services/apiClient.js +35 -35
  26. package/lib/commonjs/services/apiKeyService.js +99 -99
  27. package/lib/commonjs/services/authService.js +82 -82
  28. package/lib/commonjs/services/biometricPinService.js +10 -10
  29. package/lib/commonjs/services/connectedAccountsService.js +32 -32
  30. package/lib/commonjs/services/googleAuthService.js +15 -15
  31. package/lib/commonjs/services/imageCompressionService.js +15 -15
  32. package/lib/commonjs/services/jwtStorageService.js +59 -59
  33. package/lib/commonjs/services/mobileTrainingService.js +14 -14
  34. package/lib/commonjs/services/pinEncryptionService.js +10 -10
  35. package/lib/commonjs/services/pinStorageUtils.js +15 -15
  36. package/lib/commonjs/services/platformAuthService.js +47 -47
  37. package/lib/commonjs/services/storageService.js +31 -31
  38. package/lib/commonjs/services/trainingApiHelpers.js +33 -33
  39. package/lib/commonjs/services/userConnectionsService.js +24 -24
  40. package/lib/commonjs/utils/Portal.js +4 -4
  41. package/lib/commonjs/utils/api.js +24 -24
  42. package/lib/commonjs/utils/auth.js +18 -18
  43. package/lib/commonjs/utils/crypto.js +13 -13
  44. package/lib/commonjs/utils/encryption.js +12 -12
  45. package/lib/commonjs/utils/eventUtils.js +52 -52
  46. package/lib/commonjs/utils/programmaticFlow.js +16 -16
  47. package/lib/commonjs/utils/retryHelper.js +27 -27
  48. package/lib/module/assets/images/Checkbox.svg +3 -3
  49. package/lib/module/assets/images/EnochE.svg +19 -19
  50. package/lib/module/assets/images/Personalityprofile.svg +3 -3
  51. package/lib/module/assets/images/Personalitytraits.svg +3 -3
  52. package/lib/module/assets/images/Userpreferences.svg +3 -3
  53. package/lib/module/assets/images/arrow.svg +20 -20
  54. package/lib/module/assets/images/basicproficon.svg +43 -43
  55. package/lib/module/assets/images/basicprofile.svg +3 -3
  56. package/lib/module/assets/images/checkmark.svg +4 -4
  57. package/lib/module/assets/images/contentanalysis.svg +3 -3
  58. package/lib/module/assets/images/contenticon.svg +23 -23
  59. package/lib/module/assets/images/personalityicon.svg +18 -18
  60. package/lib/module/assets/images/x-close.svg +3 -3
  61. package/lib/module/components/OnairosButton.js +282 -0
  62. package/lib/module/components/OnairosButton.js.map +1 -0
  63. package/lib/module/components/OnairosSignInButton.js +30 -8
  64. package/lib/module/components/OnairosSignInButton.js.map +1 -1
  65. package/lib/module/components/UniversalOnboarding.js +4 -4
  66. package/lib/module/config/api.js +2 -2
  67. package/lib/module/hooks/useConnections.js +6 -6
  68. package/lib/module/hooks/useUserConnections.js +10 -10
  69. package/lib/module/index.js +8 -10
  70. package/lib/module/index.js.map +1 -1
  71. package/lib/module/services/apiClient.js +35 -35
  72. package/lib/module/services/apiKeyService.js +99 -99
  73. package/lib/module/services/authService.js +82 -82
  74. package/lib/module/services/biometricPinService.js +10 -10
  75. package/lib/module/services/connectedAccountsService.js +32 -32
  76. package/lib/module/services/googleAuthService.js +15 -15
  77. package/lib/module/services/imageCompressionService.js +15 -15
  78. package/lib/module/services/jwtStorageService.js +59 -59
  79. package/lib/module/services/mobileTrainingService.js +14 -14
  80. package/lib/module/services/pinEncryptionService.js +10 -10
  81. package/lib/module/services/pinStorageUtils.js +15 -15
  82. package/lib/module/services/platformAuthService.js +47 -47
  83. package/lib/module/services/storageService.js +31 -31
  84. package/lib/module/services/trainingApiHelpers.js +33 -33
  85. package/lib/module/services/userConnectionsService.js +24 -24
  86. package/lib/module/utils/Portal.js +4 -4
  87. package/lib/module/utils/api.js +24 -24
  88. package/lib/module/utils/auth.js +18 -18
  89. package/lib/module/utils/crypto.js +13 -13
  90. package/lib/module/utils/encryption.js +12 -12
  91. package/lib/module/utils/eventUtils.js +52 -52
  92. package/lib/module/utils/programmaticFlow.js +16 -16
  93. package/lib/module/utils/retryHelper.js +27 -27
  94. package/lib/typescript/components/OnairosButton.d.ts +37 -0
  95. package/lib/typescript/components/OnairosButton.d.ts.map +1 -0
  96. package/lib/typescript/components/OnairosSignInButton.d.ts +2 -1
  97. package/lib/typescript/components/OnairosSignInButton.d.ts.map +1 -1
  98. package/lib/typescript/index.d.ts +3 -4
  99. package/lib/typescript/index.d.ts.map +1 -1
  100. package/package.json +163 -163
  101. package/src/api/index.ts +151 -151
  102. package/src/assets/images/Checkbox.svg +3 -3
  103. package/src/assets/images/EnochE.svg +19 -19
  104. package/src/assets/images/Personalityprofile.svg +3 -3
  105. package/src/assets/images/Personalitytraits.svg +3 -3
  106. package/src/assets/images/Userpreferences.svg +3 -3
  107. package/src/assets/images/arrow.svg +20 -20
  108. package/src/assets/images/basicproficon.svg +43 -43
  109. package/src/assets/images/basicprofile.svg +3 -3
  110. package/src/assets/images/checkmark.svg +4 -4
  111. package/src/assets/images/contentanalysis.svg +3 -3
  112. package/src/assets/images/contenticon.svg +23 -23
  113. package/src/assets/images/personalityicon.svg +18 -18
  114. package/src/assets/images/x-close.svg +3 -3
  115. package/src/components/BodyText.tsx +33 -33
  116. package/src/components/BrandMark.tsx +62 -62
  117. package/src/components/CodeInput.tsx +32 -32
  118. package/src/components/DataRequestScreen.tsx +355 -355
  119. package/src/components/EmailInput.tsx +31 -31
  120. package/src/components/EmailVerificationModal.tsx +363 -363
  121. package/src/components/ExistingUserDataConfirmation.tsx +506 -506
  122. package/src/components/GoogleButton.tsx +55 -55
  123. package/src/components/HeadingGroup.tsx +49 -49
  124. package/src/components/ModalHeader.tsx +125 -125
  125. package/src/components/ModalSheet.tsx +57 -57
  126. package/src/components/Onairos.tsx +422 -422
  127. package/src/components/OnairosButton.tsx +339 -0
  128. package/src/components/OnairosSignInButton.tsx +30 -10
  129. package/src/components/Overlay.tsx +506 -506
  130. package/src/components/PersonaImage.tsx +79 -79
  131. package/src/components/PersonaLoadingScreen.tsx +201 -201
  132. package/src/components/PersonalizationConsentScreen.tsx +410 -410
  133. package/src/components/PinCreationScreen.tsx +492 -492
  134. package/src/components/PinInput.tsx +555 -555
  135. package/src/components/PlatformConnectorsStep.tsx +891 -891
  136. package/src/components/PlatformList.tsx +144 -144
  137. package/src/components/PlatformToggle.tsx +226 -226
  138. package/src/components/PrimaryButton.tsx +213 -213
  139. package/src/components/SignInMatchAnimation.tsx +225 -225
  140. package/src/components/SignInStep.tsx +217 -217
  141. package/src/components/TrainingModal.tsx +1047 -1047
  142. package/src/components/UniversalOnboarding.tsx +2887 -2887
  143. package/src/components/VerificationStep.tsx +198 -198
  144. package/src/components/WelcomeScreen.tsx +473 -473
  145. package/src/components/icons/Basicproficon.tsx +30 -30
  146. package/src/components/icons/Basicprofile.tsx +17 -17
  147. package/src/components/icons/Checkbox.tsx +17 -17
  148. package/src/components/icons/Checkmark.tsx +24 -24
  149. package/src/components/icons/Contentanalysis.tsx +17 -17
  150. package/src/components/icons/Contenticon.tsx +30 -30
  151. package/src/components/icons/EnochE.tsx +39 -39
  152. package/src/components/icons/Personalityicon.tsx +22 -22
  153. package/src/components/icons/Personalityprofile.tsx +17 -17
  154. package/src/components/icons/Personalitytraits.tsx +17 -17
  155. package/src/components/icons/Userpreferences.tsx +17 -17
  156. package/src/components/icons/index.ts +12 -12
  157. package/src/components/onboarding/OAuthWebView.tsx +232 -232
  158. package/src/config/api.ts +25 -25
  159. package/src/context/AuthContext.tsx +393 -393
  160. package/src/hooks/useConnectedAccounts.ts +138 -138
  161. package/src/hooks/useConnections.ts +161 -161
  162. package/src/hooks/useCredentials.ts +174 -174
  163. package/src/hooks/useUserConnections.ts +165 -165
  164. package/src/index.js +14 -0
  165. package/src/index.ts +94 -96
  166. package/src/services/apiClient.ts +336 -336
  167. package/src/services/apiKeyService.ts +919 -919
  168. package/src/services/authService.ts +1008 -1008
  169. package/src/services/biometricPinService.ts +192 -192
  170. package/src/services/connectedAccountsService.ts +289 -289
  171. package/src/services/googleAuthService.ts +279 -279
  172. package/src/services/imageCompressionService.ts +302 -302
  173. package/src/services/jwtStorageService.ts +256 -256
  174. package/src/services/mobileTrainingService.ts +203 -203
  175. package/src/services/pinEncryptionService.ts +75 -75
  176. package/src/services/pinStorageUtils.ts +96 -96
  177. package/src/services/platformAuthService.ts +1346 -1346
  178. package/src/services/storageService.ts +451 -451
  179. package/src/services/trainingApiHelpers.ts +66 -66
  180. package/src/services/userConnectionsService.ts +556 -556
  181. package/src/services/youtubeMigrationService.ts +453 -453
  182. package/src/theme/index.ts +239 -239
  183. package/src/types/ambient.d.ts +28 -28
  184. package/src/types/index.ts +265 -265
  185. package/src/types/node-fix.d.ts +18 -18
  186. package/src/types/node-override.d.ts +23 -23
  187. package/src/types/opacity.d.ts +15 -15
  188. package/src/types/types.d.ts +17 -17
  189. package/src/utils/Portal.tsx +82 -82
  190. package/src/utils/api.js +111 -111
  191. package/src/utils/auth.js +103 -103
  192. package/src/utils/crypto.js +59 -59
  193. package/src/utils/encryption.ts +68 -68
  194. package/src/utils/eventUtils.ts +302 -302
  195. package/src/utils/haptics.ts +58 -58
  196. package/src/utils/imagePreloader.ts +2 -2
  197. package/src/utils/programmaticFlow.ts +112 -112
  198. package/src/utils/retryHelper.ts +274 -274
@@ -1,454 +1,454 @@
1
- import { GoogleSignin } from '@react-native-google-signin/google-signin';
2
- import { Alert } from 'react-native';
3
- import AsyncStorage from '@react-native-async-storage/async-storage';
4
-
5
- // 🔑 CRITICAL: Using the same client ID for both web and iOS to avoid audience errors
6
- const WEB_CLIENT_ID = '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com';
7
- const IOS_CLIENT_ID = '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com';
8
-
9
- // Configuration for Google Sign-In with refresh token support
10
- const configureGoogleSignInForRefreshTokens = () => {
11
- GoogleSignin.configure({
12
- webClientId: WEB_CLIENT_ID, // ✅ CRITICAL: Web client ID for refresh tokens
13
- iosClientId: IOS_CLIENT_ID, // ✅ iOS client ID for native auth
14
-
15
- // 🔑 CRITICAL: These are the missing parameters that fix the refresh token issue
16
- offlineAccess: true, // ← Enables refresh tokens
17
- forceCodeForRefreshToken: true, // ← Forces refresh token generation
18
-
19
- // ✅ Enhanced scopes for YouTube
20
- scopes: [
21
- 'https://www.googleapis.com/auth/youtube.readonly',
22
- 'openid',
23
- 'profile',
24
- 'email'
25
- ],
26
-
27
- // 🚀 Additional settings (helps with refresh tokens)
28
- hostedDomain: '',
29
- accountName: ''
30
- });
31
- };
32
-
33
- // Check if user needs YouTube migration
34
- export const checkYouTubeMigrationNeeded = async (username: string): Promise<boolean> => {
35
- try {
36
- const response = await fetch(`https://api2.onairos.uk/youtube/validate-connection/${username}`);
37
- const result = await response.json();
38
-
39
- if (result.success && result.validation) {
40
- // ✅ NEW: Check if this is temporary mode (matches exact backend response fields)
41
- const isTemporaryMode = result.validation?.isTemporaryMode === true ||
42
- result.temporaryMode?.enabled === true ||
43
- result.isTemporaryMode === true ||
44
- (result.message && result.message.includes('temporary access token mode'));
45
-
46
- const needsReconnection = result.validation.needsReconnection;
47
- const hasRefreshToken = result.validation.hasRefreshToken;
48
-
49
- console.log('🔍 YouTube migration check:', {
50
- username,
51
- needsReconnection,
52
- hasRefreshToken,
53
- isTemporaryMode,
54
- temporaryModeEnabled: result.temporaryMode?.enabled,
55
- message: result.message
56
- });
57
-
58
- if (isTemporaryMode) {
59
- console.log('🔄 User using temporary mode - connection working correctly');
60
- console.log('⚠️ No refresh capability but training can proceed');
61
- return false; // Don't flag temporary mode as needing migration
62
- } else if (needsReconnection) {
63
- console.log('🔧 User needs YouTube migration for refresh token');
64
- return true; // Only flag genuinely old connections
65
- } else {
66
- console.log('✅ YouTube connection fully working with refresh tokens');
67
- return false; // Connection is perfect
68
- }
69
- }
70
-
71
- return false;
72
- } catch (error) {
73
- console.error('❌ Error checking YouTube migration status:', error);
74
- return false;
75
- }
76
- };
77
-
78
- // ✅ NEW: Enhanced function to get detailed YouTube connection status
79
- export const getYouTubeConnectionStatus = async (username: string): Promise<{
80
- isReady: boolean;
81
- mode: 'full' | 'temporary' | 'limited' | 'none';
82
- needsMigration: boolean;
83
- trainingReady: boolean;
84
- details: any;
85
- }> => {
86
- try {
87
- const response = await fetch(`https://api2.onairos.uk/youtube/validate-connection/${username}`);
88
- const result = await response.json();
89
-
90
- console.log('📋 Complete YouTube status response:', result);
91
-
92
- if (result.success && result.validation) {
93
- // Check for temporary mode (prioritize exact backend fields)
94
- const isTemporaryMode = result.validation?.isTemporaryMode === true ||
95
- result.temporaryMode?.enabled === true ||
96
- result.isTemporaryMode === true ||
97
- (result.message && result.message.includes('temporary access token mode'));
98
-
99
- const hasRefreshToken = result.validation.hasRefreshToken === true;
100
- const needsReconnection = result.validation.needsReconnection === true;
101
-
102
- if (isTemporaryMode) {
103
- console.log('🔄 YouTube connected in temporary mode');
104
- console.log('✅ Training will work, but connection expires in ~1 hour');
105
- return {
106
- isReady: true,
107
- mode: 'temporary',
108
- needsMigration: false,
109
- trainingReady: true,
110
- details: result
111
- };
112
- } else if (hasRefreshToken) {
113
- console.log('✅ YouTube connected with refresh tokens');
114
- return {
115
- isReady: true,
116
- mode: 'full',
117
- needsMigration: false,
118
- trainingReady: true,
119
- details: result
120
- };
121
- } else if (needsReconnection) {
122
- console.log('⚠️ YouTube connected but needs migration');
123
- return {
124
- isReady: false,
125
- mode: 'limited',
126
- needsMigration: true,
127
- trainingReady: false,
128
- details: result
129
- };
130
- } else {
131
- console.log('❓ YouTube connection status unclear');
132
- return {
133
- isReady: false,
134
- mode: 'none',
135
- needsMigration: false,
136
- trainingReady: false,
137
- details: result
138
- };
139
- }
140
- }
141
-
142
- console.log('❌ No YouTube connection found');
143
- return {
144
- isReady: false,
145
- mode: 'none',
146
- needsMigration: false,
147
- trainingReady: false,
148
- details: result
149
- };
150
- } catch (error) {
151
- console.error('❌ Error getting YouTube connection status:', error);
152
- return {
153
- isReady: false,
154
- mode: 'none',
155
- needsMigration: false,
156
- trainingReady: false,
157
- details: { error: error instanceof Error ? error.message : String(error) }
158
- };
159
- }
160
- };
161
-
162
- // Show user-friendly migration prompt
163
- const showYouTubeMigrationPrompt = async (): Promise<boolean> => {
164
- return new Promise((resolve) => {
165
- Alert.alert(
166
- 'YouTube Connection Upgrade',
167
- 'To improve your training experience, we need to upgrade your YouTube connection. This prevents interruptions during data collection.\n\n✅ One-time upgrade\n✅ Takes 30 seconds\n✅ No data loss\n\nUpgrade now?',
168
- [
169
- {
170
- text: 'Skip for Now',
171
- style: 'cancel',
172
- onPress: () => resolve(false)
173
- },
174
- {
175
- text: 'Upgrade Now',
176
- style: 'default',
177
- onPress: () => resolve(true)
178
- }
179
- ]
180
- );
181
- });
182
- };
183
-
184
- // Force YouTube reconnection with consent screen
185
- const forceYouTubeReconnection = async (username: string): Promise<boolean> => {
186
- try {
187
- console.log('🔄 Starting YouTube migration for user:', username);
188
-
189
- // 1. Configure for refresh tokens
190
- configureGoogleSignInForRefreshTokens();
191
-
192
- // 2. Complete sign out to clear cached consent
193
- await GoogleSignin.signOut();
194
- console.log('✅ Signed out - consent cache cleared');
195
-
196
- // 3. Clear cached tokens if available
197
- try {
198
- const existingTokens = await GoogleSignin.getTokens();
199
- if (existingTokens.accessToken) {
200
- await GoogleSignin.clearCachedAccessToken(existingTokens.accessToken);
201
- console.log('✅ Token cache cleared');
202
- }
203
- } catch (clearError) {
204
- console.log('ℹ️ No token cache to clear');
205
- }
206
-
207
- // 4. Check Google Play Services
208
- await GoogleSignin.hasPlayServices();
209
-
210
- // 5. Sign in (this will show consent screen)
211
- console.log('🔐 Initiating sign-in - consent screen should appear...');
212
- const userInfo = await GoogleSignin.signIn();
213
-
214
- // 6. Get tokens after consent
215
- const tokens = await GoogleSignin.getTokens();
216
- const currentUser = await GoogleSignin.getCurrentUser();
217
-
218
- // 7. Analyze tokens thoroughly (serverAuthCode is the refresh token mechanism in React Native)
219
- let finalRefreshToken = null;
220
- let refreshTokenSource = 'None';
221
-
222
- // Check for server auth code from sign-in response (primary method)
223
- if ((userInfo as any).serverAuthCode) {
224
- finalRefreshToken = (userInfo as any).serverAuthCode;
225
- refreshTokenSource = 'Server auth code from sign-in';
226
- }
227
- // Check current user server auth code (backup method)
228
- else if (currentUser?.serverAuthCode) {
229
- finalRefreshToken = currentUser.serverAuthCode;
230
- refreshTokenSource = 'Current user server auth code';
231
- }
232
-
233
- console.log('🔍 Token Analysis:', {
234
- hasAccessToken: !!tokens.accessToken,
235
- hasIdToken: !!tokens.idToken,
236
- hasServerAuthCode: !!(userInfo as any).serverAuthCode,
237
- hasCurrentUserAuthCode: !!currentUser?.serverAuthCode,
238
- refreshTokenSource: refreshTokenSource,
239
- finalRefreshToken: finalRefreshToken ? 'Available' : 'MISSING!',
240
- userEmail: (userInfo as any).user?.email
241
- });
242
-
243
- if (!finalRefreshToken) {
244
- console.error('❌ No refresh token received - Google Console configuration may be incorrect');
245
- console.error('💡 This usually means:');
246
- console.error(' 1. offlineAccess: true is missing from configuration');
247
- console.error(' 2. forceCodeForRefreshToken: true is missing');
248
- console.error(' 3. User did not grant offline access permission');
249
- return false;
250
- }
251
-
252
- console.log('✅ Got refresh token after consent:', finalRefreshToken.substring(0, 20) + '...');
253
- console.log('🔑 Refresh token source:', refreshTokenSource);
254
-
255
- // 8. Get existing authentication token
256
- let authToken = await AsyncStorage.getItem('onairos_jwt_token') ||
257
- await AsyncStorage.getItem('enoch_token') ||
258
- await AsyncStorage.getItem('auth_token');
259
-
260
- if (!authToken) {
261
- console.warn('⚠️ No authentication token found for YouTube migration');
262
- authToken = 'migration_token_placeholder';
263
- }
264
-
265
- // 9. Send enhanced payload to backend
266
- const backendPayload = {
267
- session: {
268
- username: username,
269
- platform: 'youtube',
270
- channelName: (userInfo as any).user?.name || 'YouTube Channel',
271
- channelId: null
272
- },
273
- googleUser: (userInfo as any).user,
274
- accessToken: tokens.accessToken,
275
- idToken: tokens.idToken,
276
- refreshToken: finalRefreshToken,
277
- serverAuthCode: (userInfo as any).serverAuthCode,
278
- userAccountInfo: {
279
- username: username,
280
- email: (userInfo as any).user?.email,
281
- authToken: authToken,
282
- channelName: (userInfo as any).user?.name || 'YouTube Channel',
283
- channelId: null
284
- }
285
- };
286
-
287
- console.log('📤 Sending migration payload to backend...');
288
-
289
- // 10. Send to backend
290
- const response = await fetch('https://api2.onairos.uk/youtube/native-auth', {
291
- method: 'POST',
292
- headers: {
293
- 'Content-Type': 'application/json',
294
- 'x-api-key': 'your-api-key', // Use your existing API key if needed
295
- ...(authToken !== 'migration_token_placeholder' && { 'Authorization': `Bearer ${authToken}` })
296
- },
297
- body: JSON.stringify(backendPayload)
298
- });
299
-
300
- const result = await response.json();
301
-
302
- if (result.success) {
303
- console.log('✅ YouTube migration successful');
304
-
305
- // Track migration success
306
- await trackMigrationAttempt(username, true);
307
-
308
- return true;
309
- } else {
310
- console.error('❌ Backend rejected YouTube migration:', result.error);
311
- await trackMigrationAttempt(username, false, result.error);
312
- return false;
313
- }
314
-
315
- } catch (error) {
316
- console.error('❌ YouTube migration failed:', error);
317
- await trackMigrationAttempt(username, false, error instanceof Error ? error.message : 'Unknown error');
318
- return false;
319
- }
320
- };
321
-
322
- // Track migration attempts for monitoring
323
- const trackMigrationAttempt = async (username: string, success: boolean, error?: string) => {
324
- try {
325
- await fetch('https://api2.onairos.uk/youtube/migration-status', {
326
- method: 'POST',
327
- headers: {
328
- 'Content-Type': 'application/json',
329
- },
330
- body: JSON.stringify({
331
- username,
332
- success,
333
- error: error || null,
334
- timestamp: new Date().toISOString()
335
- })
336
- });
337
- } catch (trackError) {
338
- console.error('❌ Failed to track migration attempt:', trackError);
339
- }
340
- };
341
-
342
- // ✅ NEW: Explicit function to check if user should see migration warnings
343
- export const shouldShowYouTubeMigrationWarning = async (username: string): Promise<boolean> => {
344
- try {
345
- console.log('🔍 [MIGRATION WARNING] Checking if user should see migration warning:', username);
346
-
347
- const response = await fetch(`https://api2.onairos.uk/youtube/validate-connection/${username}`);
348
- const result = await response.json();
349
-
350
- if (result.success && result.validation) {
351
- // Check for temporary mode (prioritize exact backend fields)
352
- const isTemporaryMode = result.validation?.isTemporaryMode === true ||
353
- result.temporaryMode?.enabled === true ||
354
- result.isTemporaryMode === true;
355
-
356
- const hasAccessToken = result.validation.hasAccessToken === true;
357
- const hasRefreshToken = result.validation.hasRefreshToken === true;
358
- const needsReconnection = result.validation.needsReconnection === true;
359
-
360
- console.log('📊 [MIGRATION WARNING] Status check:', {
361
- username,
362
- hasAccessToken,
363
- hasRefreshToken,
364
- needsReconnection,
365
- isTemporaryMode,
366
- connectedAt: result.validation.connectedAt
367
- });
368
-
369
- if (isTemporaryMode) {
370
- console.log('🚫 [MIGRATION WARNING] NO WARNING - user is in temporary mode (working correctly)');
371
- return false; // Don't show migration warning
372
- } else if (hasAccessToken && !hasRefreshToken && needsReconnection) {
373
- console.log('⚠️ [MIGRATION WARNING] SHOW WARNING - genuinely old connection needs update');
374
- return true; // Show migration warning for genuinely broken connections
375
- } else {
376
- console.log('✅ [MIGRATION WARNING] NO WARNING - connection is working properly');
377
- return false; // Don't show warning for working connections
378
- }
379
- }
380
-
381
- console.log('ℹ️ [MIGRATION WARNING] NO WARNING - no YouTube connection found');
382
- return false; // Don't show warning if no connection
383
- } catch (error) {
384
- console.error('❌ [MIGRATION WARNING] Error checking migration warning status:', error);
385
- return false; // Don't show warning on error
386
- }
387
- };
388
-
389
- // Main function to check and fix YouTube connection
390
- export const checkAndFixYouTubeConnection = async (username: string): Promise<boolean> => {
391
- try {
392
- console.log('🔄 Checking YouTube connection for user:', username);
393
-
394
- // 1. First check if user should see migration warning at all
395
- const shouldShowWarning = await shouldShowYouTubeMigrationWarning(username);
396
-
397
- if (!shouldShowWarning) {
398
- console.log('🚫 [MIGRATION] NO MIGRATION NEEDED - user connection is working correctly');
399
- console.log('✅ [MIGRATION] User can proceed with training normally');
400
- return true; // Return success - no migration needed
401
- }
402
-
403
- // 2. Double-check migration is actually needed (for safety)
404
- const needsMigration = await checkYouTubeMigrationNeeded(username);
405
-
406
- if (!needsMigration) {
407
- console.log('✅ [MIGRATION] YouTube connection working properly - no migration needed');
408
- return true;
409
- }
410
-
411
- console.log('🔧 [MIGRATION] User needs YouTube migration for refresh token (genuinely old connection)');
412
-
413
- // 2. Show user-friendly prompt
414
- const userConfirmed = await showYouTubeMigrationPrompt();
415
-
416
- if (!userConfirmed) {
417
- console.log('ℹ️ User declined YouTube migration');
418
- return false;
419
- }
420
-
421
- // 3. Attempt migration
422
- const migrationSuccess = await forceYouTubeReconnection(username);
423
-
424
- if (migrationSuccess) {
425
- // Show success message
426
- Alert.alert(
427
- 'YouTube Upgraded! ✅',
428
- 'Your YouTube connection has been upgraded successfully. Training will now work seamlessly without interruptions.',
429
- [{ text: 'Great!', style: 'default' }]
430
- );
431
-
432
- return true;
433
- } else {
434
- // Show failure message
435
- Alert.alert(
436
- 'Upgrade Failed ❌',
437
- 'YouTube connection upgrade failed. Please try again later or contact support if the problem persists.',
438
- [{ text: 'OK', style: 'default' }]
439
- );
440
-
441
- return false;
442
- }
443
-
444
- } catch (error) {
445
- console.error('❌ Error in YouTube connection check:', error);
446
- return false;
447
- }
448
- };
449
-
450
- // Optional: Reconnect function for manual use
451
- export const reconnectYouTube = async (username: string): Promise<boolean> => {
452
- console.log('🔄 Manual YouTube reconnection requested for:', username);
453
- return await forceYouTubeReconnection(username);
1
+ import { GoogleSignin } from '@react-native-google-signin/google-signin';
2
+ import { Alert } from 'react-native';
3
+ import AsyncStorage from '@react-native-async-storage/async-storage';
4
+
5
+ // 🔑 CRITICAL: Using the same client ID for both web and iOS to avoid audience errors
6
+ const WEB_CLIENT_ID = '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com';
7
+ const IOS_CLIENT_ID = '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com';
8
+
9
+ // Configuration for Google Sign-In with refresh token support
10
+ const configureGoogleSignInForRefreshTokens = () => {
11
+ GoogleSignin.configure({
12
+ webClientId: WEB_CLIENT_ID, // ✅ CRITICAL: Web client ID for refresh tokens
13
+ iosClientId: IOS_CLIENT_ID, // ✅ iOS client ID for native auth
14
+
15
+ // 🔑 CRITICAL: These are the missing parameters that fix the refresh token issue
16
+ offlineAccess: true, // ← Enables refresh tokens
17
+ forceCodeForRefreshToken: true, // ← Forces refresh token generation
18
+
19
+ // ✅ Enhanced scopes for YouTube
20
+ scopes: [
21
+ 'https://www.googleapis.com/auth/youtube.readonly',
22
+ 'openid',
23
+ 'profile',
24
+ 'email'
25
+ ],
26
+
27
+ // 🚀 Additional settings (helps with refresh tokens)
28
+ hostedDomain: '',
29
+ accountName: ''
30
+ });
31
+ };
32
+
33
+ // Check if user needs YouTube migration
34
+ export const checkYouTubeMigrationNeeded = async (username: string): Promise<boolean> => {
35
+ try {
36
+ const response = await fetch(`https://api2.onairos.uk/youtube/validate-connection/${username}`);
37
+ const result = await response.json();
38
+
39
+ if (result.success && result.validation) {
40
+ // ✅ NEW: Check if this is temporary mode (matches exact backend response fields)
41
+ const isTemporaryMode = result.validation?.isTemporaryMode === true ||
42
+ result.temporaryMode?.enabled === true ||
43
+ result.isTemporaryMode === true ||
44
+ (result.message && result.message.includes('temporary access token mode'));
45
+
46
+ const needsReconnection = result.validation.needsReconnection;
47
+ const hasRefreshToken = result.validation.hasRefreshToken;
48
+
49
+ console.log('🔍 YouTube migration check:', {
50
+ username,
51
+ needsReconnection,
52
+ hasRefreshToken,
53
+ isTemporaryMode,
54
+ temporaryModeEnabled: result.temporaryMode?.enabled,
55
+ message: result.message
56
+ });
57
+
58
+ if (isTemporaryMode) {
59
+ console.log('🔄 User using temporary mode - connection working correctly');
60
+ console.log('⚠️ No refresh capability but training can proceed');
61
+ return false; // Don't flag temporary mode as needing migration
62
+ } else if (needsReconnection) {
63
+ console.log('🔧 User needs YouTube migration for refresh token');
64
+ return true; // Only flag genuinely old connections
65
+ } else {
66
+ console.log('✅ YouTube connection fully working with refresh tokens');
67
+ return false; // Connection is perfect
68
+ }
69
+ }
70
+
71
+ return false;
72
+ } catch (error) {
73
+ console.error('❌ Error checking YouTube migration status:', error);
74
+ return false;
75
+ }
76
+ };
77
+
78
+ // ✅ NEW: Enhanced function to get detailed YouTube connection status
79
+ export const getYouTubeConnectionStatus = async (username: string): Promise<{
80
+ isReady: boolean;
81
+ mode: 'full' | 'temporary' | 'limited' | 'none';
82
+ needsMigration: boolean;
83
+ trainingReady: boolean;
84
+ details: any;
85
+ }> => {
86
+ try {
87
+ const response = await fetch(`https://api2.onairos.uk/youtube/validate-connection/${username}`);
88
+ const result = await response.json();
89
+
90
+ console.log('📋 Complete YouTube status response:', result);
91
+
92
+ if (result.success && result.validation) {
93
+ // Check for temporary mode (prioritize exact backend fields)
94
+ const isTemporaryMode = result.validation?.isTemporaryMode === true ||
95
+ result.temporaryMode?.enabled === true ||
96
+ result.isTemporaryMode === true ||
97
+ (result.message && result.message.includes('temporary access token mode'));
98
+
99
+ const hasRefreshToken = result.validation.hasRefreshToken === true;
100
+ const needsReconnection = result.validation.needsReconnection === true;
101
+
102
+ if (isTemporaryMode) {
103
+ console.log('🔄 YouTube connected in temporary mode');
104
+ console.log('✅ Training will work, but connection expires in ~1 hour');
105
+ return {
106
+ isReady: true,
107
+ mode: 'temporary',
108
+ needsMigration: false,
109
+ trainingReady: true,
110
+ details: result
111
+ };
112
+ } else if (hasRefreshToken) {
113
+ console.log('✅ YouTube connected with refresh tokens');
114
+ return {
115
+ isReady: true,
116
+ mode: 'full',
117
+ needsMigration: false,
118
+ trainingReady: true,
119
+ details: result
120
+ };
121
+ } else if (needsReconnection) {
122
+ console.log('⚠️ YouTube connected but needs migration');
123
+ return {
124
+ isReady: false,
125
+ mode: 'limited',
126
+ needsMigration: true,
127
+ trainingReady: false,
128
+ details: result
129
+ };
130
+ } else {
131
+ console.log('❓ YouTube connection status unclear');
132
+ return {
133
+ isReady: false,
134
+ mode: 'none',
135
+ needsMigration: false,
136
+ trainingReady: false,
137
+ details: result
138
+ };
139
+ }
140
+ }
141
+
142
+ console.log('❌ No YouTube connection found');
143
+ return {
144
+ isReady: false,
145
+ mode: 'none',
146
+ needsMigration: false,
147
+ trainingReady: false,
148
+ details: result
149
+ };
150
+ } catch (error) {
151
+ console.error('❌ Error getting YouTube connection status:', error);
152
+ return {
153
+ isReady: false,
154
+ mode: 'none',
155
+ needsMigration: false,
156
+ trainingReady: false,
157
+ details: { error: error instanceof Error ? error.message : String(error) }
158
+ };
159
+ }
160
+ };
161
+
162
+ // Show user-friendly migration prompt
163
+ const showYouTubeMigrationPrompt = async (): Promise<boolean> => {
164
+ return new Promise((resolve) => {
165
+ Alert.alert(
166
+ 'YouTube Connection Upgrade',
167
+ 'To improve your training experience, we need to upgrade your YouTube connection. This prevents interruptions during data collection.\n\n✅ One-time upgrade\n✅ Takes 30 seconds\n✅ No data loss\n\nUpgrade now?',
168
+ [
169
+ {
170
+ text: 'Skip for Now',
171
+ style: 'cancel',
172
+ onPress: () => resolve(false)
173
+ },
174
+ {
175
+ text: 'Upgrade Now',
176
+ style: 'default',
177
+ onPress: () => resolve(true)
178
+ }
179
+ ]
180
+ );
181
+ });
182
+ };
183
+
184
+ // Force YouTube reconnection with consent screen
185
+ const forceYouTubeReconnection = async (username: string): Promise<boolean> => {
186
+ try {
187
+ console.log('🔄 Starting YouTube migration for user:', username);
188
+
189
+ // 1. Configure for refresh tokens
190
+ configureGoogleSignInForRefreshTokens();
191
+
192
+ // 2. Complete sign out to clear cached consent
193
+ await GoogleSignin.signOut();
194
+ console.log('✅ Signed out - consent cache cleared');
195
+
196
+ // 3. Clear cached tokens if available
197
+ try {
198
+ const existingTokens = await GoogleSignin.getTokens();
199
+ if (existingTokens.accessToken) {
200
+ await GoogleSignin.clearCachedAccessToken(existingTokens.accessToken);
201
+ console.log('✅ Token cache cleared');
202
+ }
203
+ } catch (clearError) {
204
+ console.log('ℹ️ No token cache to clear');
205
+ }
206
+
207
+ // 4. Check Google Play Services
208
+ await GoogleSignin.hasPlayServices();
209
+
210
+ // 5. Sign in (this will show consent screen)
211
+ console.log('🔐 Initiating sign-in - consent screen should appear...');
212
+ const userInfo = await GoogleSignin.signIn();
213
+
214
+ // 6. Get tokens after consent
215
+ const tokens = await GoogleSignin.getTokens();
216
+ const currentUser = await GoogleSignin.getCurrentUser();
217
+
218
+ // 7. Analyze tokens thoroughly (serverAuthCode is the refresh token mechanism in React Native)
219
+ let finalRefreshToken = null;
220
+ let refreshTokenSource = 'None';
221
+
222
+ // Check for server auth code from sign-in response (primary method)
223
+ if ((userInfo as any).serverAuthCode) {
224
+ finalRefreshToken = (userInfo as any).serverAuthCode;
225
+ refreshTokenSource = 'Server auth code from sign-in';
226
+ }
227
+ // Check current user server auth code (backup method)
228
+ else if (currentUser?.serverAuthCode) {
229
+ finalRefreshToken = currentUser.serverAuthCode;
230
+ refreshTokenSource = 'Current user server auth code';
231
+ }
232
+
233
+ console.log('🔍 Token Analysis:', {
234
+ hasAccessToken: !!tokens.accessToken,
235
+ hasIdToken: !!tokens.idToken,
236
+ hasServerAuthCode: !!(userInfo as any).serverAuthCode,
237
+ hasCurrentUserAuthCode: !!currentUser?.serverAuthCode,
238
+ refreshTokenSource: refreshTokenSource,
239
+ finalRefreshToken: finalRefreshToken ? 'Available' : 'MISSING!',
240
+ userEmail: (userInfo as any).user?.email
241
+ });
242
+
243
+ if (!finalRefreshToken) {
244
+ console.error('❌ No refresh token received - Google Console configuration may be incorrect');
245
+ console.error('💡 This usually means:');
246
+ console.error(' 1. offlineAccess: true is missing from configuration');
247
+ console.error(' 2. forceCodeForRefreshToken: true is missing');
248
+ console.error(' 3. User did not grant offline access permission');
249
+ return false;
250
+ }
251
+
252
+ console.log('✅ Got refresh token after consent:', finalRefreshToken.substring(0, 20) + '...');
253
+ console.log('🔑 Refresh token source:', refreshTokenSource);
254
+
255
+ // 8. Get existing authentication token
256
+ let authToken = await AsyncStorage.getItem('onairos_jwt_token') ||
257
+ await AsyncStorage.getItem('enoch_token') ||
258
+ await AsyncStorage.getItem('auth_token');
259
+
260
+ if (!authToken) {
261
+ console.warn('⚠️ No authentication token found for YouTube migration');
262
+ authToken = 'migration_token_placeholder';
263
+ }
264
+
265
+ // 9. Send enhanced payload to backend
266
+ const backendPayload = {
267
+ session: {
268
+ username: username,
269
+ platform: 'youtube',
270
+ channelName: (userInfo as any).user?.name || 'YouTube Channel',
271
+ channelId: null
272
+ },
273
+ googleUser: (userInfo as any).user,
274
+ accessToken: tokens.accessToken,
275
+ idToken: tokens.idToken,
276
+ refreshToken: finalRefreshToken,
277
+ serverAuthCode: (userInfo as any).serverAuthCode,
278
+ userAccountInfo: {
279
+ username: username,
280
+ email: (userInfo as any).user?.email,
281
+ authToken: authToken,
282
+ channelName: (userInfo as any).user?.name || 'YouTube Channel',
283
+ channelId: null
284
+ }
285
+ };
286
+
287
+ console.log('📤 Sending migration payload to backend...');
288
+
289
+ // 10. Send to backend
290
+ const response = await fetch('https://api2.onairos.uk/youtube/native-auth', {
291
+ method: 'POST',
292
+ headers: {
293
+ 'Content-Type': 'application/json',
294
+ 'x-api-key': 'your-api-key', // Use your existing API key if needed
295
+ ...(authToken !== 'migration_token_placeholder' && { 'Authorization': `Bearer ${authToken}` })
296
+ },
297
+ body: JSON.stringify(backendPayload)
298
+ });
299
+
300
+ const result = await response.json();
301
+
302
+ if (result.success) {
303
+ console.log('✅ YouTube migration successful');
304
+
305
+ // Track migration success
306
+ await trackMigrationAttempt(username, true);
307
+
308
+ return true;
309
+ } else {
310
+ console.error('❌ Backend rejected YouTube migration:', result.error);
311
+ await trackMigrationAttempt(username, false, result.error);
312
+ return false;
313
+ }
314
+
315
+ } catch (error) {
316
+ console.error('❌ YouTube migration failed:', error);
317
+ await trackMigrationAttempt(username, false, error instanceof Error ? error.message : 'Unknown error');
318
+ return false;
319
+ }
320
+ };
321
+
322
+ // Track migration attempts for monitoring
323
+ const trackMigrationAttempt = async (username: string, success: boolean, error?: string) => {
324
+ try {
325
+ await fetch('https://api2.onairos.uk/youtube/migration-status', {
326
+ method: 'POST',
327
+ headers: {
328
+ 'Content-Type': 'application/json',
329
+ },
330
+ body: JSON.stringify({
331
+ username,
332
+ success,
333
+ error: error || null,
334
+ timestamp: new Date().toISOString()
335
+ })
336
+ });
337
+ } catch (trackError) {
338
+ console.error('❌ Failed to track migration attempt:', trackError);
339
+ }
340
+ };
341
+
342
+ // ✅ NEW: Explicit function to check if user should see migration warnings
343
+ export const shouldShowYouTubeMigrationWarning = async (username: string): Promise<boolean> => {
344
+ try {
345
+ console.log('🔍 [MIGRATION WARNING] Checking if user should see migration warning:', username);
346
+
347
+ const response = await fetch(`https://api2.onairos.uk/youtube/validate-connection/${username}`);
348
+ const result = await response.json();
349
+
350
+ if (result.success && result.validation) {
351
+ // Check for temporary mode (prioritize exact backend fields)
352
+ const isTemporaryMode = result.validation?.isTemporaryMode === true ||
353
+ result.temporaryMode?.enabled === true ||
354
+ result.isTemporaryMode === true;
355
+
356
+ const hasAccessToken = result.validation.hasAccessToken === true;
357
+ const hasRefreshToken = result.validation.hasRefreshToken === true;
358
+ const needsReconnection = result.validation.needsReconnection === true;
359
+
360
+ console.log('📊 [MIGRATION WARNING] Status check:', {
361
+ username,
362
+ hasAccessToken,
363
+ hasRefreshToken,
364
+ needsReconnection,
365
+ isTemporaryMode,
366
+ connectedAt: result.validation.connectedAt
367
+ });
368
+
369
+ if (isTemporaryMode) {
370
+ console.log('🚫 [MIGRATION WARNING] NO WARNING - user is in temporary mode (working correctly)');
371
+ return false; // Don't show migration warning
372
+ } else if (hasAccessToken && !hasRefreshToken && needsReconnection) {
373
+ console.log('⚠️ [MIGRATION WARNING] SHOW WARNING - genuinely old connection needs update');
374
+ return true; // Show migration warning for genuinely broken connections
375
+ } else {
376
+ console.log('✅ [MIGRATION WARNING] NO WARNING - connection is working properly');
377
+ return false; // Don't show warning for working connections
378
+ }
379
+ }
380
+
381
+ console.log('ℹ️ [MIGRATION WARNING] NO WARNING - no YouTube connection found');
382
+ return false; // Don't show warning if no connection
383
+ } catch (error) {
384
+ console.error('❌ [MIGRATION WARNING] Error checking migration warning status:', error);
385
+ return false; // Don't show warning on error
386
+ }
387
+ };
388
+
389
+ // Main function to check and fix YouTube connection
390
+ export const checkAndFixYouTubeConnection = async (username: string): Promise<boolean> => {
391
+ try {
392
+ console.log('🔄 Checking YouTube connection for user:', username);
393
+
394
+ // 1. First check if user should see migration warning at all
395
+ const shouldShowWarning = await shouldShowYouTubeMigrationWarning(username);
396
+
397
+ if (!shouldShowWarning) {
398
+ console.log('🚫 [MIGRATION] NO MIGRATION NEEDED - user connection is working correctly');
399
+ console.log('✅ [MIGRATION] User can proceed with training normally');
400
+ return true; // Return success - no migration needed
401
+ }
402
+
403
+ // 2. Double-check migration is actually needed (for safety)
404
+ const needsMigration = await checkYouTubeMigrationNeeded(username);
405
+
406
+ if (!needsMigration) {
407
+ console.log('✅ [MIGRATION] YouTube connection working properly - no migration needed');
408
+ return true;
409
+ }
410
+
411
+ console.log('🔧 [MIGRATION] User needs YouTube migration for refresh token (genuinely old connection)');
412
+
413
+ // 2. Show user-friendly prompt
414
+ const userConfirmed = await showYouTubeMigrationPrompt();
415
+
416
+ if (!userConfirmed) {
417
+ console.log('ℹ️ User declined YouTube migration');
418
+ return false;
419
+ }
420
+
421
+ // 3. Attempt migration
422
+ const migrationSuccess = await forceYouTubeReconnection(username);
423
+
424
+ if (migrationSuccess) {
425
+ // Show success message
426
+ Alert.alert(
427
+ 'YouTube Upgraded! ✅',
428
+ 'Your YouTube connection has been upgraded successfully. Training will now work seamlessly without interruptions.',
429
+ [{ text: 'Great!', style: 'default' }]
430
+ );
431
+
432
+ return true;
433
+ } else {
434
+ // Show failure message
435
+ Alert.alert(
436
+ 'Upgrade Failed ❌',
437
+ 'YouTube connection upgrade failed. Please try again later or contact support if the problem persists.',
438
+ [{ text: 'OK', style: 'default' }]
439
+ );
440
+
441
+ return false;
442
+ }
443
+
444
+ } catch (error) {
445
+ console.error('❌ Error in YouTube connection check:', error);
446
+ return false;
447
+ }
448
+ };
449
+
450
+ // Optional: Reconnect function for manual use
451
+ export const reconnectYouTube = async (username: string): Promise<boolean> => {
452
+ console.log('🔄 Manual YouTube reconnection requested for:', username);
453
+ return await forceYouTubeReconnection(username);
454
454
  };