@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.
- package/lib/commonjs/components/OnairosButton.js +2 -3
- package/lib/commonjs/components/OnairosButton.js.map +1 -1
- package/lib/commonjs/components/TrainingModal.js +120 -16
- package/lib/commonjs/components/TrainingModal.js.map +1 -1
- package/lib/commonjs/components/UniversalOnboarding.js +196 -94
- package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
- package/lib/commonjs/components/onboarding/OAuthWebView.js +138 -27
- package/lib/commonjs/components/onboarding/OAuthWebView.js.map +1 -1
- package/lib/commonjs/constants/index.js +3 -3
- package/lib/commonjs/constants/index.js.map +1 -1
- package/lib/commonjs/services/platformAuthService.js +95 -44
- package/lib/commonjs/services/platformAuthService.js.map +1 -1
- package/lib/module/components/OnairosButton.js +2 -3
- package/lib/module/components/OnairosButton.js.map +1 -1
- package/lib/module/components/TrainingModal.js +120 -17
- package/lib/module/components/TrainingModal.js.map +1 -1
- package/lib/module/components/UniversalOnboarding.js +197 -95
- package/lib/module/components/UniversalOnboarding.js.map +1 -1
- package/lib/module/components/onboarding/OAuthWebView.js +139 -28
- package/lib/module/components/onboarding/OAuthWebView.js.map +1 -1
- package/lib/module/constants/index.js +3 -3
- package/lib/module/constants/index.js.map +1 -1
- package/lib/module/services/platformAuthService.js +92 -42
- package/lib/module/services/platformAuthService.js.map +1 -1
- package/lib/typescript/components/OnairosButton.d.ts.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/components/onboarding/OAuthWebView.d.ts.map +1 -1
- package/lib/typescript/services/platformAuthService.d.ts +11 -4
- package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/OnairosButton.tsx +1 -4
- package/src/components/TrainingModal.tsx +202 -61
- package/src/components/UniversalOnboarding.tsx +188 -83
- package/src/components/onboarding/OAuthWebView.tsx +135 -25
- package/src/constants/index.ts +3 -3
- 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 [
|
|
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
|
-
//
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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]:
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
//
|
|
193
|
-
|
|
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
|
-
}
|
|
207
|
-
|
|
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]:
|
|
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,
|
|
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:
|
|
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,
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
//
|
|
264
|
-
|
|
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
|
-
|
|
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 +=
|
|
313
|
+
progress += 1; // 10% progress every second for 1 seconds total
|
|
275
314
|
setTraining({
|
|
276
315
|
progress,
|
|
277
|
-
eta: `${Math.round((1 - progress) *
|
|
316
|
+
eta: `${Math.round((1 - progress) * 10)} seconds remaining`,
|
|
278
317
|
});
|
|
279
318
|
if (progress >= 1) {
|
|
280
319
|
clearInterval(interval);
|
|
281
|
-
|
|
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
|
|
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
|
|
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={
|
|
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
|
-
<
|
|
366
|
-
{
|
|
367
|
-
|
|
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={
|
|
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.
|
|
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
|
-
|
|
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:
|
|
551
|
-
marginBottom:
|
|
623
|
+
borderRadius: 12,
|
|
624
|
+
marginBottom: 12,
|
|
552
625
|
borderWidth: 1,
|
|
553
|
-
borderColor: '#
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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.
|
|
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
|
});
|
package/src/constants/index.ts
CHANGED
|
@@ -77,7 +77,7 @@ export const PIN_REQUIREMENTS = {
|
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
export const DEEP_LINK_CONFIG = {
|
|
80
|
-
scheme: '
|
|
81
|
-
host: '
|
|
82
|
-
redirectUri: '
|
|
80
|
+
scheme: 'onairosevents',
|
|
81
|
+
host: 'auth',
|
|
82
|
+
redirectUri: 'onairosevents://auth/callback',
|
|
83
83
|
};
|