From 60d6abf08b36b151c4bfd8b6b541ecef102678bd Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Fri, 17 May 2024 18:34:39 -0400 Subject: [PATCH] REF: useBiometrics --- MasterView.tsx | 2 - blue_modules/start-and-decrypt.ts | 4 +- class/biometrics.ts | 304 +++++++++---------- components/addresses/AddressItem.tsx | 13 +- hooks/useBiometrics.ts | 196 ++++++++++++ hooks/useExtendedNavigation.ts | 12 +- screen/UnlockWith.tsx | 26 +- screen/lnd/ldkOpenChannel.tsx | 18 +- screen/lnd/lnurlPay.js | 13 +- screen/lnd/scanLndInvoice.js | 14 +- screen/send/confirm.js | 16 +- screen/send/psbtWithHardwareWallet.js | 15 +- screen/settings/encryptStorage.js | 35 +-- screen/wallets/ViewEditMultisigCosigners.tsx | 7 +- screen/wallets/details.js | 13 +- screen/wallets/transactions.js | 13 +- 16 files changed, 437 insertions(+), 264 deletions(-) create mode 100644 hooks/useBiometrics.ts diff --git a/MasterView.tsx b/MasterView.tsx index 7bef9f3c9..9f2614ef2 100644 --- a/MasterView.tsx +++ b/MasterView.tsx @@ -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 ( <> - {walletsInitialized && ( diff --git a/blue_modules/start-and-decrypt.ts b/blue_modules/start-and-decrypt.ts index 3be918f51..9e1af6cbc 100644 --- a/blue_modules/start-and-decrypt.ts +++ b/blue_modules/start-and-decrypt.ts @@ -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 => { 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; } diff --git a/class/biometrics.ts b/class/biometrics.ts index d22e9093d..684935b98 100644 --- a/class/biometrics.ts +++ b/class/biometrics.ts @@ -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; - isDeviceBiometricCapable: () => Promise; - setBiometricUseEnabled: (arg: boolean) => Promise; - biometricType: () => Promise; - isBiometricUseEnabled: () => Promise; - unlockWithBiometrics: () => Promise; - 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 }; diff --git a/components/addresses/AddressItem.tsx b/components/addresses/AddressItem.tsx index a6ce981f7..2fafc8761 100644 --- a/components/addresses/AddressItem.tsx +++ b/components/addresses/AddressItem.tsx @@ -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; } } diff --git a/hooks/useBiometrics.ts b/hooks/useBiometrics.ts new file mode 100644 index 000000000..7de7da6bc --- /dev/null +++ b/hooks/useBiometrics.ts @@ -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 }; diff --git a/hooks/useExtendedNavigation.ts b/hooks/useExtendedNavigation.ts index 6eb612c8d..3720789de 100644 --- a/hooks/useExtendedNavigation.ts +++ b/hooks/useExtendedNavigation.ts @@ -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 => { const originalNavigation = useNavigation>(); - const { wallets, saveToDisk } = useContext(BlueStorageContext); + const { wallets, saveToDisk } = useStorage(); + const { isBiometricUseEnabled, unlockWithBiometrics } = useBiometrics(); const enhancedNavigate: NavigationProp['navigate'] = (screenOrOptions: any, params?: any) => { let screenName: string; @@ -42,9 +42,9 @@ export const useExtendedNavigation = (): NavigationProp => { (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 diff --git a/screen/UnlockWith.tsx b/screen/UnlockWith.tsx index b4f032562..2d3e800ad 100644 --- a/screen/UnlockWith.tsx +++ b/screen/UnlockWith.tsx @@ -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(); } diff --git a/screen/lnd/ldkOpenChannel.tsx b/screen/lnd/ldkOpenChannel.tsx index a6186ae31..318cda0bd 100644 --- a/screen/lnd/ldkOpenChannel.tsx +++ b/screen/lnd/ldkOpenChannel.tsx @@ -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; } diff --git a/screen/lnd/lnurlPay.js b/screen/lnd/lnurlPay.js index 120d6f078..7b6a54bd5 100644 --- a/screen/lnd/lnurlPay.js +++ b/screen/lnd/lnurlPay.js @@ -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; } } diff --git a/screen/lnd/scanLndInvoice.js b/screen/lnd/scanLndInvoice.js index e343f7e74..550607811 100644 --- a/screen/lnd/scanLndInvoice.js +++ b/screen/lnd/scanLndInvoice.js @@ -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; } } diff --git a/screen/send/confirm.js b/screen/send/confirm.js index e95393583..e4354c6e7 100644 --- a/screen/send/confirm.js +++ b/screen/send/confirm.js @@ -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; } } diff --git a/screen/send/psbtWithHardwareWallet.js b/screen/send/psbtWithHardwareWallet.js index ea5ce92a2..33fde1f1e 100644 --- a/screen/send/psbtWithHardwareWallet.js +++ b/screen/send/psbtWithHardwareWallet.js @@ -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; } diff --git a/screen/settings/encryptStorage.js b/screen/settings/encryptStorage.js index 5d668eb5d..7f3fe6c06 100644 --- a/screen/settings/encryptStorage.js +++ b/screen/settings/encryptStorage.js @@ -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 ? ( <> - {loc.formatString(loc.settings.biometrics_fail, { type: biometrics.biometricsType })} + {loc.formatString(loc.settings.biometrics_fail, { type: deviceBiometricType })} ) : null; }; @@ -147,18 +136,18 @@ const EncryptStorage = () => { ) : ( - {biometrics.isDeviceBiometricCapable && ( + {isDeviceBiometricCapable && ( <> {loc.settings.biometrics} - {loc.formatString(loc.settings.encrypt_use_expl, { type: biometrics.biometricsType })} + {loc.formatString(loc.settings.encrypt_use_expl, { type: deviceBiometricType })} {renderPasscodeExplanation()} diff --git a/screen/wallets/ViewEditMultisigCosigners.tsx b/screen/wallets/ViewEditMultisigCosigners.tsx index e96f46870..38d8e04ed 100644 --- a/screen/wallets/ViewEditMultisigCosigners.tsx +++ b/screen/wallets/ViewEditMultisigCosigners.tsx @@ -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; } diff --git a/screen/wallets/details.js b/screen/wallets/details.js index e9f8f6ea6..66722f7bf 100644 --- a/screen/wallets/details.js +++ b/screen/wallets/details.js @@ -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; } } diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index 582691238..ed59cc245 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -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'); }