mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-20 14:05:27 +01:00
ADD: Biometrics
FIX: Added FaceID Usage description ADD: Use Biometrics for Show balance. ADD: Unlock With Boot Screen FIX: Allow the use of Biometrics after decrypt FIX: Build system revert. FIX: Remove biometric from receive address. ADD: Biometric for export FIX: Use RNSecureKeyStore for biometrics FIX: Realign views Update biometrics.js
This commit is contained in:
parent
461a8e24f7
commit
29ce0271f8
23 changed files with 302 additions and 42 deletions
|
@ -33,6 +33,7 @@ import ToolTip from 'react-native-tooltip';
|
|||
import { BlurView } from '@react-native-community/blur';
|
||||
import showPopupMenu from 'react-native-popup-menu-android';
|
||||
import NetworkTransactionFees, { NetworkTransactionFeeType } from './models/networkTransactionFees';
|
||||
import Biometric from './class/biometrics';
|
||||
let loc = require('./loc/');
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('./BlueApp');
|
||||
|
@ -179,6 +180,15 @@ export class BlueWalletNavigationHeader extends Component {
|
|||
|
||||
handleBalanceVisibility = async _item => {
|
||||
const wallet = this.state.wallet;
|
||||
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled && wallet.hideBalance) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return this.props.navigation.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
wallet.hideBalance = !wallet.hideBalance;
|
||||
this.setState({ wallet });
|
||||
await BlueApp.saveToDisk();
|
||||
|
|
97
UnlockWith.js
Normal file
97
UnlockWith.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
import React, { Component } from 'react';
|
||||
import { View, Image, TouchableOpacity } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import Biometric from './class/biometrics';
|
||||
import PropTypes from 'prop-types';
|
||||
import Biometrics from 'react-native-biometrics';
|
||||
import { SafeAreaView } from 'react-navigation';
|
||||
/** @type {AppStorage} */
|
||||
|
||||
const BlueApp = require('./BlueApp');
|
||||
|
||||
export default class UnlockWith extends Component {
|
||||
state = { biometricType: false, isStorageEncrypted: false, isAuthenticating: false };
|
||||
|
||||
async componentDidMount() {
|
||||
let biometricType = false;
|
||||
const isStorageEncrypted = await BlueApp.storageIsEncrypted();
|
||||
if ((await Biometric.isBiometricUseCapableAndEnabled()) && !isStorageEncrypted) {
|
||||
biometricType = await Biometric.biometricType();
|
||||
}
|
||||
this.setState({ biometricType, isStorageEncrypted }, async () => {
|
||||
if (!biometricType) {
|
||||
await BlueApp.startAndDecrypt();
|
||||
this.props.onSuccessfullyAuthenticated();
|
||||
} else if (biometricType !== false && !isStorageEncrypted) this.unlockWithBiometrics();
|
||||
});
|
||||
}
|
||||
|
||||
successfullyAuthenticated = () => {
|
||||
this.props.onSuccessfullyAuthenticated();
|
||||
};
|
||||
|
||||
unlockWithBiometrics = () => {
|
||||
this.setState({ isAuthenticating: true }, async () => {
|
||||
if (await Biometric.unlockWithBiometrics()) {
|
||||
await BlueApp.startAndDecrypt();
|
||||
return this.props.onSuccessfullyAuthenticated();
|
||||
}
|
||||
this.setState({ isAuthenticating: false });
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.biometricType && !this.state.isStorageEncrypted) {
|
||||
return <View />;
|
||||
}
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<View style={{ flex: 2, justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
||||
<Image source={require('./img/qr-code.png')} style={{ width: 120, height: 120 }} />
|
||||
</View>
|
||||
<View style={{ flex: 0.1, justifyContent: 'flex-end', marginBottom: 64 }}>
|
||||
<View style={{ justifyContent: 'center', flexDirection: 'row' }}>
|
||||
{this.state.biometricType === Biometrics.TouchID && (
|
||||
<>
|
||||
<TouchableOpacity disabled={this.state.isAuthenticating} onPress={this.unlockWithBiometrics}>
|
||||
<Image source={require('./img/fingerprint.png')} style={{ width: 64, height: 64 }} />
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
{this.state.biometricType === Biometrics.FaceID && (
|
||||
<>
|
||||
<TouchableOpacity disabled={this.state.isAuthenticating} onPress={this.unlockWithBiometrics}>
|
||||
<Image source={require('./img/faceid.png')} style={{ width: 64, height: 64 }} />
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
{this.state.biometricType !== false && this.state.isStorageEncrypted && (
|
||||
<View style={{ backgroundColor: 'gray', width: 0.5, height: 20, marginHorizontal: 16 }} />
|
||||
)}
|
||||
{this.state.isStorageEncrypted && (
|
||||
<>
|
||||
<TouchableOpacity
|
||||
disabled={this.state.isAuthenticating}
|
||||
onPress={() => {
|
||||
this.setState({ isAuthenticating: true }, async () => {
|
||||
await BlueApp.startAndDecrypt();
|
||||
this.props.onSuccessfullyAuthenticated();
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Icon name="key" size={64} type="font-awesome" />
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UnlockWith.propTypes = {
|
||||
onSuccessfullyAuthenticated: PropTypes.func,
|
||||
};
|
|
@ -118,7 +118,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "4.5.0"
|
||||
versionName "4.5.1"
|
||||
multiDexEnabled true
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
}
|
||||
|
|
51
class/biometrics.js
Normal file
51
class/biometrics.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import Biometrics from 'react-native-biometrics';
|
||||
const BlueApp = require('../BlueApp');
|
||||
|
||||
export default class Biometric {
|
||||
static STORAGEKEY = 'Biometrics';
|
||||
|
||||
static async isDeviceBiometricCapable() {
|
||||
if ((await Biometrics.isSensorAvailable()) !== null) {
|
||||
return true;
|
||||
}
|
||||
Biometric.setBiometricUseEnabled(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
static async biometricType() {
|
||||
return Biometrics.isSensorAvailable();
|
||||
}
|
||||
|
||||
static async isBiometricUseEnabled() {
|
||||
try {
|
||||
const enabledBiometrics = await BlueApp.getItem(Biometric.STORAGEKEY);
|
||||
return !!enabledBiometrics;
|
||||
} catch (_e) {
|
||||
await BlueApp.setItem(Biometric.STORAGEKEY, '');
|
||||
return !!'';
|
||||
}
|
||||
}
|
||||
|
||||
static async isBiometricUseCapableAndEnabled() {
|
||||
const isBiometricUseEnabled = await Biometric.isBiometricUseEnabled();
|
||||
const isDeviceBiometricCapable = await Biometric.isDeviceBiometricCapable();
|
||||
return !!isBiometricUseEnabled && isDeviceBiometricCapable;
|
||||
}
|
||||
|
||||
static async setBiometricUseEnabled(value) {
|
||||
await BlueApp.setItem(Biometric.STORAGEKEY, value === true ? '1' : '');
|
||||
}
|
||||
|
||||
static async unlockWithBiometrics() {
|
||||
const isDeviceBiometricCapable = await Biometric.isDeviceBiometricCapable();
|
||||
if (isDeviceBiometricCapable) {
|
||||
try {
|
||||
await Biometrics.simplePrompt('Please confirm your identity.');
|
||||
return true;
|
||||
} catch (_e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
BIN
img/faceid.png
Normal file
BIN
img/faceid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
BIN
img/fingerprint.png
Normal file
BIN
img/fingerprint.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
13
index.js
13
index.js
|
@ -2,15 +2,15 @@ import 'intl';
|
|||
import 'intl/locale-data/jsonp/en';
|
||||
import React from 'react';
|
||||
import './shim.js';
|
||||
import App from './App';
|
||||
import { Sentry } from 'react-native-sentry';
|
||||
import { AppRegistry } from 'react-native';
|
||||
import WalletMigrate from './screen/wallets/walletMigrate';
|
||||
import { name as appName } from './app.json';
|
||||
import App from './App';
|
||||
import LottieView from 'lottie-react-native';
|
||||
import UnlockWith from './UnlockWith.js';
|
||||
|
||||
/** @type {AppStorage} */
|
||||
const BlueApp = require('./BlueApp');
|
||||
let A = require('./analytics');
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
Sentry.config('https://23377936131848ca8003448a893cb622@sentry.io/1295736').install();
|
||||
|
@ -24,7 +24,7 @@ if (!Error.captureStackTrace) {
|
|||
class BlueAppComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { isMigratingData: true, onAnimationFinished: false };
|
||||
this.state = { isMigratingData: true, onAnimationFinished: false, successfullyAuthenticated: false };
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -33,7 +33,6 @@ class BlueAppComponent extends React.Component {
|
|||
}
|
||||
|
||||
setIsMigratingData = async () => {
|
||||
await BlueApp.startAndDecrypt();
|
||||
A(A.ENUM.INIT);
|
||||
this.setState({ isMigratingData: false });
|
||||
};
|
||||
|
@ -46,6 +45,10 @@ class BlueAppComponent extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
onSuccessfullyAuthenticated = () => {
|
||||
this.setState({ successfullyAuthenticated: true })
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.isMigratingData) {
|
||||
return (
|
||||
|
@ -59,7 +62,7 @@ class BlueAppComponent extends React.Component {
|
|||
);
|
||||
} else {
|
||||
if (this.state.onAnimationFinished) {
|
||||
return <App />;
|
||||
return this.state.successfullyAuthenticated ? <App /> : <UnlockWith onSuccessfullyAuthenticated={this.onSuccessfullyAuthenticated} />;
|
||||
} else {
|
||||
return (
|
||||
<LottieView
|
||||
|
|
|
@ -630,7 +630,7 @@
|
|||
13B07F861A680F5B00A75B9A = {
|
||||
DevelopmentTeam = A7W54YZ4WU;
|
||||
LastSwiftMigration = 1030;
|
||||
ProvisioningStyle = Manual;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.Keychain = {
|
||||
enabled = 0;
|
||||
|
@ -1132,11 +1132,11 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = BlueWallet/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
|
@ -1152,7 +1152,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet;
|
||||
PRODUCT_NAME = BlueWallet;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "io.bluewallet.bluewallet AppStore";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "BlueWallet-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.2;
|
||||
|
@ -1167,10 +1167,10 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = BlueWallet/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
|
@ -1186,7 +1186,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet;
|
||||
PRODUCT_NAME = BlueWallet;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "io.bluewallet.bluewallet AppStore";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "BlueWallet-Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
|
|
@ -27,6 +27,15 @@
|
|||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "BlueWallet.app"
|
||||
BlueprintName = "BlueWallet"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
|
@ -39,17 +48,6 @@
|
|||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "BlueWallet.app"
|
||||
BlueprintName = "BlueWallet"
|
||||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
@ -71,8 +69,6 @@
|
|||
ReferencedContainer = "container:BlueWallet.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -17,7 +17,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.5.0</string>
|
||||
<string>4.5.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
@ -56,6 +56,8 @@
|
|||
</dict>
|
||||
<key>NSAppleMusicUsageDescription</key>
|
||||
<string>This alert should not show up as we do not require this data</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>In order to confirm your identity, we need your permission to use FaceID.</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<string>This alert should not show up as we do not require this data</string>
|
||||
<key>NSCalendarsUsageDescription</key>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.5.0</string>
|
||||
<string>4.5.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>239</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.5.0</string>
|
||||
<string>4.5.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>239</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
|
|
@ -84,6 +84,8 @@ PODS:
|
|||
- React-cxxreact (= 0.60.5)
|
||||
- React-jsi (= 0.60.5)
|
||||
- React-jsinspector (0.60.5)
|
||||
- react-native-biometrics (1.6.1):
|
||||
- React
|
||||
- react-native-blur (0.8.0):
|
||||
- React
|
||||
- react-native-camera (3.4.0):
|
||||
|
@ -178,6 +180,7 @@ DEPENDENCIES:
|
|||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- react-native-biometrics (from `../node_modules/react-native-biometrics`)
|
||||
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
|
||||
- react-native-camera (from `../node_modules/react-native-camera`)
|
||||
- react-native-haptic-feedback (from `../node_modules/react-native-haptic-feedback`)
|
||||
|
@ -253,6 +256,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||
React-jsinspector:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||
react-native-biometrics:
|
||||
:path: "../node_modules/react-native-biometrics"
|
||||
react-native-blur:
|
||||
:path: "../node_modules/@react-native-community/blur"
|
||||
react-native-camera:
|
||||
|
@ -338,6 +343,7 @@ SPEC CHECKSUMS:
|
|||
React-jsi: 4d8c9efb6312a9725b18d6fc818ffc103f60fec2
|
||||
React-jsiexecutor: 90ad2f9db09513fc763bc757fdc3c4ff8bde2a30
|
||||
React-jsinspector: e08662d1bf5b129a3d556eb9ea343a3f40353ae4
|
||||
react-native-biometrics: 4aaf49f9f8bd28c6aa3ec53534ca1b6b00486f6a
|
||||
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
|
||||
react-native-camera: 203091b4bf99d48b788a0682ad573e8718724893
|
||||
react-native-haptic-feedback: 22c9dc85fd8059f83bf9edd9212ac4bd4ae6074d
|
||||
|
|
9
package-lock.json
generated
9
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "BlueWallet",
|
||||
"version": "4.5.0",
|
||||
"version": "4.5.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -10686,6 +10686,11 @@
|
|||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"react-native-biometrics": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-biometrics/-/react-native-biometrics-1.6.1.tgz",
|
||||
"integrity": "sha512-WYdZ6k3Miyl/+lEdhq6y4B6wz16d+PomO/xjR1hScYKeQWj1jaugPy0Ep+EjoWpJkPYLvnxasmIYR9v8OvWNHg=="
|
||||
},
|
||||
"react-native-camera": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.4.0.tgz",
|
||||
|
@ -13293,4 +13298,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "BlueWallet",
|
||||
"version": "4.5.0",
|
||||
"version": "4.5.1",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.0",
|
||||
"@babel/runtime": "^7.5.1",
|
||||
|
@ -84,6 +84,7 @@
|
|||
"react": "16.8.6",
|
||||
"react-localization": "1.0.13",
|
||||
"react-native": "0.60.5",
|
||||
"react-native-biometrics": "^1.6.1",
|
||||
"react-native-camera": "3.4.0",
|
||||
"react-native-device-info": "2.2.2",
|
||||
"react-native-elements": "0.19.0",
|
||||
|
|
|
@ -15,6 +15,7 @@ import { LightningCustodianWallet } from '../../class/lightning-custodian-wallet
|
|||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import Biometric from '../../class/biometrics';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../../BlueApp');
|
||||
let EV = require('../../events');
|
||||
|
@ -143,6 +144,14 @@ export default class ScanLndInvoice extends React.Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
isLoading: true,
|
||||
|
|
|
@ -6,6 +6,7 @@ import { BlueButton, BlueText, SafeBlueArea, BlueCard, BlueSpacing40, BlueNaviga
|
|||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import Biometric from '../../class/biometrics';
|
||||
let loc = require('../../loc');
|
||||
let EV = require('../../events');
|
||||
let currency = require('../../currency');
|
||||
|
@ -46,6 +47,15 @@ export default class Confirm extends Component {
|
|||
try {
|
||||
await BlueElectrum.ping();
|
||||
await BlueElectrum.waitTillConnected();
|
||||
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let result = await this.state.fromWallet.broadcastTx(this.state.tx);
|
||||
if (result && result.code) {
|
||||
if (result.code === 1) {
|
||||
|
@ -163,7 +173,15 @@ export default class Confirm extends Component {
|
|||
|
||||
<TouchableOpacity
|
||||
style={{ marginVertical: 24 }}
|
||||
onPress={() =>
|
||||
onPress={async () => {
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.props.navigation.navigate('CreateTransaction', {
|
||||
fee: this.state.fee,
|
||||
recipients: this.state.recipients,
|
||||
|
@ -172,8 +190,8 @@ export default class Confirm extends Component {
|
|||
satoshiPerByte: this.state.satoshiPerByte,
|
||||
wallet: this.state.fromWallet,
|
||||
feeSatoshi: this.state.feeSatoshi,
|
||||
})
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#0c2550', fontSize: 15, fontWeight: '500', alignSelf: 'center' }}>
|
||||
{loc.transactions.details.transaction_details}
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import PropTypes from 'prop-types';
|
||||
import { AppStorage } from '../../class';
|
||||
import Biometric from '../../class/biometrics';
|
||||
let loc = require('../../loc');
|
||||
|
||||
export default class Settings extends Component {
|
||||
|
@ -24,14 +25,19 @@ export default class Settings extends Component {
|
|||
this.state = {
|
||||
isLoading: true,
|
||||
language: loc.getLanguage(),
|
||||
biometrics: { isDeviceBiometricCapable: false, isBiometricsEnabled: false },
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
let advancedModeEnabled = !!(await AsyncStorage.getItem(AppStorage.ADVANCED_MODE_ENABLED));
|
||||
const advancedModeEnabled = !!(await AsyncStorage.getItem(AppStorage.ADVANCED_MODE_ENABLED));
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseEnabled();
|
||||
const isDeviceBiometricCapable = await Biometric.isDeviceBiometricCapable();
|
||||
const biometricsType = (await Biometric.biometricType()) || 'biometrics';
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
advancedModeEnabled,
|
||||
biometrics: { isBiometricsEnabled, isDeviceBiometricCapable, biometricsType },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -44,6 +50,15 @@ export default class Settings extends Component {
|
|||
this.setState({ advancedModeEnabled: value });
|
||||
}
|
||||
|
||||
onUseBiometricSwitch = async value => {
|
||||
let isBiometricsEnabled = this.state.biometrics;
|
||||
if (await Biometric.unlockWithBiometrics()) {
|
||||
isBiometricsEnabled.isBiometricsEnabled = value;
|
||||
await Biometric.setBiometricUseEnabled(value);
|
||||
this.setState({ biometrics: isBiometricsEnabled });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.isLoading) {
|
||||
return <BlueLoading />;
|
||||
|
@ -56,6 +71,15 @@ export default class Settings extends Component {
|
|||
<TouchableOpacity onPress={() => this.props.navigation.navigate('EncryptStorage')}>
|
||||
<BlueListItem title={loc.settings.encrypt_storage} />
|
||||
</TouchableOpacity>
|
||||
{this.state.biometrics.isDeviceBiometricCapable && (
|
||||
<BlueListItem
|
||||
hideChevron
|
||||
title={`Use ${this.state.biometrics.biometricsType}`}
|
||||
switchButton
|
||||
onSwitch={this.onUseBiometricSwitch}
|
||||
switched={this.state.biometrics.isBiometricsEnabled}
|
||||
/>
|
||||
)}
|
||||
<TouchableOpacity onPress={() => this.props.navigation.navigate('LightningSettings')}>
|
||||
<BlueListItem title={loc.settings.lightning_settings} />
|
||||
</TouchableOpacity>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { HDLegacyP2PKHWallet } from '../../class/hd-legacy-p2pkh-wallet';
|
|||
import { HDSegwitP2SHWallet } from '../../class/hd-segwit-p2sh-wallet';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import { HDSegwitBech32Wallet } from '../../class';
|
||||
import Biometric from '../../class/biometrics';
|
||||
let EV = require('../../events');
|
||||
let prompt = require('../../prompt');
|
||||
/** @type {AppStorage} */
|
||||
|
@ -75,6 +76,14 @@ export default class WalletDetails extends Component {
|
|||
'plain-text',
|
||||
);
|
||||
if (Number(walletBalanceConfirmation) === this.state.wallet.getBalance()) {
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.props.navigation.setParams({ isLoading: true });
|
||||
this.setState({ isLoading: true }, async () => {
|
||||
BlueApp.deleteWallet(this.state.wallet);
|
||||
|
|
|
@ -5,6 +5,7 @@ import { BlueSpacing20, SafeBlueArea, BlueNavigationStyle, BlueText } from '../.
|
|||
import PropTypes from 'prop-types';
|
||||
import Privacy from '../../Privacy';
|
||||
import SystemSetting from 'react-native-system-setting';
|
||||
import Biometric from '../../class/biometrics';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../../BlueApp');
|
||||
let loc = require('../../loc');
|
||||
|
@ -39,11 +40,20 @@ export default class WalletExport extends Component {
|
|||
|
||||
async componentDidMount() {
|
||||
Privacy.enableBlur();
|
||||
|
||||
await SystemSetting.saveBrightness();
|
||||
await SystemSetting.setAppBrightness(1.0);
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return this.props.navigation.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
});
|
||||
await SystemSetting.saveBrightness();
|
||||
await SystemSetting.setAppBrightness(1.0);
|
||||
}
|
||||
|
||||
async componentWillUnmount() {
|
||||
|
|
|
@ -85,6 +85,7 @@ export default class WalletMigrate {
|
|||
const data = await AsyncStorage.getItem('data');
|
||||
if (data) {
|
||||
const isEncrypted = (await AsyncStorage.getItem('data_encrypted')) || '';
|
||||
await RNSecureKeyStore.set('Biometrics', '', { accessible: ACCESSIBLE.WHEN_UNLOCKED });
|
||||
await RNSecureKeyStore.set('data', data, { accessible: ACCESSIBLE.WHEN_UNLOCKED });
|
||||
await RNSecureKeyStore.set('data_encrypted', isEncrypted, {
|
||||
accessible: ACCESSIBLE.WHEN_UNLOCKED,
|
||||
|
|
|
@ -5,6 +5,7 @@ import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle, BlueCopyTex
|
|||
import PropTypes from 'prop-types';
|
||||
import Privacy from '../../Privacy';
|
||||
import SystemSetting from 'react-native-system-setting';
|
||||
import Biometric from '../../class/biometrics';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../../BlueApp');
|
||||
let loc = require('../../loc');
|
||||
|
@ -41,11 +42,20 @@ export default class WalletXpub extends Component {
|
|||
|
||||
async componentDidMount() {
|
||||
Privacy.enableBlur();
|
||||
await SystemSetting.saveBrightness();
|
||||
await SystemSetting.setAppBrightness(1.0);
|
||||
|
||||
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||
|
||||
if (isBiometricsEnabled) {
|
||||
if (!(await Biometric.unlockWithBiometrics())) {
|
||||
return this.props.navigation.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
});
|
||||
await SystemSetting.saveBrightness();
|
||||
await SystemSetting.setAppBrightness(1.0);
|
||||
}
|
||||
|
||||
async componentWillUnmount() {
|
||||
|
|
Loading…
Add table
Reference in a new issue