mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 21:35:21 +01:00
REF: useBiometrics
This commit is contained in:
parent
3b835e3dfe
commit
60d6abf08b
@ -2,7 +2,6 @@ import 'react-native-gesture-handler'; // should be on top
|
||||
import React, { Suspense, lazy } from 'react';
|
||||
import MainRoot from './navigation';
|
||||
import { useStorage } from './blue_modules/storage-context';
|
||||
import Biometric from './class/biometrics';
|
||||
const CompanionDelegates = lazy(() => import('./components/CompanionDelegates'));
|
||||
|
||||
const MasterView = () => {
|
||||
@ -10,7 +9,6 @@ const MasterView = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Biometric />
|
||||
<MainRoot />
|
||||
{walletsInitialized && (
|
||||
<Suspense>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Platform } from 'react-native';
|
||||
import Biometric from '../class/biometrics';
|
||||
import prompt from '../helpers/prompt';
|
||||
import loc from '../loc';
|
||||
import { BlueApp as BlueAppClass } from '../class/';
|
||||
import { showKeychainWipeAlert } from '../class/biometrics';
|
||||
|
||||
const BlueApp = BlueAppClass.getInstance();
|
||||
// If attempt reaches 10, a wipe keychain option will be provided to the user.
|
||||
@ -55,7 +55,7 @@ export const startAndDecrypt = async (retry?: boolean): Promise<boolean> => {
|
||||
return startAndDecrypt(true);
|
||||
} else {
|
||||
unlockAttempt = 0;
|
||||
Biometric.showKeychainWipeAlert();
|
||||
showKeychainWipeAlert();
|
||||
// We want to return false to let the UnlockWith screen that it is NOT ok to proceed.
|
||||
return false;
|
||||
}
|
||||
|
@ -1,186 +1,174 @@
|
||||
import { useContext } from 'react';
|
||||
import { Alert, Platform } from 'react-native';
|
||||
import ReactNativeBiometrics, { BiometryTypes as RNBiometryTypes } from 'react-native-biometrics';
|
||||
import PasscodeAuth from 'react-native-passcode-auth';
|
||||
import RNSecureKeyStore, { ACCESSIBLE } from 'react-native-secure-key-store';
|
||||
import loc from '../loc';
|
||||
import * as NavigationService from '../NavigationService';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import presentAlert from '../components/Alert';
|
||||
import { BlueApp } from './blue-app';
|
||||
|
||||
const STORAGEKEY = 'Biometrics';
|
||||
|
||||
const rnBiometrics = new ReactNativeBiometrics({ allowDeviceCredentials: true });
|
||||
|
||||
// Define a function type with properties
|
||||
type DescribableFunction = {
|
||||
(): null; // Call signature
|
||||
FaceID: 'Face ID';
|
||||
TouchID: 'Touch ID';
|
||||
Biometrics: 'Biometrics';
|
||||
isBiometricUseCapableAndEnabled: () => Promise<boolean>;
|
||||
isDeviceBiometricCapable: () => Promise<boolean>;
|
||||
setBiometricUseEnabled: (arg: boolean) => Promise<void>;
|
||||
biometricType: () => Promise<keyof typeof RNBiometryTypes | undefined>;
|
||||
isBiometricUseEnabled: () => Promise<boolean>;
|
||||
unlockWithBiometrics: () => Promise<boolean>;
|
||||
showKeychainWipeAlert: () => void;
|
||||
const FaceID = 'Face ID';
|
||||
const TouchID = 'Touch ID';
|
||||
const Biometrics = 'Biometrics';
|
||||
|
||||
const isDeviceBiometricCapable = async () => {
|
||||
try {
|
||||
const { available } = await rnBiometrics.isSensorAvailable();
|
||||
return available;
|
||||
} catch (e) {
|
||||
console.log('Biometrics isDeviceBiometricCapable failed');
|
||||
console.log(e);
|
||||
setBiometricUseEnabled(false);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Bastard component/module. All properties are added in runtime
|
||||
const Biometric = function () {
|
||||
const { getItem, setItem } = useContext(BlueStorageContext);
|
||||
Biometric.FaceID = 'Face ID';
|
||||
Biometric.TouchID = 'Touch ID';
|
||||
Biometric.Biometrics = 'Biometrics';
|
||||
|
||||
Biometric.isDeviceBiometricCapable = async () => {
|
||||
try {
|
||||
const { available } = await rnBiometrics.isSensorAvailable();
|
||||
return available;
|
||||
} catch (e) {
|
||||
console.log('Biometrics isDeviceBiometricCapable failed');
|
||||
console.log(e);
|
||||
Biometric.setBiometricUseEnabled(false);
|
||||
const biometricType = async () => {
|
||||
try {
|
||||
const { available, biometryType } = await rnBiometrics.isSensorAvailable();
|
||||
if (!available) {
|
||||
return undefined;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Biometric.biometricType = async () => {
|
||||
try {
|
||||
const { available, biometryType } = await rnBiometrics.isSensorAvailable();
|
||||
if (!available) {
|
||||
return undefined;
|
||||
}
|
||||
return biometryType;
|
||||
} catch (e) {
|
||||
console.log('Biometrics biometricType failed');
|
||||
console.log(e);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
return biometryType;
|
||||
} catch (e) {
|
||||
console.log('Biometrics biometricType failed');
|
||||
console.log(e);
|
||||
return undefined; // Explicitly return false in case of an error
|
||||
}
|
||||
};
|
||||
const isBiometricUseEnabled = async () => {
|
||||
try {
|
||||
const enabledBiometrics = await BlueApp.getInstance().getItem(STORAGEKEY);
|
||||
return !!enabledBiometrics;
|
||||
} catch (_) {}
|
||||
|
||||
Biometric.isBiometricUseEnabled = async () => {
|
||||
try {
|
||||
const enabledBiometrics = await getItem(STORAGEKEY);
|
||||
return !!enabledBiometrics;
|
||||
} catch (_) {}
|
||||
return false;
|
||||
};
|
||||
|
||||
return false;
|
||||
};
|
||||
const isBiometricUseCapableAndEnabled = async () => {
|
||||
const isEnabled = await isBiometricUseEnabled();
|
||||
const isCapable = await isDeviceBiometricCapable();
|
||||
return isEnabled && isCapable;
|
||||
};
|
||||
|
||||
Biometric.isBiometricUseCapableAndEnabled = async () => {
|
||||
const isBiometricUseEnabled = await Biometric.isBiometricUseEnabled();
|
||||
const isDeviceBiometricCapable = await Biometric.isDeviceBiometricCapable();
|
||||
return isBiometricUseEnabled && isDeviceBiometricCapable;
|
||||
};
|
||||
const setBiometricUseEnabled = async (value: boolean) => {
|
||||
await BlueApp.getInstance().setItem(STORAGEKEY, value === true ? '1' : '');
|
||||
};
|
||||
|
||||
Biometric.setBiometricUseEnabled = async value => {
|
||||
await setItem(STORAGEKEY, value === true ? '1' : '');
|
||||
};
|
||||
|
||||
Biometric.unlockWithBiometrics = async () => {
|
||||
const isDeviceBiometricCapable = await Biometric.isDeviceBiometricCapable();
|
||||
if (isDeviceBiometricCapable) {
|
||||
return new Promise(resolve => {
|
||||
rnBiometrics
|
||||
.simplePrompt({ promptMessage: loc.settings.biom_conf_identity })
|
||||
.then((result: { success: any }) => {
|
||||
if (result.success) {
|
||||
resolve(true);
|
||||
} else {
|
||||
console.log('Biometrics authentication failed');
|
||||
resolve(false);
|
||||
}
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
console.log('Biometrics authentication error');
|
||||
presentAlert({ message: error.message });
|
||||
const unlockWithBiometrics = async () => {
|
||||
const isCapable = await isDeviceBiometricCapable();
|
||||
if (isCapable) {
|
||||
return new Promise(resolve => {
|
||||
rnBiometrics
|
||||
.simplePrompt({ promptMessage: loc.settings.biom_conf_identity })
|
||||
.then((result: { success: any }) => {
|
||||
if (result.success) {
|
||||
resolve(true);
|
||||
} else {
|
||||
console.log('Biometrics authentication failed');
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
console.log('Biometrics authentication error');
|
||||
presentAlert({ message: error.message });
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const clearKeychain = async () => {
|
||||
try {
|
||||
console.log('Wiping keychain');
|
||||
console.log('Wiping key: data');
|
||||
await RNSecureKeyStore.set('data', JSON.stringify({ data: { wallets: [] } }), {
|
||||
accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
||||
});
|
||||
console.log('Wiped key: data');
|
||||
console.log('Wiping key: data_encrypted');
|
||||
await RNSecureKeyStore.set('data_encrypted', '', { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
|
||||
console.log('Wiped key: data_encrypted');
|
||||
console.log('Wiping key: STORAGEKEY');
|
||||
await RNSecureKeyStore.set(STORAGEKEY, '', { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
|
||||
console.log('Wiped key: STORAGEKEY');
|
||||
NavigationService.reset();
|
||||
} catch (error: any) {
|
||||
console.warn(error);
|
||||
presentAlert({ message: error.message });
|
||||
}
|
||||
};
|
||||
const clearKeychain = async () => {
|
||||
try {
|
||||
console.log('Wiping keychain');
|
||||
console.log('Wiping key: data');
|
||||
await RNSecureKeyStore.set('data', JSON.stringify({ data: { wallets: [] } }), {
|
||||
accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
||||
});
|
||||
console.log('Wiped key: data');
|
||||
console.log('Wiping key: data_encrypted');
|
||||
await RNSecureKeyStore.set('data_encrypted', '', { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
|
||||
console.log('Wiped key: data_encrypted');
|
||||
console.log('Wiping key: STORAGEKEY');
|
||||
await RNSecureKeyStore.set(STORAGEKEY, '', { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
|
||||
console.log('Wiped key: STORAGEKEY');
|
||||
NavigationService.reset();
|
||||
} catch (error: any) {
|
||||
console.warn(error);
|
||||
presentAlert({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const requestDevicePasscode = async () => {
|
||||
let isDevicePasscodeSupported: boolean | undefined = false;
|
||||
try {
|
||||
isDevicePasscodeSupported = await PasscodeAuth.isSupported();
|
||||
if (isDevicePasscodeSupported) {
|
||||
const isAuthenticated = await PasscodeAuth.authenticate();
|
||||
if (isAuthenticated) {
|
||||
Alert.alert(
|
||||
loc.settings.encrypt_tstorage,
|
||||
loc.settings.biom_remove_decrypt,
|
||||
[
|
||||
{ text: loc._.cancel, style: 'cancel' },
|
||||
{
|
||||
text: loc._.ok,
|
||||
style: 'destructive',
|
||||
onPress: async () => await clearKeychain(),
|
||||
},
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
isDevicePasscodeSupported = undefined;
|
||||
}
|
||||
if (isDevicePasscodeSupported === false) {
|
||||
presentAlert({ message: loc.settings.biom_no_passcode });
|
||||
}
|
||||
};
|
||||
|
||||
Biometric.showKeychainWipeAlert = () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
Alert.alert(
|
||||
loc.settings.encrypt_tstorage,
|
||||
loc.settings.biom_10times,
|
||||
[
|
||||
{
|
||||
text: loc._.cancel,
|
||||
onPress: () => {
|
||||
console.log('Cancel Pressed');
|
||||
const requestDevicePasscode = async () => {
|
||||
let isDevicePasscodeSupported: boolean | undefined = false;
|
||||
try {
|
||||
isDevicePasscodeSupported = await PasscodeAuth.isSupported();
|
||||
if (isDevicePasscodeSupported) {
|
||||
const isAuthenticated = await PasscodeAuth.authenticate();
|
||||
if (isAuthenticated) {
|
||||
Alert.alert(
|
||||
loc.settings.encrypt_tstorage,
|
||||
loc.settings.biom_remove_decrypt,
|
||||
[
|
||||
{ text: loc._.cancel, style: 'cancel' },
|
||||
{
|
||||
text: loc._.ok,
|
||||
style: 'destructive',
|
||||
onPress: async () => await clearKeychain(),
|
||||
},
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: loc._.ok,
|
||||
onPress: () => requestDevicePasscode(),
|
||||
style: 'default',
|
||||
},
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
} catch {
|
||||
isDevicePasscodeSupported = undefined;
|
||||
}
|
||||
if (isDevicePasscodeSupported === false) {
|
||||
presentAlert({ message: loc.settings.biom_no_passcode });
|
||||
}
|
||||
};
|
||||
|
||||
return null;
|
||||
} as DescribableFunction;
|
||||
export const showKeychainWipeAlert = () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
Alert.alert(
|
||||
loc.settings.encrypt_tstorage,
|
||||
loc.settings.biom_10times,
|
||||
[
|
||||
{
|
||||
text: loc._.cancel,
|
||||
onPress: () => {
|
||||
console.log('Cancel Pressed');
|
||||
},
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: loc._.ok,
|
||||
onPress: () => requestDevicePasscode(),
|
||||
style: 'default',
|
||||
},
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
FaceID,
|
||||
TouchID,
|
||||
Biometrics,
|
||||
isDeviceBiometricCapable,
|
||||
biometricType,
|
||||
isBiometricUseEnabled,
|
||||
isBiometricUseCapableAndEnabled,
|
||||
setBiometricUseEnabled,
|
||||
unlockWithBiometrics,
|
||||
};
|
||||
|
||||
export default Biometric;
|
||||
export { RNBiometryTypes as BiometricType };
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { ListItem } from 'react-native-elements';
|
||||
@ -10,12 +10,12 @@ import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import Share from 'react-native-share';
|
||||
import { useTheme } from '../themes';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import { useStorage } from '../../blue_modules/storage-context';
|
||||
import presentAlert from '../Alert';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import QRCodeComponent from '../QRCodeComponent';
|
||||
import confirm from '../../helpers/confirm';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
interface AddressItemProps {
|
||||
// todo: fix `any` after addresses.js is converted to the church of holy typescript
|
||||
@ -26,8 +26,9 @@ interface AddressItemProps {
|
||||
}
|
||||
|
||||
const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }: AddressItemProps) => {
|
||||
const { wallets } = useContext(BlueStorageContext);
|
||||
const { wallets } = useStorage();
|
||||
const { colors } = useTheme();
|
||||
const { isBiometricUseCapableAndEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
|
||||
const hasTransactions = item.transactions > 0;
|
||||
|
||||
@ -118,8 +119,8 @@ const AddressItem = ({ item, balanceUnit, walletID, allowSignVerifyMessage }: Ad
|
||||
navigateToSignVerify();
|
||||
} else if (id === AddressItem.actionKeys.ExportPrivateKey) {
|
||||
if (await confirm(loc.addresses.sensitive_private_key)) {
|
||||
if (await Biometric.isBiometricUseCapableAndEnabled()) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (await isBiometricUseCapableAndEnabled()) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
196
hooks/useBiometrics.ts
Normal file
196
hooks/useBiometrics.ts
Normal file
@ -0,0 +1,196 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Alert, Platform } from 'react-native';
|
||||
import ReactNativeBiometrics, { BiometryTypes as RNBiometryTypes } from 'react-native-biometrics';
|
||||
import PasscodeAuth from 'react-native-passcode-auth';
|
||||
import RNSecureKeyStore, { ACCESSIBLE } from 'react-native-secure-key-store';
|
||||
import loc from '../loc';
|
||||
import * as NavigationService from '../NavigationService';
|
||||
import { useStorage } from '../blue_modules/storage-context';
|
||||
import presentAlert from '../components/Alert';
|
||||
|
||||
const STORAGEKEY = 'Biometrics';
|
||||
const rnBiometrics = new ReactNativeBiometrics({ allowDeviceCredentials: true });
|
||||
|
||||
const FaceID = 'Face ID';
|
||||
const TouchID = 'Touch ID';
|
||||
const Biometrics = 'Biometrics';
|
||||
|
||||
const clearKeychain = async () => {
|
||||
try {
|
||||
console.log('Wiping keychain');
|
||||
console.log('Wiping key: data');
|
||||
await RNSecureKeyStore.set('data', JSON.stringify({ data: { wallets: [] } }), {
|
||||
accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
||||
});
|
||||
console.log('Wiped key: data');
|
||||
console.log('Wiping key: data_encrypted');
|
||||
await RNSecureKeyStore.set('data_encrypted', '', { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
|
||||
console.log('Wiped key: data_encrypted');
|
||||
console.log('Wiping key: STORAGEKEY');
|
||||
await RNSecureKeyStore.set(STORAGEKEY, '', { accessible: ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY });
|
||||
console.log('Wiped key: STORAGEKEY');
|
||||
NavigationService.reset();
|
||||
} catch (error: any) {
|
||||
console.warn(error);
|
||||
presentAlert({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const requestDevicePasscode = async () => {
|
||||
let isDevicePasscodeSupported: boolean | undefined = false;
|
||||
try {
|
||||
isDevicePasscodeSupported = await PasscodeAuth.isSupported();
|
||||
if (isDevicePasscodeSupported) {
|
||||
const isAuthenticated = await PasscodeAuth.authenticate();
|
||||
if (isAuthenticated) {
|
||||
Alert.alert(
|
||||
loc.settings.encrypt_tstorage,
|
||||
loc.settings.biom_remove_decrypt,
|
||||
[
|
||||
{ text: loc._.cancel, style: 'cancel' },
|
||||
{
|
||||
text: loc._.ok,
|
||||
style: 'destructive',
|
||||
onPress: async () => await clearKeychain(),
|
||||
},
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
isDevicePasscodeSupported = undefined;
|
||||
}
|
||||
if (isDevicePasscodeSupported === false) {
|
||||
presentAlert({ message: loc.settings.biom_no_passcode });
|
||||
}
|
||||
};
|
||||
|
||||
const showKeychainWipeAlert = () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
Alert.alert(
|
||||
loc.settings.encrypt_tstorage,
|
||||
loc.settings.biom_10times,
|
||||
[
|
||||
{
|
||||
text: loc._.cancel,
|
||||
onPress: () => {
|
||||
console.log('Cancel Pressed');
|
||||
},
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: loc._.ok,
|
||||
onPress: () => requestDevicePasscode(),
|
||||
style: 'default',
|
||||
},
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const useBiometrics = () => {
|
||||
const { getItem, setItem } = useStorage();
|
||||
const [biometricEnabled, setBiometricEnabled] = useState(false);
|
||||
const [deviceBiometricType, setDeviceBiometricType] = useState<'TouchID' | 'FaceID' | 'Biometrics' | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchBiometricEnabledStatus = async () => {
|
||||
const enabled = await isBiometricUseEnabled();
|
||||
setBiometricEnabled(enabled);
|
||||
|
||||
const biometricType = await type();
|
||||
setDeviceBiometricType(biometricType);
|
||||
};
|
||||
|
||||
fetchBiometricEnabledStatus();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const isDeviceBiometricCapable = async () => {
|
||||
try {
|
||||
const { available } = await rnBiometrics.isSensorAvailable();
|
||||
return available;
|
||||
} catch (e) {
|
||||
console.log('Biometrics isDeviceBiometricCapable failed');
|
||||
console.log(e);
|
||||
setBiometricUseEnabled(false);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const type = async () => {
|
||||
try {
|
||||
const { available, biometryType } = await rnBiometrics.isSensorAvailable();
|
||||
if (!available) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return biometryType;
|
||||
} catch (e) {
|
||||
console.log('Biometrics biometricType failed');
|
||||
console.log(e);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const isBiometricUseEnabled = async () => {
|
||||
try {
|
||||
const enabledBiometrics = await getItem(STORAGEKEY);
|
||||
return !!enabledBiometrics;
|
||||
} catch (_) {}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const isBiometricUseCapableAndEnabled = async () => {
|
||||
const isEnabled = await isBiometricUseEnabled();
|
||||
const isCapable = await isDeviceBiometricCapable();
|
||||
return isEnabled && isCapable;
|
||||
};
|
||||
|
||||
const setBiometricUseEnabled = async (value: boolean) => {
|
||||
await setItem(STORAGEKEY, value === true ? '1' : '');
|
||||
setBiometricEnabled(value);
|
||||
};
|
||||
|
||||
const unlockWithBiometrics = async () => {
|
||||
const isCapable = await isDeviceBiometricCapable();
|
||||
if (isCapable) {
|
||||
return new Promise(resolve => {
|
||||
rnBiometrics
|
||||
.simplePrompt({ promptMessage: loc.settings.biom_conf_identity })
|
||||
.then((result: { success: any }) => {
|
||||
if (result.success) {
|
||||
resolve(true);
|
||||
} else {
|
||||
console.log('Biometrics authentication failed');
|
||||
resolve(false);
|
||||
}
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
console.log('Biometrics authentication error');
|
||||
presentAlert({ message: error.message });
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
isDeviceBiometricCapable,
|
||||
deviceBiometricType,
|
||||
isBiometricUseEnabled,
|
||||
isBiometricUseCapableAndEnabled,
|
||||
setBiometricUseEnabled,
|
||||
unlockWithBiometrics,
|
||||
clearKeychain,
|
||||
requestDevicePasscode,
|
||||
showKeychainWipeAlert,
|
||||
biometricEnabled,
|
||||
};
|
||||
};
|
||||
|
||||
export { FaceID, TouchID, Biometrics, RNBiometryTypes as BiometricType, useBiometrics };
|
@ -1,9 +1,8 @@
|
||||
import { useNavigation, NavigationProp, ParamListBase } from '@react-navigation/native';
|
||||
import Biometric from '../class/biometrics';
|
||||
import { navigationRef } from '../NavigationService';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import { useContext } from 'react';
|
||||
import { useStorage } from '../blue_modules/storage-context';
|
||||
import { presentWalletExportReminder } from '../helpers/presentWalletExportReminder';
|
||||
import { useBiometrics } from './useBiometrics';
|
||||
|
||||
// List of screens that require biometrics
|
||||
|
||||
@ -15,7 +14,8 @@ const requiresWalletExportIsSaved = ['ReceiveDetailsRoot', 'WalletAddresses'];
|
||||
|
||||
export const useExtendedNavigation = (): NavigationProp<ParamListBase> => {
|
||||
const originalNavigation = useNavigation<NavigationProp<ParamListBase>>();
|
||||
const { wallets, saveToDisk } = useContext(BlueStorageContext);
|
||||
const { wallets, saveToDisk } = useStorage();
|
||||
const { isBiometricUseEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
|
||||
const enhancedNavigate: NavigationProp<ParamListBase>['navigate'] = (screenOrOptions: any, params?: any) => {
|
||||
let screenName: string;
|
||||
@ -42,9 +42,9 @@ export const useExtendedNavigation = (): NavigationProp<ParamListBase> => {
|
||||
|
||||
(async () => {
|
||||
if (isRequiresBiometrics) {
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseEnabled();
|
||||
const isBiometricsEnabled = await isBiometricUseEnabled();
|
||||
if (isBiometricsEnabled) {
|
||||
const isAuthenticated = await Biometric.unlockWithBiometrics();
|
||||
const isAuthenticated = await unlockWithBiometrics();
|
||||
if (isAuthenticated) {
|
||||
proceedWithNavigation();
|
||||
return; // Ensure the function exits if this path is taken
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React, { useCallback, useContext, useEffect, useReducer, useRef } from 'react';
|
||||
import React, { useCallback, useEffect, useReducer, useRef } from 'react';
|
||||
import { View, Image, ActivityIndicator, StyleSheet } from 'react-native';
|
||||
import Biometric, { BiometricType } from '../class/biometrics';
|
||||
import { BlueStorageContext } from '../blue_modules/storage-context';
|
||||
import { useStorage } from '../blue_modules/storage-context';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../blue_modules/hapticFeedback';
|
||||
import SafeArea from '../components/SafeArea';
|
||||
import { BlueTextCentered } from '../BlueComponents';
|
||||
import loc from '../loc';
|
||||
import Button from '../components/Button';
|
||||
import { BiometricType, useBiometrics } from '../hooks/useBiometrics';
|
||||
|
||||
enum AuthType {
|
||||
Encrypted,
|
||||
@ -52,7 +52,8 @@ function reducer(state: State, action: Action): State {
|
||||
const UnlockWith: React.FC = () => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const isUnlockingWallets = useRef(false);
|
||||
const { setWalletsInitialized, isStorageEncrypted, startAndDecrypt } = useContext(BlueStorageContext);
|
||||
const { setWalletsInitialized, isStorageEncrypted, startAndDecrypt } = useStorage();
|
||||
const { deviceBiometricType, unlockWithBiometrics, isBiometricUseCapableAndEnabled, isBiometricUseEnabled } = useBiometrics();
|
||||
|
||||
const successfullyAuthenticated = useCallback(() => {
|
||||
setWalletsInitialized(true);
|
||||
@ -60,19 +61,20 @@ const UnlockWith: React.FC = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const unlockWithBiometrics = useCallback(async () => {
|
||||
const unlockUsingBiometrics = useCallback(async () => {
|
||||
if (isUnlockingWallets.current || state.isAuthenticating) return;
|
||||
isUnlockingWallets.current = true;
|
||||
dispatch({ type: SET_IS_AUTHENTICATING, payload: true });
|
||||
|
||||
if (await Biometric.unlockWithBiometrics()) {
|
||||
if (await unlockWithBiometrics()) {
|
||||
await startAndDecrypt();
|
||||
successfullyAuthenticated();
|
||||
}
|
||||
|
||||
dispatch({ type: SET_IS_AUTHENTICATING, payload: false });
|
||||
isUnlockingWallets.current = false;
|
||||
}, [state.isAuthenticating, startAndDecrypt, successfullyAuthenticated]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [state.isAuthenticating]);
|
||||
|
||||
const unlockWithKey = useCallback(async () => {
|
||||
if (isUnlockingWallets.current || state.isAuthenticating) return;
|
||||
@ -91,14 +93,14 @@ const UnlockWith: React.FC = () => {
|
||||
useEffect(() => {
|
||||
const startUnlock = async () => {
|
||||
const storageIsEncrypted = await isStorageEncrypted();
|
||||
const isBiometricUseCapableAndEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
const biometricType = isBiometricUseCapableAndEnabled ? await Biometric.biometricType() : undefined;
|
||||
const biometricsUseEnabled = await Biometric.isBiometricUseEnabled();
|
||||
const biometricUseCapableAndEnabled = await isBiometricUseCapableAndEnabled();
|
||||
const biometricsUseEnabled = await isBiometricUseEnabled();
|
||||
const biometricType = biometricUseCapableAndEnabled ? deviceBiometricType : undefined;
|
||||
|
||||
if (storageIsEncrypted) {
|
||||
dispatch({ type: SET_AUTH, payload: { type: AuthType.Encrypted, detail: undefined } });
|
||||
unlockWithKey();
|
||||
} else if (isBiometricUseCapableAndEnabled) {
|
||||
} else if (biometricUseCapableAndEnabled) {
|
||||
dispatch({ type: SET_AUTH, payload: { type: AuthType.Biometrics, detail: biometricType } });
|
||||
unlockWithBiometrics();
|
||||
} else if (biometricsUseEnabled && biometricType === undefined) {
|
||||
@ -116,7 +118,7 @@ const UnlockWith: React.FC = () => {
|
||||
|
||||
const onUnlockPressed = () => {
|
||||
if (state.auth.type === AuthType.Biometrics) {
|
||||
unlockWithBiometrics();
|
||||
unlockUsingBiometrics();
|
||||
} else {
|
||||
unlockWithKey();
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { BlueLoading, BlueDismissKeyboardInputAccessory, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useStorage } from '../../blue_modules/storage-context';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import AddressInput from '../../components/AddressInput';
|
||||
import AmountInput from '../../components/AmountInput';
|
||||
@ -11,13 +11,13 @@ import loc from '../../loc';
|
||||
import { HDSegwitBech32Wallet, LightningLdkWallet } from '../../class';
|
||||
import { ArrowPicker } from '../../components/ArrowPicker';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import Button from '../../components/Button';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import { btcToSatoshi, fiatToBTC } from '../../blue_modules/currency';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
type LdkOpenChannelProps = RouteProp<
|
||||
{
|
||||
@ -33,8 +33,8 @@ type LdkOpenChannelProps = RouteProp<
|
||||
>;
|
||||
|
||||
const LdkOpenChannel = (props: any) => {
|
||||
const { wallets, fetchAndSaveWalletTransactions } = useContext(BlueStorageContext);
|
||||
const [isBiometricUseCapableAndEnabled, setIsBiometricUseCapableAndEnabled] = useState(false);
|
||||
const { wallets, fetchAndSaveWalletTransactions } = useStorage();
|
||||
const { isBiometricUseCapableAndEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
const { colors }: { colors: any } = useTheme();
|
||||
const { navigate, setParams } = useNavigation();
|
||||
const {
|
||||
@ -75,14 +75,10 @@ const LdkOpenChannel = (props: any) => {
|
||||
})();
|
||||
}, [psbt]);
|
||||
|
||||
useEffect(() => {
|
||||
Biometric.isBiometricUseCapableAndEnabled().then(setIsBiometricUseCapableAndEnabled);
|
||||
}, []);
|
||||
|
||||
const finalizeOpenChannel = async () => {
|
||||
setIsLoading(true);
|
||||
if (isBiometricUseCapableAndEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (await isBiometricUseCapableAndEnabled()) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { I18nManager, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { useNavigation, useRoute } from '@react-navigation/native';
|
||||
@ -8,8 +8,7 @@ import AmountInput from '../../components/AmountInput';
|
||||
import Lnurl from '../../class/lnurl';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import loc, { formatBalanceWithoutSuffix, formatBalance } from '../../loc';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useStorage } from '../../blue_modules/storage-context';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import Button from '../../components/Button';
|
||||
@ -17,6 +16,7 @@ import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/h
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import { btcToSatoshi, fiatToBTC, satoshiToBTC, satoshiToLocalCurrency } from '../../blue_modules/currency';
|
||||
import prompt from '../../helpers/prompt';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
/**
|
||||
* if user has default currency - fiat, attempting to pay will trigger conversion from entered in input field fiat value
|
||||
@ -26,7 +26,8 @@ import prompt from '../../helpers/prompt';
|
||||
const _cacheFiatToSat = {};
|
||||
|
||||
const LnurlPay = () => {
|
||||
const { wallets } = useContext(BlueStorageContext);
|
||||
const { wallets } = useStorage();
|
||||
const { isBiometricUseCapableAndEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
const { walletID, lnurl } = useRoute().params;
|
||||
/** @type {LightningCustodianWallet} */
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
@ -105,9 +106,9 @@ const LnurlPay = () => {
|
||||
/** @type {Lnurl} */
|
||||
const LN = _LN;
|
||||
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
Text,
|
||||
ActivityIndicator,
|
||||
@ -12,24 +12,24 @@ import {
|
||||
} from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { useFocusEffect, useNavigation, useRoute } from '@react-navigation/native';
|
||||
|
||||
import { BlueCard, BlueDismissKeyboardInputAccessory, BlueLoading } from '../../BlueComponents';
|
||||
import AddressInput from '../../components/AddressInput';
|
||||
import AmountInput from '../../components/AmountInput';
|
||||
import Lnurl from '../../class/lnurl';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import loc, { formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useStorage } from '../../blue_modules/storage-context';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import Button from '../../components/Button';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import { btcToSatoshi, fiatToBTC } from '../../blue_modules/currency';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
const ScanLndInvoice = () => {
|
||||
const { wallets, fetchAndSaveWalletTransactions } = useContext(BlueStorageContext);
|
||||
const { wallets, fetchAndSaveWalletTransactions } = useStorage();
|
||||
const { unlockWithBiometrics, isBiometricUseCapableAndEnabled } = useBiometrics();
|
||||
const { colors } = useTheme();
|
||||
const { walletID, uri, invoice } = useRoute().params;
|
||||
const name = useRoute().name;
|
||||
@ -167,10 +167,10 @@ const ScanLndInvoice = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,9 @@ import { PayjoinClient } from 'payjoin-client';
|
||||
import PropTypes from 'prop-types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
|
||||
import PayjoinTransaction from '../../class/payjoin-transaction';
|
||||
import { BlueText, BlueCard } from '../../BlueComponents';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import loc, { formatBalance, formatBalanceWithoutSuffix } from '../../loc';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
@ -21,10 +19,11 @@ import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/h
|
||||
import SafeArea from '../../components/SafeArea';
|
||||
import { satoshiToBTC, satoshiToLocalCurrency } from '../../blue_modules/currency';
|
||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
const Confirm = () => {
|
||||
const { wallets, fetchAndSaveWalletTransactions, isElectrumDisabled } = useContext(BlueStorageContext);
|
||||
const [isBiometricUseCapableAndEnabled, setIsBiometricUseCapableAndEnabled] = useState(false);
|
||||
const { isBiometricUseCapableAndEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
const { params } = useRoute();
|
||||
const { recipients = [], walletID, fee, memo, tx, satoshiPerByte, psbt } = params;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@ -64,7 +63,6 @@ const Confirm = () => {
|
||||
useEffect(() => {
|
||||
console.log('send/confirm - useEffect');
|
||||
console.log('address = ', recipients);
|
||||
Biometric.isBiometricUseCapableAndEnabled().then(setIsBiometricUseCapableAndEnabled);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@ -77,8 +75,8 @@ const Confirm = () => {
|
||||
testID="TransactionDetailsButton"
|
||||
style={[styles.txDetails, stylesHook.txDetails]}
|
||||
onPress={async () => {
|
||||
if (isBiometricUseCapableAndEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (await isBiometricUseCapableAndEnabled()) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -99,7 +97,7 @@ const Confirm = () => {
|
||||
),
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [colors, fee, feeSatoshi, isBiometricUseCapableAndEnabled, memo, recipients, satoshiPerByte, tx, wallet]);
|
||||
}, [colors, fee, feeSatoshi, memo, recipients, satoshiPerByte, tx, wallet]);
|
||||
|
||||
/**
|
||||
* we need to look into `recipients`, find destination address and return its outputScript
|
||||
@ -163,8 +161,8 @@ const Confirm = () => {
|
||||
await BlueElectrum.ping();
|
||||
await BlueElectrum.waitTillConnected();
|
||||
|
||||
if (isBiometricUseCapableAndEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (await isBiometricUseCapableAndEnabled()) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import { useIsFocused, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { ActivityIndicator, Linking, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import RNFS from 'react-native-fs';
|
||||
|
||||
import { BlueCard, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import { useStorage } from '../../blue_modules/storage-context';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import CopyToClipboardButton from '../../components/CopyToClipboardButton';
|
||||
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||
@ -20,9 +18,12 @@ import { requestCameraAuthorization } from '../../helpers/scan-qr';
|
||||
import loc from '../../loc';
|
||||
import SaveFileButton from '../../components/SaveFileButton';
|
||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
const PsbtWithHardwareWallet = () => {
|
||||
const { txMetadata, fetchAndSaveWalletTransactions, isElectrumDisabled } = useContext(BlueStorageContext);
|
||||
const { txMetadata, fetchAndSaveWalletTransactions, isElectrumDisabled } = useStorage();
|
||||
const { isBiometricUseCapableAndEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
|
||||
const navigation = useNavigation();
|
||||
const route = useRoute();
|
||||
const { fromWallet, memo, psbt, deepLinkPSBT, launchedBy } = route.params;
|
||||
@ -116,10 +117,10 @@ const PsbtWithHardwareWallet = () => {
|
||||
|
||||
const broadcast = async () => {
|
||||
setIsLoading(true);
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
import React, { useEffect, useState, useCallback, useContext } from 'react';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { View, ScrollView, Alert, TouchableOpacity, TouchableWithoutFeedback, Text, StyleSheet, Platform } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { BlueLoading, BlueSpacing20, BlueCard, BlueText } from '../../BlueComponents';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import loc from '../../loc';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useStorage } from '../../blue_modules/storage-context';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import ListItem from '../../components/ListItem';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import { useTheme } from '../../components/themes';
|
||||
import prompt from '../../helpers/prompt';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
const EncryptStorage = () => {
|
||||
const { isStorageEncrypted, encryptStorage, decryptStorage, saveToDisk } = useContext(BlueStorageContext);
|
||||
const { isStorageEncrypted, encryptStorage, decryptStorage, saveToDisk } = useStorage();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [biometrics, setBiometrics] = useState({ isDeviceBiometricCapable: false, isBiometricsEnabled: false, biometricsType: '' });
|
||||
const { isDeviceBiometricCapable, biometricEnabled, setBiometricUseEnabled, deviceBiometricType, unlockWithBiometrics } = useBiometrics();
|
||||
const [storageIsEncryptedSwitchEnabled, setStorageIsEncryptedSwitchEnabled] = useState(false);
|
||||
const { navigate, popToTop } = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
@ -28,12 +28,8 @@ const EncryptStorage = () => {
|
||||
});
|
||||
|
||||
const initialState = useCallback(async () => {
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseEnabled();
|
||||
const isDeviceBiometricCapable = await Biometric.isDeviceBiometricCapable();
|
||||
const biometricsType = (await Biometric.biometricType()) || loc.settings.biometrics;
|
||||
const isStorageEncryptedSwitchEnabled = await isStorageEncrypted();
|
||||
setStorageIsEncryptedSwitchEnabled(isStorageEncryptedSwitchEnabled);
|
||||
setBiometrics({ isBiometricsEnabled, isDeviceBiometricCapable, biometricsType });
|
||||
setIsLoading(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
@ -107,15 +103,8 @@ const EncryptStorage = () => {
|
||||
};
|
||||
|
||||
const onUseBiometricSwitch = async value => {
|
||||
const isBiometricsEnabled = {
|
||||
isDeviceBiometricCapable: biometrics.isDeviceBiometricCapable,
|
||||
isBiometricsEnabled: biometrics.isBiometricsEnabled,
|
||||
biometricsType: biometrics.biometricsType,
|
||||
};
|
||||
if (await Biometric.unlockWithBiometrics()) {
|
||||
isBiometricsEnabled.isBiometricsEnabled = value;
|
||||
await Biometric.setBiometricUseEnabled(value);
|
||||
setBiometrics(isBiometricsEnabled);
|
||||
if (await unlockWithBiometrics()) {
|
||||
setBiometricUseEnabled(value);
|
||||
}
|
||||
};
|
||||
|
||||
@ -135,7 +124,7 @@ const EncryptStorage = () => {
|
||||
return isCapable ? (
|
||||
<>
|
||||
<BlueText />
|
||||
<BlueText>{loc.formatString(loc.settings.biometrics_fail, { type: biometrics.biometricsType })}</BlueText>
|
||||
<BlueText>{loc.formatString(loc.settings.biometrics_fail, { type: deviceBiometricType })}</BlueText>
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
@ -147,18 +136,18 @@ const EncryptStorage = () => {
|
||||
) : (
|
||||
<ScrollView contentContainerStyle={styles.root} automaticallyAdjustContentInsets contentInsetAdjustmentBehavior="automatic">
|
||||
<View style={styles.paddingTop} />
|
||||
{biometrics.isDeviceBiometricCapable && (
|
||||
{isDeviceBiometricCapable && (
|
||||
<>
|
||||
<Text adjustsFontSizeToFit style={[styles.headerText, styleHooks.headerText]}>
|
||||
{loc.settings.biometrics}
|
||||
</Text>
|
||||
<ListItem
|
||||
title={loc.formatString(loc.settings.encrypt_use, { type: biometrics.biometricsType })}
|
||||
title={loc.formatString(loc.settings.encrypt_use, { type: deviceBiometricType })}
|
||||
Component={TouchableWithoutFeedback}
|
||||
switch={{ value: biometrics.isBiometricsEnabled, onValueChange: onUseBiometricSwitch }}
|
||||
switch={{ value: biometricEnabled, onValueChange: onUseBiometricSwitch }}
|
||||
/>
|
||||
<BlueCard>
|
||||
<BlueText>{loc.formatString(loc.settings.encrypt_use_expl, { type: biometrics.biometricsType })}</BlueText>
|
||||
<BlueText>{loc.formatString(loc.settings.encrypt_use_expl, { type: deviceBiometricType })}</BlueText>
|
||||
{renderPasscodeExplanation()}
|
||||
</BlueCard>
|
||||
<BlueSpacing20 />
|
||||
|
@ -31,7 +31,6 @@ import * as NavigationService from '../../NavigationService';
|
||||
import { useStorage } from '../../blue_modules/storage-context';
|
||||
import { encodeUR } from '../../blue_modules/ur';
|
||||
import { HDSegwitBech32Wallet, MultisigCosigner, MultisigHDWallet } from '../../class';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
import Button from '../../components/Button';
|
||||
@ -52,11 +51,13 @@ import SaveFileButton from '../../components/SaveFileButton';
|
||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import prompt from '../../helpers/prompt';
|
||||
import { useSettings } from '../../components/Context/SettingsContext';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
const ViewEditMultisigCosigners: React.FC = () => {
|
||||
const hasLoaded = useRef(false);
|
||||
const { colors } = useTheme();
|
||||
const { wallets, setWalletsWithNewOrder, isElectrumDisabled } = useStorage();
|
||||
const { isBiometricUseCapableAndEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
const { isAdvancedModeEnabled } = useSettings();
|
||||
const { navigate, dispatch, addListener } = useExtendedNavigation();
|
||||
const openScannerButtonRef = useRef();
|
||||
@ -173,10 +174,10 @@ const ViewEditMultisigCosigners: React.FC = () => {
|
||||
}
|
||||
setIsLoading(true);
|
||||
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useRoute } from '@react-navigation/native';
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
@ -20,7 +20,7 @@ import {
|
||||
import { BlueCard, BlueLoading, BlueSpacing10, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { useStorage } from '../../blue_modules/storage-context';
|
||||
import {
|
||||
HDAezeedWallet,
|
||||
HDSegwitBech32Wallet,
|
||||
@ -31,7 +31,6 @@ import {
|
||||
SegwitP2SHWallet,
|
||||
WatchOnlyWallet,
|
||||
} from '../../class';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import { AbstractHDElectrumWallet } from '../../class/wallets/abstract-hd-electrum-wallet';
|
||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||
import presentAlert from '../../components/Alert';
|
||||
@ -47,6 +46,7 @@ import SaveFileButton from '../../components/SaveFileButton';
|
||||
import { useSettings } from '../../components/Context/SettingsContext';
|
||||
import HeaderRightButton from '../../components/HeaderRightButton';
|
||||
import { writeFileAndExport } from '../../blue_modules/fs';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scrollViewContent: {
|
||||
@ -107,7 +107,8 @@ const styles = StyleSheet.create({
|
||||
});
|
||||
|
||||
const WalletDetails = () => {
|
||||
const { saveToDisk, wallets, deleteWallet, setSelectedWalletID, txMetadata } = useContext(BlueStorageContext);
|
||||
const { saveToDisk, wallets, deleteWallet, setSelectedWalletID, txMetadata } = useStorage();
|
||||
const { isBiometricUseCapableAndEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
const { walletID } = useRoute().params;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [backdoorPressed, setBackdoorPressed] = useState(0);
|
||||
@ -402,10 +403,10 @@ const WalletDetails = () => {
|
||||
{
|
||||
text: loc.wallets.details_yes_delete,
|
||||
onPress: async () => {
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
if (!(await unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useFocusEffect, useRoute } from '@react-navigation/native';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
@ -24,9 +24,8 @@ import BlueClipboard from '../../blue_modules/clipboard';
|
||||
import { isDesktop } from '../../blue_modules/environment';
|
||||
import * as fs from '../../blue_modules/fs';
|
||||
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
|
||||
import { BlueStorageContext, WalletTransactionsStatus } from '../../blue_modules/storage-context';
|
||||
import { WalletTransactionsStatus, useStorage } from '../../blue_modules/storage-context';
|
||||
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class';
|
||||
import Biometric from '../../class/biometrics';
|
||||
import WalletGradient from '../../class/wallet-gradient';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import { FButton, FContainer } from '../../components/FloatButtons';
|
||||
@ -41,6 +40,7 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||
import loc from '../../loc';
|
||||
import { Chain } from '../../models/bitcoinUnits';
|
||||
import ActionSheet from '../ActionSheet';
|
||||
import { useBiometrics } from '../../hooks/useBiometrics';
|
||||
|
||||
const buttonFontSize =
|
||||
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
|
||||
@ -55,7 +55,8 @@ const WalletTransactions = ({ navigation }) => {
|
||||
walletTransactionUpdateStatus,
|
||||
isElectrumDisabled,
|
||||
setReloadTransactionsMenuActionFunction,
|
||||
} = useContext(BlueStorageContext);
|
||||
} = useStorage();
|
||||
const { isBiometricUseCapableAndEnabled, unlockWithBiometrics } = useBiometrics();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { walletID } = useRoute().params;
|
||||
const { name } = useRoute();
|
||||
@ -485,10 +486,10 @@ const WalletTransactions = ({ navigation }) => {
|
||||
})
|
||||
}
|
||||
onWalletBalanceVisibilityChange={async isShouldBeVisible => {
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
const isBiometricsEnabled = await isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (wallet.hideBalance && isBiometricsEnabled) {
|
||||
const unlocked = await Biometric.unlockWithBiometrics();
|
||||
const unlocked = await unlockWithBiometrics();
|
||||
if (!unlocked) {
|
||||
throw new Error('Biometrics failed');
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user