@onairos/react-native 3.0.24 → 3.0.26

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 (37) hide show
  1. package/lib/commonjs/components/OnairosButton.js +2 -3
  2. package/lib/commonjs/components/OnairosButton.js.map +1 -1
  3. package/lib/commonjs/components/TrainingModal.js +120 -16
  4. package/lib/commonjs/components/TrainingModal.js.map +1 -1
  5. package/lib/commonjs/components/UniversalOnboarding.js +196 -94
  6. package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
  7. package/lib/commonjs/components/onboarding/OAuthWebView.js +138 -27
  8. package/lib/commonjs/components/onboarding/OAuthWebView.js.map +1 -1
  9. package/lib/commonjs/constants/index.js +3 -3
  10. package/lib/commonjs/constants/index.js.map +1 -1
  11. package/lib/commonjs/services/platformAuthService.js +95 -44
  12. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  13. package/lib/module/components/OnairosButton.js +2 -3
  14. package/lib/module/components/OnairosButton.js.map +1 -1
  15. package/lib/module/components/TrainingModal.js +120 -17
  16. package/lib/module/components/TrainingModal.js.map +1 -1
  17. package/lib/module/components/UniversalOnboarding.js +197 -95
  18. package/lib/module/components/UniversalOnboarding.js.map +1 -1
  19. package/lib/module/components/onboarding/OAuthWebView.js +139 -28
  20. package/lib/module/components/onboarding/OAuthWebView.js.map +1 -1
  21. package/lib/module/constants/index.js +3 -3
  22. package/lib/module/constants/index.js.map +1 -1
  23. package/lib/module/services/platformAuthService.js +92 -42
  24. package/lib/module/services/platformAuthService.js.map +1 -1
  25. package/lib/typescript/components/OnairosButton.d.ts.map +1 -1
  26. package/lib/typescript/components/TrainingModal.d.ts.map +1 -1
  27. package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
  28. package/lib/typescript/components/onboarding/OAuthWebView.d.ts.map +1 -1
  29. package/lib/typescript/services/platformAuthService.d.ts +11 -4
  30. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/components/OnairosButton.tsx +1 -4
  33. package/src/components/TrainingModal.tsx +202 -61
  34. package/src/components/UniversalOnboarding.tsx +188 -83
  35. package/src/components/onboarding/OAuthWebView.tsx +135 -25
  36. package/src/constants/index.ts +3 -3
  37. package/src/services/platformAuthService.ts +111 -40
@@ -15,6 +15,7 @@ import {
15
15
  Image,
16
16
  Switch,
17
17
  Linking,
18
+ Alert,
18
19
  } from 'react-native';
19
20
  import Icon from 'react-native-vector-icons/MaterialIcons';
20
21
  import { PlatformList } from './PlatformList';
@@ -52,7 +53,9 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
52
53
  const [platformToggles, setPlatformToggles] = useState<{[key: string]: boolean}>({});
53
54
  const [oauthUrl, setOauthUrl] = useState<string>('');
54
55
  const [currentPlatform, setCurrentPlatform] = useState<string>('');
55
- const [username, setUsername] = useState<string>(`user_${Math.floor(Math.random() * 10000)}`);
56
+ const [userEmail, setUserEmail] = useState<string>('user@example.com'); // Use proper email instead of random username
57
+ const [isConnectingPlatform, setIsConnectingPlatform] = useState<boolean>(false);
58
+ const [connectingPlatform, setConnectingPlatform] = useState<string | null>(null);
56
59
 
57
60
  const platforms = [
58
61
  { id: 'instagram', name: 'Instagram', icon: require('../assets/images/instagram.png') },
@@ -155,63 +158,86 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
155
158
  }, [getConnectionStatus]);
156
159
 
157
160
  const togglePlatform = useCallback(async (platformId: string) => {
158
- // If toggling on, initiate the OAuth flow for the platform
159
- if (!platformToggles[platformId]) {
160
- try {
161
- // Special case for Instagram which uses the existing flow
162
- if (platformId === 'instagram') {
161
+ // Check if already connected or connecting
162
+ if (connections[platformId]?.connected) {
163
+ // Disconnect platform
164
+ console.log(`Disconnecting ${platformId}`);
165
+ setPlatformToggles(prev => ({
166
+ ...prev,
167
+ [platformId]: false
168
+ }));
169
+
170
+ // Update connections state
171
+ setConnections(prev => {
172
+ const newConnections = { ...prev };
173
+ delete newConnections[platformId];
174
+ return newConnections;
175
+ });
176
+ return;
177
+ }
178
+
179
+ if (connectingPlatform === platformId) {
180
+ console.log(`Already connecting to ${platformId}, ignoring...`);
181
+ return;
182
+ }
183
+
184
+ // Attempt to connect platform
185
+ try {
186
+ setIsConnectingPlatform(true);
187
+ setConnectingPlatform(platformId);
188
+ console.log(`[OAuth] Initiating connection for ${platformId} with email: ${userEmail}`);
189
+
190
+ // Check if platform has a native SDK
191
+ if (hasNativeSDK(platformId)) {
192
+ console.log(`[OAuth] Using native SDK for ${platformId}`);
193
+ // Use native SDK for authentication
194
+ const success = await initiateNativeAuth(platformId);
195
+ if (success) {
196
+ console.log(`[OAuth] Native authentication successful for ${platformId}`);
197
+ // Update platform toggle state
163
198
  setPlatformToggles(prev => ({
164
199
  ...prev,
165
- [platformId]: !prev[platformId]
200
+ [platformId]: true
166
201
  }));
167
- return;
168
- }
169
-
170
- // Check if the platform has a native SDK
171
- if (hasNativeSDK(platformId)) {
172
- // Use native SDK authentication
173
- setCurrentPlatform(platformId);
174
- const success = await initiateNativeAuth(platformId);
175
202
 
176
- if (success) {
177
- // Update connections state
178
- setConnections(prev => ({
179
- ...prev,
180
- [platformId]: { userName: username, connected: true }
181
- }));
182
-
183
- // Update platform toggles
184
- setPlatformToggles(prev => ({
185
- ...prev,
186
- [platformId]: true
187
- }));
188
- }
189
- return;
203
+ // Update connections state
204
+ setConnections(prev => ({
205
+ ...prev,
206
+ [platformId]: { userName: userEmail, connected: true }
207
+ }));
190
208
  }
209
+ } else {
210
+ console.log(`[OAuth] Initiating OAuth flow for ${platformId}`);
211
+ console.log(`[OAuth] Making request to: https://api2.onairos.uk/${platformId}/authorize`);
191
212
 
192
- // For other platforms, use the web OAuth flow
193
- setCurrentPlatform(platformId);
194
- const oauthUrl = await initiateOAuth(platformId, username);
195
-
196
- // If oauthUrl is null, it means we should use native SDK (which should have been caught above)
213
+ // Use OAuth flow through proxy server
214
+ const oauthUrl = await initiateOAuth(platformId, userEmail);
197
215
  if (oauthUrl) {
216
+ console.log(`[OAuth] Received OAuth URL for ${platformId}: ${oauthUrl}`);
217
+ setCurrentPlatform(platformId);
198
218
  setOauthUrl(oauthUrl);
199
219
  setStep('oauth');
220
+ } else {
221
+ console.error(`[OAuth] No OAuth URL returned for ${platformId}`);
222
+ throw new Error(`No OAuth URL returned for ${platformId}`);
200
223
  }
201
- } catch (error) {
202
- console.error(`Error initiating OAuth for ${platformId}:`, error);
203
- // If there's an error, don't toggle the platform
204
- return;
205
224
  }
206
- } else {
207
- // If toggling off, just update the state
225
+ } catch (error) {
226
+ console.error(`[OAuth] Error connecting ${platformId}:`, error);
227
+ // Reset toggle state on error
208
228
  setPlatformToggles(prev => ({
209
229
  ...prev,
210
- [platformId]: !prev[platformId]
230
+ [platformId]: false
211
231
  }));
232
+
233
+ // Show error to user
234
+ Alert.alert('Connection Error', `Failed to connect to ${platformId}. Please try again.`);
235
+ } finally {
236
+ setIsConnectingPlatform(false);
237
+ setConnectingPlatform(null);
212
238
  }
213
- }, [platformToggles, username]);
214
-
239
+ }, [platformToggles, userEmail, connections, connectingPlatform]);
240
+
215
241
  /**
216
242
  * Handles OAuth callback URLs
217
243
  */
@@ -226,7 +252,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
226
252
  // Update connections state
227
253
  setConnections(prev => ({
228
254
  ...prev,
229
- [platform]: { userName: username, connected: true }
255
+ [platform]: { userName: userEmail, connected: true }
230
256
  }));
231
257
 
232
258
  // Update platform toggles
@@ -241,18 +267,21 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
241
267
  } catch (error) {
242
268
  console.error('Error handling OAuth callback:', error);
243
269
  }
244
- }, [currentPlatform, username]);
270
+ }, [currentPlatform, userEmail]);
245
271
 
246
272
  /**
247
273
  * Handles completion of the OAuth flow
248
274
  */
249
275
  const handleOAuthSuccess = useCallback((code: string) => {
250
- if (currentPlatform) {
251
- // Update connections state
252
- setConnections(prev => ({
253
- ...prev,
254
- [currentPlatform]: { userName: username, connected: true }
255
- }));
276
+ console.log(`OAuth success for ${currentPlatform} with code: ${code}`);
277
+
278
+ // Update connections for the current platform
279
+ if (currentPlatform) {
280
+ // Update connections state
281
+ setConnections(prev => ({
282
+ ...prev,
283
+ [currentPlatform]: { userName: userEmail, connected: true }
284
+ }));
256
285
 
257
286
  // Update platform toggles
258
287
  setPlatformToggles(prev => ({
@@ -260,33 +289,37 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
260
289
  [currentPlatform]: true
261
290
  }));
262
291
 
263
- // Return to the connect step
264
- setStep('connect');
292
+ // In a real implementation, we would send the code to our server
293
+ // to exchange it for an access token and store it securely
294
+ console.log(`Simulating token exchange for ${currentPlatform}`);
295
+
296
+ // For demo purposes, we'll just simulate a successful token exchange
297
+ if (debug || test) {
298
+ console.log('Debug mode: Simulating successful token exchange');
299
+ }
265
300
  }
266
- }, [currentPlatform, username]);
301
+
302
+ // Close OAuth window and return to connect step
303
+ setOauthUrl('');
304
+ setStep('connect');
305
+ }, [currentPlatform, userEmail, debug, test]);
267
306
 
268
307
  const handlePinSubmit = useCallback(async (userPin: string) => {
269
308
  setPin(userPin);
270
309
  setStep('training');
271
- // Simulate training progress
310
+ // Simulate training progress over 10 seconds
272
311
  let progress = 0;
273
312
  const interval = setInterval(() => {
274
- progress += 0.1;
313
+ progress += 1; // 10% progress every second for 1 seconds total
275
314
  setTraining({
276
315
  progress,
277
- eta: `${Math.round((1 - progress) * 100)} seconds remaining`,
316
+ eta: `${Math.round((1 - progress) * 10)} seconds remaining`,
278
317
  });
279
318
  if (progress >= 1) {
280
319
  clearInterval(interval);
281
- onComplete('https://api2.onairos.uk', 'dummy-token', {
282
- pin: userPin,
283
- connections,
284
- platformToggles,
285
- selectedTier,
286
- tierData: requestData?.[selectedTier],
287
- });
320
+ // Training complete - TrainingModal will handle email input and final completion
288
321
  }
289
- }, 1000);
322
+ }, 1000); // 1 second intervals for 10 total seconds
290
323
  }, [connections, onComplete, selectedTier, requestData, platformToggles]);
291
324
 
292
325
  const canProceedToPin = useCallback(() => {
@@ -339,13 +372,20 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
339
372
  <Text style={styles.onairosIconText}>O</Text>
340
373
  </View>
341
374
  </View>
375
+ <Text style={styles.headerDescription}>
376
+ Generate insights from your data to send to {AppName}
377
+ </Text>
342
378
  </View>
343
379
 
344
- <ScrollView style={styles.content}>
380
+ <ScrollView
381
+ style={styles.content}
382
+ contentContainerStyle={styles.contentContainer}
383
+ showsVerticalScrollIndicator={false}
384
+ >
345
385
  {/* Main title and description */}
346
386
  <View style={styles.titleContainer}>
347
387
  <Text style={styles.mainTitle}>
348
- Connect your data to create a Persona of you, to connect to Cosmos
388
+ Connect your data to create a Persona of you
349
389
  </Text>
350
390
  <Text style={styles.privacyMessage}>
351
391
  None of your app data is shared with ANYONE
@@ -355,23 +395,41 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
355
395
  {/* Platform connection options */}
356
396
  <View style={styles.platformsContainer}>
357
397
  {platforms.map((platform) => (
358
- <View key={platform.id} style={styles.platformItem}>
398
+ <View key={platform.id} style={[
399
+ styles.platformItem,
400
+ connections[platform.id]?.connected && styles.platformItemConnected
401
+ ]}>
359
402
  <View style={styles.platformInfo}>
360
403
  <Image
361
404
  source={platform.icon}
362
405
  style={styles.platformIcon}
363
406
  resizeMode="contain"
364
407
  />
365
- <Text style={styles.platformName}>
366
- {platform.name}
367
- </Text>
408
+ <View style={styles.platformTextContainer}>
409
+ <Text style={styles.platformName}>
410
+ {platform.name}
411
+ </Text>
412
+ {connections[platform.id]?.connected && (
413
+ <Text style={styles.connectedText}>Connected</Text>
414
+ )}
415
+ {connectingPlatform === platform.id && (
416
+ <Text style={styles.connectingText}>Connecting...</Text>
417
+ )}
418
+ </View>
419
+ </View>
420
+ <View style={styles.platformActions}>
421
+ {connectingPlatform === platform.id ? (
422
+ <ActivityIndicator size="small" color={COLORS.primary} />
423
+ ) : (
424
+ <Switch
425
+ value={platformToggles[platform.id] || connections[platform.id]?.connected || false}
426
+ onValueChange={() => togglePlatform(platform.id)}
427
+ trackColor={{ false: '#767577', true: '#4CAF50' }}
428
+ thumbColor={platformToggles[platform.id] || connections[platform.id]?.connected ? '#4CAF50' : '#f4f3f4'}
429
+ disabled={connectingPlatform !== null}
430
+ />
431
+ )}
368
432
  </View>
369
- <Switch
370
- value={platformToggles[platform.id]}
371
- onValueChange={() => togglePlatform(platform.id)}
372
- trackColor={{ false: '#767577', true: '#81b0ff' }}
373
- thumbColor={platformToggles[platform.id] ? '#2196F3' : '#f4f3f4'}
374
- />
375
433
  </View>
376
434
  ))}
377
435
  </View>
@@ -422,10 +480,11 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
422
480
  platformToggles,
423
481
  selectedTier,
424
482
  tierData: requestData?.[selectedTier],
483
+ userEmail,
425
484
  });
426
485
  }}
427
486
  modelKey="onairosTrainingModel"
428
- username={username}
487
+ username={userEmail}
429
488
  />
430
489
  )}
431
490
 
@@ -460,7 +519,7 @@ const styles = StyleSheet.create({
460
519
  bottomSheet: {
461
520
  backgroundColor: '#fff',
462
521
  width: width,
463
- height: height * 0.6,
522
+ height: height * 0.85,
464
523
  borderTopLeftRadius: 24,
465
524
  borderTopRightRadius: 24,
466
525
  overflow: 'hidden',
@@ -482,8 +541,12 @@ const styles = StyleSheet.create({
482
541
  backgroundColor: '#E0E0E0',
483
542
  },
484
543
  header: {
485
- padding: 24,
544
+ paddingHorizontal: 24,
545
+ paddingTop: 16,
546
+ paddingBottom: 8,
486
547
  alignItems: 'center',
548
+ borderBottomWidth: 1,
549
+ borderBottomColor: '#F0F0F0',
487
550
  },
488
551
  headerContent: {
489
552
  flexDirection: 'row',
@@ -518,6 +581,13 @@ const styles = StyleSheet.create({
518
581
  fontSize: 24,
519
582
  color: '#000',
520
583
  },
584
+ headerDescription: {
585
+ fontSize: 14,
586
+ color: '#666',
587
+ textAlign: 'center',
588
+ marginTop: 12,
589
+ paddingHorizontal: 16,
590
+ },
521
591
  titleContainer: {
522
592
  marginBottom: 30,
523
593
  },
@@ -536,7 +606,10 @@ const styles = StyleSheet.create({
536
606
  },
537
607
  content: {
538
608
  flex: 1,
609
+ },
610
+ contentContainer: {
539
611
  paddingHorizontal: 24,
612
+ paddingBottom: 20,
540
613
  },
541
614
  platformsContainer: {
542
615
  marginTop: 16,
@@ -547,10 +620,18 @@ const styles = StyleSheet.create({
547
620
  alignItems: 'center',
548
621
  padding: 16,
549
622
  backgroundColor: '#fff',
550
- borderRadius: 16,
551
- marginBottom: 16,
623
+ borderRadius: 12,
624
+ marginBottom: 12,
552
625
  borderWidth: 1,
553
- borderColor: '#eee',
626
+ borderColor: '#E0E0E0',
627
+ shadowColor: '#000',
628
+ shadowOffset: {
629
+ width: 0,
630
+ height: 1,
631
+ },
632
+ shadowOpacity: 0.05,
633
+ shadowRadius: 2,
634
+ elevation: 1,
554
635
  },
555
636
  platformInfo: {
556
637
  flexDirection: 'row',
@@ -566,6 +647,30 @@ const styles = StyleSheet.create({
566
647
  fontWeight: '500',
567
648
  color: '#000',
568
649
  },
650
+ platformTextContainer: {
651
+ flex: 1,
652
+ },
653
+ connectedText: {
654
+ fontSize: 12,
655
+ color: '#4CAF50',
656
+ fontWeight: '500',
657
+ marginTop: 2,
658
+ },
659
+ connectingText: {
660
+ fontSize: 12,
661
+ color: '#FF9800',
662
+ fontWeight: '500',
663
+ marginTop: 2,
664
+ },
665
+ platformActions: {
666
+ alignItems: 'center',
667
+ justifyContent: 'center',
668
+ },
669
+ platformItemConnected: {
670
+ backgroundColor: '#F8F9FA',
671
+ borderColor: '#4CAF50',
672
+ borderWidth: 1,
673
+ },
569
674
  footer: {
570
675
  flexDirection: 'row',
571
676
  alignItems: 'center',
@@ -1,8 +1,9 @@
1
1
  import React, { useCallback, useState } from 'react';
2
- import { View, StyleSheet, ActivityIndicator, SafeAreaView, TouchableOpacity } from 'react-native';
2
+ import { View, StyleSheet, ActivityIndicator, SafeAreaView, TouchableOpacity, Text } from 'react-native';
3
3
  import { WebView, WebViewNavigation } from 'react-native-webview';
4
4
  import Icon from 'react-native-vector-icons/MaterialIcons';
5
5
  import { COLORS } from '../../constants';
6
+ import { isOAuthSuccess, handleOAuthCallback } from '../../services/platformAuthService';
6
7
  import type { OAuthWebViewProps } from '../../types';
7
8
 
8
9
  export const OAuthWebView: React.FC<OAuthWebViewProps> = ({
@@ -13,41 +14,83 @@ export const OAuthWebView: React.FC<OAuthWebViewProps> = ({
13
14
  onSuccess,
14
15
  }) => {
15
16
  const [loading, setLoading] = useState(true);
17
+ const [currentUrl, setCurrentUrl] = useState(url);
16
18
 
17
19
  const handleNavigationStateChange = useCallback(
18
20
  (navState: WebViewNavigation) => {
19
- // Check if the URL includes our redirect URI
20
- if (navState.url.startsWith('onairosanime://auth/')) {
21
- // Extract the authorization code from the URL
22
- const authCode = extractAuthCode(navState.url);
23
- if (authCode) {
24
- onSuccess(authCode);
21
+ console.log(`[OAuthWebView] Navigation state changed for ${platform}:`, navState.url);
22
+ setCurrentUrl(navState.url);
23
+
24
+ // Check for final redirect to onairos.uk/Home (indicates proxy has processed the callback)
25
+ if (navState.url.includes('onairos.uk/Home')) {
26
+ console.log(`[OAuthWebView] Detected final redirect to onairos.uk/Home for ${platform}`);
27
+
28
+ // Add a small delay to ensure the redirect is fully processed
29
+ setTimeout(() => {
30
+ onSuccess('success');
31
+ if (onComplete) {
32
+ onComplete();
33
+ }
34
+ }, 500);
35
+ return;
36
+ }
37
+
38
+ // Check for platform-specific success patterns using the service
39
+ if (platform && isOAuthSuccess(navState.url, platform)) {
40
+ console.log(`[OAuthWebView] Detected platform-specific success pattern for ${platform}`);
41
+
42
+ // For YouTube/Google, wait for the final redirect
43
+ if (platform === 'youtube') {
44
+ console.log(`[OAuthWebView] YouTube success detected, waiting for final redirect...`);
45
+ // Don't close immediately for YouTube, wait for the onairos.uk/Home redirect
46
+ return;
25
47
  }
26
48
 
27
- // OAuth flow is complete
28
- if (onComplete) {
29
- onComplete();
49
+ // For other platforms, close immediately
50
+ setTimeout(() => {
51
+ onSuccess('success');
52
+ if (onComplete) {
53
+ onComplete();
54
+ }
55
+ }, 300);
56
+ return;
57
+ }
58
+
59
+ // Check for callback URLs with authorization codes
60
+ if (navState.url.includes('/callback') || navState.url.includes('code=')) {
61
+ console.log(`[OAuthWebView] Detected callback URL for ${platform}:`, navState.url);
62
+
63
+ // Extract the authorization code
64
+ const authCode = handleOAuthCallback(navState.url);
65
+ if (authCode) {
66
+ console.log(`[OAuthWebView] Extracted auth code for ${platform}:`, authCode);
67
+ onSuccess(authCode);
68
+ if (onComplete) {
69
+ onComplete();
70
+ }
30
71
  }
31
72
  return;
32
73
  }
33
74
  },
34
- [onComplete, onSuccess]
75
+ [platform, onComplete, onSuccess]
35
76
  );
36
77
 
78
+ const handleLoadStart = useCallback(() => {
79
+ setLoading(true);
80
+ }, []);
81
+
37
82
  const handleLoadEnd = useCallback(() => {
38
83
  setLoading(false);
39
84
  }, []);
40
85
 
41
- // Extract auth code from redirect URL
42
- const extractAuthCode = (redirectUrl: string): string => {
43
- try {
44
- const url = new URL(redirectUrl);
45
- return url.searchParams.get('code') || '';
46
- } catch (error) {
47
- console.error('Error extracting auth code:', error);
48
- return '';
49
- }
50
- };
86
+ const handleError = useCallback((syntheticEvent: any) => {
87
+ const { nativeEvent } = syntheticEvent;
88
+ console.error(`[OAuthWebView] WebView error for ${platform}:`, nativeEvent);
89
+ setLoading(false);
90
+ }, [platform]);
91
+
92
+ // Get iOS Safari user agent for better compatibility
93
+ const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1';
51
94
 
52
95
  return (
53
96
  <SafeAreaView style={styles.container}>
@@ -61,21 +104,56 @@ export const OAuthWebView: React.FC<OAuthWebViewProps> = ({
61
104
  size={20}
62
105
  color={getPlatformColor(platform || 'default')}
63
106
  />
107
+ <Text style={styles.platformTitle}>
108
+ Connect {platform ? platform.charAt(0).toUpperCase() + platform.slice(1) : 'Account'}
109
+ </Text>
64
110
  </View>
111
+ <View style={styles.placeholder} />
65
112
  </View>
66
113
 
67
114
  <WebView
68
115
  source={{ uri: url }}
69
116
  onNavigationStateChange={handleNavigationStateChange}
117
+ onLoadStart={handleLoadStart}
70
118
  onLoadEnd={handleLoadEnd}
119
+ onError={handleError}
71
120
  startInLoadingState={true}
72
121
  renderLoading={() => <View />}
73
122
  style={styles.webView}
123
+ // Essential WebView configuration for OAuth
124
+ javaScriptEnabled={true}
125
+ domStorageEnabled={true}
126
+ cacheEnabled={true}
127
+ incognito={false} // Allow session persistence
128
+ sharedCookiesEnabled={true} // Critical for Google/YouTube OAuth
129
+ thirdPartyCookiesEnabled={true} // Required for OAuth flows
130
+ userAgent={userAgent} // iOS Safari for better compatibility
131
+ // Additional settings for better OAuth support
132
+ allowsInlineMediaPlayback={true}
133
+ mediaPlaybackRequiresUserAction={false}
134
+ allowsBackForwardNavigationGestures={true}
135
+ // Security settings
136
+ allowsLinkPreview={false}
137
+ allowFileAccess={false}
138
+ allowUniversalAccessFromFileURLs={false}
139
+ mixedContentMode="compatibility"
74
140
  />
75
141
 
76
142
  {loading && (
77
143
  <View style={styles.loadingContainer}>
78
144
  <ActivityIndicator size="large" color={COLORS.primary} />
145
+ <Text style={styles.loadingText}>
146
+ Connecting to {platform ? platform.charAt(0).toUpperCase() + platform.slice(1) : 'platform'}...
147
+ </Text>
148
+ </View>
149
+ )}
150
+
151
+ {/* Debug info in development */}
152
+ {__DEV__ && (
153
+ <View style={styles.debugContainer}>
154
+ <Text style={styles.debugText} numberOfLines={1}>
155
+ {currentUrl}
156
+ </Text>
79
157
  </View>
80
158
  )}
81
159
  </SafeAreaView>
@@ -95,6 +173,8 @@ const getPlatformIcon = (platform: string): string => {
95
173
  return 'push-pin';
96
174
  case 'reddit':
97
175
  return 'forum';
176
+ case 'email':
177
+ return 'email';
98
178
  default:
99
179
  return 'link';
100
180
  }
@@ -113,6 +193,8 @@ const getPlatformColor = (platform: string): string => {
113
193
  return '#E60023';
114
194
  case 'reddit':
115
195
  return '#FF4500';
196
+ case 'email':
197
+ return '#4285F4';
116
198
  default:
117
199
  return COLORS.primary;
118
200
  }
@@ -134,21 +216,49 @@ const styles = StyleSheet.create({
134
216
  },
135
217
  titleContainer: {
136
218
  flex: 1,
219
+ flexDirection: 'row',
137
220
  alignItems: 'center',
221
+ justifyContent: 'center',
222
+ },
223
+ platformTitle: {
224
+ fontSize: 16,
225
+ fontWeight: '600',
226
+ color: '#000',
227
+ marginLeft: 8,
138
228
  },
139
229
  closeButton: {
140
230
  padding: 8,
141
- position: 'absolute',
142
- left: 16,
143
- zIndex: 10,
231
+ width: 40,
232
+ },
233
+ placeholder: {
234
+ width: 40,
144
235
  },
145
236
  webView: {
146
237
  flex: 1,
147
238
  },
148
239
  loadingContainer: {
149
240
  ...StyleSheet.absoluteFillObject,
150
- backgroundColor: 'rgba(255, 255, 255, 0.8)',
241
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
151
242
  alignItems: 'center',
152
243
  justifyContent: 'center',
153
244
  },
245
+ loadingText: {
246
+ fontSize: 16,
247
+ color: '#666',
248
+ marginTop: 16,
249
+ textAlign: 'center',
250
+ },
251
+ debugContainer: {
252
+ position: 'absolute',
253
+ bottom: 0,
254
+ left: 0,
255
+ right: 0,
256
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
257
+ padding: 8,
258
+ },
259
+ debugText: {
260
+ color: '#fff',
261
+ fontSize: 12,
262
+ fontFamily: 'monospace',
263
+ },
154
264
  });
@@ -77,7 +77,7 @@ export const PIN_REQUIREMENTS = {
77
77
  };
78
78
 
79
79
  export const DEEP_LINK_CONFIG = {
80
- scheme: 'onairosanime',
81
- host: 'authenticate',
82
- redirectUri: 'onairosanime://auth/',
80
+ scheme: 'onairosevents',
81
+ host: 'auth',
82
+ redirectUri: 'onairosevents://auth/callback',
83
83
  };