@onairos/react-native 3.0.37 → 3.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/TrainingModal.js +20 -8
- package/lib/commonjs/components/TrainingModal.js.map +1 -1
- package/lib/commonjs/components/UniversalOnboarding.js +187 -55
- package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
- package/lib/commonjs/hooks/useCredentials.js +88 -8
- package/lib/commonjs/hooks/useCredentials.js.map +1 -1
- package/lib/module/components/TrainingModal.js +20 -8
- package/lib/module/components/TrainingModal.js.map +1 -1
- package/lib/module/components/UniversalOnboarding.js +188 -56
- package/lib/module/components/UniversalOnboarding.js.map +1 -1
- package/lib/module/hooks/useCredentials.js +88 -7
- package/lib/module/hooks/useCredentials.js.map +1 -1
- package/lib/typescript/components/TrainingModal.d.ts.map +1 -1
- package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
- package/lib/typescript/hooks/useCredentials.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/TrainingModal.tsx +18 -3
- package/src/components/UniversalOnboarding.tsx +202 -49
- package/src/hooks/useCredentials.ts +83 -8
|
@@ -40,7 +40,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
40
40
|
test = false,
|
|
41
41
|
preferredPlatform,
|
|
42
42
|
}) => {
|
|
43
|
-
const [step, setStep] = useState<'connect' | 'pin' | 'training' | 'oauth'>('connect');
|
|
43
|
+
const [step, setStep] = useState<'connect' | 'pin' | 'training' | 'oauth' | 'success'>('connect');
|
|
44
44
|
const [connections, setConnections] = useState<ConnectionStatus>({});
|
|
45
45
|
const [pin, setPin] = useState<string>('');
|
|
46
46
|
const [selectedTier, setSelectedTier] = useState<'Small' | 'Medium' | 'Large'>('Medium');
|
|
@@ -54,6 +54,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
54
54
|
const [currentPlatform, setCurrentPlatform] = useState<string>('');
|
|
55
55
|
const [username, setUsername] = useState<string>('Avatar');
|
|
56
56
|
const [isConnectingPlatform, setIsConnectingPlatform] = useState<boolean>(false);
|
|
57
|
+
const [showLoginWebView, setShowLoginWebView] = useState<boolean>(false);
|
|
57
58
|
|
|
58
59
|
const platforms = [
|
|
59
60
|
{ id: 'instagram', name: 'Instagram', icon: require('../assets/images/instagram.png') },
|
|
@@ -220,33 +221,11 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
220
221
|
* Handles OAuth callback URLs
|
|
221
222
|
*/
|
|
222
223
|
const handleOAuthCallback = useCallback((url: string) => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (code && platform) {
|
|
230
|
-
// Update connections state
|
|
231
|
-
setConnections(prev => ({
|
|
232
|
-
...prev,
|
|
233
|
-
[platform]: { userName: username, connected: true }
|
|
234
|
-
}));
|
|
235
|
-
|
|
236
|
-
// Update platform toggles
|
|
237
|
-
setPlatformToggles(prev => ({
|
|
238
|
-
...prev,
|
|
239
|
-
[platform]: true
|
|
240
|
-
}));
|
|
241
|
-
|
|
242
|
-
// Return to the connect step
|
|
243
|
-
setStep('connect');
|
|
244
|
-
}
|
|
245
|
-
} catch (error) {
|
|
246
|
-
console.error('Error handling OAuth callback:', error);
|
|
247
|
-
}
|
|
248
|
-
}, [currentPlatform, username]);
|
|
249
|
-
|
|
224
|
+
console.log('OAuth callback received:', url);
|
|
225
|
+
// Handle the OAuth callback here
|
|
226
|
+
// This would typically extract the authorization code and complete the OAuth flow
|
|
227
|
+
}, []);
|
|
228
|
+
|
|
250
229
|
/**
|
|
251
230
|
* Handles completion of the OAuth flow
|
|
252
231
|
*/
|
|
@@ -276,6 +255,28 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
276
255
|
setStep('connect');
|
|
277
256
|
}, [currentPlatform, username]);
|
|
278
257
|
|
|
258
|
+
// Function to handle "Already have an account" button
|
|
259
|
+
const handleAlreadyHaveAccount = useCallback(() => {
|
|
260
|
+
console.log('Already have an account clicked - opening login WebView');
|
|
261
|
+
setShowLoginWebView(true);
|
|
262
|
+
// TODO: Open WebView to check for existing Onairos login details
|
|
263
|
+
}, []);
|
|
264
|
+
|
|
265
|
+
// Function to check for existing account (spoofed for now)
|
|
266
|
+
const checkExistingAccount = useCallback(async () => {
|
|
267
|
+
console.log('Checking for existing account...');
|
|
268
|
+
// TODO: Implement actual logic to check cookies/storage for existing account
|
|
269
|
+
// For now, this is spoofed and doesn't do anything
|
|
270
|
+
return false;
|
|
271
|
+
}, []);
|
|
272
|
+
|
|
273
|
+
// Function to handle login WebView completion
|
|
274
|
+
const handleLoginWebViewComplete = useCallback(() => {
|
|
275
|
+
console.log('Login WebView completed');
|
|
276
|
+
setShowLoginWebView(false);
|
|
277
|
+
// TODO: If login successful, skip onboarding and call onComplete
|
|
278
|
+
}, []);
|
|
279
|
+
|
|
279
280
|
const handlePinSubmit = useCallback(async (userPin: string) => {
|
|
280
281
|
setPin(userPin);
|
|
281
282
|
setStep('training');
|
|
@@ -307,9 +308,14 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
307
308
|
}, [platformToggles]);
|
|
308
309
|
|
|
309
310
|
const handleProceed = () => {
|
|
310
|
-
|
|
311
|
+
console.log('Proceeding to next step');
|
|
312
|
+
// Show success screen first
|
|
313
|
+
setStep('success');
|
|
314
|
+
|
|
315
|
+
// After a delay, proceed to PIN
|
|
316
|
+
setTimeout(() => {
|
|
311
317
|
setStep('pin');
|
|
312
|
-
}
|
|
318
|
+
}, 3000);
|
|
313
319
|
};
|
|
314
320
|
|
|
315
321
|
return (
|
|
@@ -351,6 +357,14 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
351
357
|
<Text style={styles.onairosIconText}>O</Text>
|
|
352
358
|
</View>
|
|
353
359
|
</View>
|
|
360
|
+
|
|
361
|
+
{/* Already have an account button */}
|
|
362
|
+
<TouchableOpacity
|
|
363
|
+
style={styles.alreadyHaveAccountButton}
|
|
364
|
+
onPress={handleAlreadyHaveAccount}
|
|
365
|
+
>
|
|
366
|
+
<Text style={styles.alreadyHaveAccountText}>Already have an account?</Text>
|
|
367
|
+
</TouchableOpacity>
|
|
354
368
|
</View>
|
|
355
369
|
|
|
356
370
|
<ScrollView
|
|
@@ -375,7 +389,12 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
375
389
|
{/* Platform connection options */}
|
|
376
390
|
<View style={styles.platformsContainer}>
|
|
377
391
|
{platforms.map((platform) => (
|
|
378
|
-
<
|
|
392
|
+
<TouchableOpacity
|
|
393
|
+
key={platform.id}
|
|
394
|
+
style={styles.platformItem}
|
|
395
|
+
onPress={() => togglePlatform(platform.id)}
|
|
396
|
+
disabled={isConnectingPlatform}
|
|
397
|
+
>
|
|
379
398
|
<View style={styles.platformInfo}>
|
|
380
399
|
<Image
|
|
381
400
|
source={platform.icon}
|
|
@@ -386,13 +405,20 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
386
405
|
{platform.name}
|
|
387
406
|
</Text>
|
|
388
407
|
</View>
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
408
|
+
|
|
409
|
+
{isConnectingPlatform && currentPlatform === platform.id ? (
|
|
410
|
+
<ActivityIndicator size="small" color={COLORS.primary} />
|
|
411
|
+
) : (
|
|
412
|
+
<View style={[
|
|
413
|
+
styles.platformToggle,
|
|
414
|
+
platformToggles[platform.id] && styles.platformToggleActive
|
|
415
|
+
]}>
|
|
416
|
+
{platformToggles[platform.id] && (
|
|
417
|
+
<Icon name="check" size={16} color="#fff" />
|
|
418
|
+
)}
|
|
419
|
+
</View>
|
|
420
|
+
)}
|
|
421
|
+
</TouchableOpacity>
|
|
396
422
|
))}
|
|
397
423
|
</View>
|
|
398
424
|
</ScrollView>
|
|
@@ -419,6 +445,35 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
419
445
|
</>
|
|
420
446
|
)}
|
|
421
447
|
|
|
448
|
+
{step === 'success' && (
|
|
449
|
+
<View style={styles.successContainer}>
|
|
450
|
+
<View style={styles.successContent}>
|
|
451
|
+
{/* Big green checkmark */}
|
|
452
|
+
<View style={styles.successIcon}>
|
|
453
|
+
<Icon name="check" size={48} color="#fff" />
|
|
454
|
+
</View>
|
|
455
|
+
|
|
456
|
+
<Text style={styles.successTitle}>Never Connect Again!</Text>
|
|
457
|
+
<Text style={styles.successSubtitle}>
|
|
458
|
+
Setup for future use complete
|
|
459
|
+
</Text>
|
|
460
|
+
|
|
461
|
+
<View style={styles.successMessage}>
|
|
462
|
+
<Text style={styles.successMessageText}>
|
|
463
|
+
Your connections are saved securely. Next time you use {AppName},
|
|
464
|
+
you'll be automatically connected.
|
|
465
|
+
</Text>
|
|
466
|
+
</View>
|
|
467
|
+
|
|
468
|
+
{/* Auto-progress indicator */}
|
|
469
|
+
<View style={styles.progressIndicator}>
|
|
470
|
+
<ActivityIndicator size="small" color="#4CAF50" />
|
|
471
|
+
<Text style={styles.progressText}>Continuing...</Text>
|
|
472
|
+
</View>
|
|
473
|
+
</View>
|
|
474
|
+
</View>
|
|
475
|
+
)}
|
|
476
|
+
|
|
422
477
|
{step === 'pin' && (
|
|
423
478
|
<PinInput
|
|
424
479
|
onSubmit={handlePinSubmit}
|
|
@@ -461,6 +516,20 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
|
|
|
461
516
|
onComplete={() => setStep('connect')}
|
|
462
517
|
/>
|
|
463
518
|
)}
|
|
519
|
+
|
|
520
|
+
{/* Login WebView for existing account check */}
|
|
521
|
+
{showLoginWebView && (
|
|
522
|
+
<OAuthWebView
|
|
523
|
+
url="https://onairos.uk/login" // TODO: Replace with actual login URL
|
|
524
|
+
platform="onairos"
|
|
525
|
+
onClose={() => setShowLoginWebView(false)}
|
|
526
|
+
onSuccess={(result) => {
|
|
527
|
+
console.log('Login successful:', result);
|
|
528
|
+
handleLoginWebViewComplete();
|
|
529
|
+
}}
|
|
530
|
+
onComplete={handleLoginWebViewComplete}
|
|
531
|
+
/>
|
|
532
|
+
)}
|
|
464
533
|
</SafeAreaView>
|
|
465
534
|
</Animated.View>
|
|
466
535
|
</TouchableWithoutFeedback>
|
|
@@ -539,20 +608,20 @@ const styles = StyleSheet.create({
|
|
|
539
608
|
color: '#000',
|
|
540
609
|
},
|
|
541
610
|
titleContainer: {
|
|
542
|
-
marginBottom:
|
|
611
|
+
marginBottom: 20,
|
|
543
612
|
},
|
|
544
613
|
mainTitle: {
|
|
545
|
-
fontSize:
|
|
614
|
+
fontSize: 20,
|
|
546
615
|
fontWeight: '600',
|
|
547
616
|
color: '#000',
|
|
548
617
|
textAlign: 'center',
|
|
549
|
-
marginBottom:
|
|
618
|
+
marginBottom: 12,
|
|
550
619
|
},
|
|
551
620
|
privacyMessage: {
|
|
552
621
|
fontSize: 14,
|
|
553
622
|
color: '#666',
|
|
554
623
|
textAlign: 'center',
|
|
555
|
-
marginBottom:
|
|
624
|
+
marginBottom: 12,
|
|
556
625
|
},
|
|
557
626
|
content: {
|
|
558
627
|
flex: 1,
|
|
@@ -560,8 +629,7 @@ const styles = StyleSheet.create({
|
|
|
560
629
|
},
|
|
561
630
|
scrollContent: {
|
|
562
631
|
flexGrow: 1,
|
|
563
|
-
paddingBottom:
|
|
564
|
-
minHeight: '100%',
|
|
632
|
+
paddingBottom: 20,
|
|
565
633
|
},
|
|
566
634
|
platformsContainer: {
|
|
567
635
|
marginTop: 16,
|
|
@@ -570,20 +638,21 @@ const styles = StyleSheet.create({
|
|
|
570
638
|
flexDirection: 'row',
|
|
571
639
|
justifyContent: 'space-between',
|
|
572
640
|
alignItems: 'center',
|
|
573
|
-
padding:
|
|
641
|
+
padding: 12,
|
|
574
642
|
backgroundColor: '#fff',
|
|
575
|
-
borderRadius:
|
|
576
|
-
marginBottom:
|
|
643
|
+
borderRadius: 12,
|
|
644
|
+
marginBottom: 8,
|
|
577
645
|
borderWidth: 1,
|
|
578
646
|
borderColor: '#eee',
|
|
579
647
|
},
|
|
580
648
|
platformInfo: {
|
|
581
649
|
flexDirection: 'row',
|
|
582
650
|
alignItems: 'center',
|
|
651
|
+
flex: 1,
|
|
583
652
|
},
|
|
584
653
|
platformIcon: {
|
|
585
|
-
width:
|
|
586
|
-
height:
|
|
654
|
+
width: 24,
|
|
655
|
+
height: 24,
|
|
587
656
|
marginRight: 12,
|
|
588
657
|
},
|
|
589
658
|
platformName: {
|
|
@@ -624,4 +693,88 @@ const styles = StyleSheet.create({
|
|
|
624
693
|
fontSize: 16,
|
|
625
694
|
fontWeight: '600',
|
|
626
695
|
},
|
|
696
|
+
alreadyHaveAccountButton: {
|
|
697
|
+
paddingVertical: 8,
|
|
698
|
+
paddingHorizontal: 16,
|
|
699
|
+
},
|
|
700
|
+
alreadyHaveAccountText: {
|
|
701
|
+
color: '#666',
|
|
702
|
+
fontSize: 16,
|
|
703
|
+
},
|
|
704
|
+
successContainer: {
|
|
705
|
+
flex: 1,
|
|
706
|
+
justifyContent: 'center',
|
|
707
|
+
alignItems: 'center',
|
|
708
|
+
},
|
|
709
|
+
successContent: {
|
|
710
|
+
backgroundColor: '#fff',
|
|
711
|
+
padding: 24,
|
|
712
|
+
borderRadius: 16,
|
|
713
|
+
alignItems: 'center',
|
|
714
|
+
},
|
|
715
|
+
successIcon: {
|
|
716
|
+
backgroundColor: '#4CAF50',
|
|
717
|
+
borderRadius: 24,
|
|
718
|
+
padding: 12,
|
|
719
|
+
marginBottom: 16,
|
|
720
|
+
},
|
|
721
|
+
successTitle: {
|
|
722
|
+
fontSize: 22,
|
|
723
|
+
fontWeight: '600',
|
|
724
|
+
color: '#000',
|
|
725
|
+
textAlign: 'center',
|
|
726
|
+
marginBottom: 16,
|
|
727
|
+
},
|
|
728
|
+
successSubtitle: {
|
|
729
|
+
fontSize: 14,
|
|
730
|
+
color: '#666',
|
|
731
|
+
textAlign: 'center',
|
|
732
|
+
marginBottom: 16,
|
|
733
|
+
},
|
|
734
|
+
successMessage: {
|
|
735
|
+
backgroundColor: '#f0f0f0',
|
|
736
|
+
padding: 16,
|
|
737
|
+
borderRadius: 8,
|
|
738
|
+
marginBottom: 16,
|
|
739
|
+
},
|
|
740
|
+
successMessageText: {
|
|
741
|
+
fontSize: 14,
|
|
742
|
+
color: '#666',
|
|
743
|
+
},
|
|
744
|
+
platformToggle: {
|
|
745
|
+
width: 24,
|
|
746
|
+
height: 24,
|
|
747
|
+
borderRadius: 12,
|
|
748
|
+
borderWidth: 1,
|
|
749
|
+
borderColor: '#ccc',
|
|
750
|
+
alignItems: 'center',
|
|
751
|
+
justifyContent: 'center',
|
|
752
|
+
backgroundColor: '#fff',
|
|
753
|
+
},
|
|
754
|
+
platformToggleActive: {
|
|
755
|
+
borderColor: '#000',
|
|
756
|
+
backgroundColor: '#000',
|
|
757
|
+
},
|
|
758
|
+
// Dark mode styles
|
|
759
|
+
darkPlatformItem: {
|
|
760
|
+
backgroundColor: '#333',
|
|
761
|
+
borderColor: '#555',
|
|
762
|
+
},
|
|
763
|
+
darkText: {
|
|
764
|
+
color: '#fff',
|
|
765
|
+
},
|
|
766
|
+
darkSubText: {
|
|
767
|
+
color: '#ccc',
|
|
768
|
+
},
|
|
769
|
+
progressIndicator: {
|
|
770
|
+
flexDirection: 'row',
|
|
771
|
+
alignItems: 'center',
|
|
772
|
+
marginTop: 16,
|
|
773
|
+
},
|
|
774
|
+
progressText: {
|
|
775
|
+
fontSize: 16,
|
|
776
|
+
fontWeight: '500',
|
|
777
|
+
color: '#000',
|
|
778
|
+
marginLeft: 8,
|
|
779
|
+
},
|
|
627
780
|
});
|
|
@@ -1,12 +1,83 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
|
-
import * as Keychain from 'react-native-keychain';
|
|
3
2
|
import { STORAGE_KEYS } from '../constants';
|
|
4
3
|
import type { CredentialsResult } from '../types';
|
|
5
4
|
|
|
5
|
+
// Create a mock storage for environments without Keychain access
|
|
6
|
+
const mockCredentialStorage: Record<string, any> = {};
|
|
7
|
+
|
|
8
|
+
// Try to import Keychain, but provide fallbacks if not available
|
|
9
|
+
let Keychain: any = null;
|
|
10
|
+
try {
|
|
11
|
+
Keychain = require('react-native-keychain');
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.warn('react-native-keychain module not available in useCredentials, using mock storage');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Check if Keychain is properly initialized and available
|
|
17
|
+
const isKeychainAvailable = () => {
|
|
18
|
+
try {
|
|
19
|
+
return Keychain && typeof Keychain.getGenericPassword === 'function';
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Safe wrapper for getGenericPassword
|
|
26
|
+
const safeGetGenericPassword = async (options: any) => {
|
|
27
|
+
try {
|
|
28
|
+
if (isKeychainAvailable()) {
|
|
29
|
+
return await Keychain.getGenericPassword(options);
|
|
30
|
+
} else {
|
|
31
|
+
const key = options?.service || 'default';
|
|
32
|
+
return mockCredentialStorage[key] || null;
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.warn('Keychain access failed, using mock storage', error);
|
|
36
|
+
const key = options?.service || 'default';
|
|
37
|
+
return mockCredentialStorage[key] || null;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Safe wrapper for setGenericPassword
|
|
42
|
+
const safeSetGenericPassword = async (username: string, password: string, options?: any) => {
|
|
43
|
+
try {
|
|
44
|
+
if (isKeychainAvailable()) {
|
|
45
|
+
return await Keychain.setGenericPassword(username, password, options);
|
|
46
|
+
} else {
|
|
47
|
+
const key = options?.service || 'default';
|
|
48
|
+
mockCredentialStorage[key] = { username, password };
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn('Keychain access failed, using mock storage', error);
|
|
53
|
+
const key = options?.service || 'default';
|
|
54
|
+
mockCredentialStorage[key] = { username, password };
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Safe wrapper for resetGenericPassword
|
|
60
|
+
const safeResetGenericPassword = async (options?: any) => {
|
|
61
|
+
try {
|
|
62
|
+
if (isKeychainAvailable()) {
|
|
63
|
+
return await Keychain.resetGenericPassword(options);
|
|
64
|
+
} else {
|
|
65
|
+
const key = options?.service || 'default';
|
|
66
|
+
delete mockCredentialStorage[key];
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.warn('Keychain access failed, using mock storage', error);
|
|
71
|
+
const key = options?.service || 'default';
|
|
72
|
+
delete mockCredentialStorage[key];
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
6
77
|
export const useCredentials = () => {
|
|
7
78
|
const hasCredentials = useCallback(async (): Promise<boolean> => {
|
|
8
79
|
try {
|
|
9
|
-
const credentials = await
|
|
80
|
+
const credentials = await safeGetGenericPassword({
|
|
10
81
|
service: STORAGE_KEYS.credentials
|
|
11
82
|
});
|
|
12
83
|
return !!credentials;
|
|
@@ -18,7 +89,7 @@ export const useCredentials = () => {
|
|
|
18
89
|
|
|
19
90
|
const getCredentials = useCallback(async () => {
|
|
20
91
|
try {
|
|
21
|
-
const credentials = await
|
|
92
|
+
const credentials = await safeGetGenericPassword({
|
|
22
93
|
service: STORAGE_KEYS.credentials
|
|
23
94
|
});
|
|
24
95
|
if (credentials) {
|
|
@@ -37,13 +108,17 @@ export const useCredentials = () => {
|
|
|
37
108
|
accessToken: string
|
|
38
109
|
): Promise<boolean> => {
|
|
39
110
|
try {
|
|
40
|
-
const options:
|
|
41
|
-
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY,
|
|
42
|
-
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED,
|
|
111
|
+
const options: any = {
|
|
43
112
|
service: STORAGE_KEYS.credentials
|
|
44
113
|
};
|
|
45
114
|
|
|
46
|
-
|
|
115
|
+
// Only use secure storage options on real devices
|
|
116
|
+
if (isKeychainAvailable()) {
|
|
117
|
+
options.accessControl = Keychain.ACCESS_CONTROL?.BIOMETRY_ANY;
|
|
118
|
+
options.accessible = Keychain.ACCESSIBLE?.WHEN_UNLOCKED;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await safeSetGenericPassword(
|
|
47
122
|
username,
|
|
48
123
|
JSON.stringify({ userPin, accessToken }),
|
|
49
124
|
options
|
|
@@ -57,7 +132,7 @@ export const useCredentials = () => {
|
|
|
57
132
|
|
|
58
133
|
const clearCredentials = useCallback(async (): Promise<void> => {
|
|
59
134
|
try {
|
|
60
|
-
await
|
|
135
|
+
await safeResetGenericPassword({
|
|
61
136
|
service: STORAGE_KEYS.credentials
|
|
62
137
|
});
|
|
63
138
|
} catch (error) {
|