From ec5bc4a3c6f58be20e59b8fd8942c6a28b6dd8c7 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Date: Tue, 24 Dec 2019 21:35:47 -0600 Subject: [PATCH] ADD: Ask user if they have backed up their seed phrase --- BlueComponents.js | 16 +++++ class/abstract-wallet.js | 9 +++ loc/es.js | 2 +- screen/lnd/lndCreateInvoice.js | 33 +++++++++- screen/receive/details.js | 117 +++++++++++++++++++-------------- screen/wallets/export.js | 12 +++- screen/wallets/import.js | 1 + screen/wallets/transactions.js | 20 +++++- 8 files changed, 151 insertions(+), 59 deletions(-) diff --git a/BlueComponents.js b/BlueComponents.js index 1f9a15520..2a5d577a3 100644 --- a/BlueComponents.js +++ b/BlueComponents.js @@ -7,6 +7,7 @@ import { TouchableOpacity, TouchableWithoutFeedback, Animated, + Alert, ActivityIndicator, View, KeyboardAvoidingView, @@ -375,6 +376,21 @@ export class BlueButtonLink extends Component { } } +export const BlueAlertWalletExportReminder = ({ onSuccess = () => {}, onFailure }) => { + Alert.alert( + 'Wallet', + `Have your saved your wallet's backup phrase? This backup phrase is required to access your funds in case you lose this device. Without the backup phrase, your funds will be permanently lost.`, + [ + { text: 'Yes, I have', onPress: onSuccess, style: 'cancel' }, + { + text: 'No, I have not', + onPress: onFailure, + }, + ], + { cancelable: false }, + ); +}; + export const BlueNavigationStyle = (navigation, withNavigationCloseButton = false, customCloseButtonFunction = undefined) => ({ headerStyle: { backgroundColor: BlueApp.settings.brandingColor, diff --git a/class/abstract-wallet.js b/class/abstract-wallet.js index eea6540d8..bf0eea6e8 100644 --- a/class/abstract-wallet.js +++ b/class/abstract-wallet.js @@ -29,6 +29,7 @@ export class AbstractWallet { this.preferredBalanceUnit = BitcoinUnit.BTC; this.chain = Chain.ONCHAIN; this.hideBalance = false; + this.userHasSavedExport = false; } getID() { @@ -42,6 +43,14 @@ export class AbstractWallet { return this.transactions; } + getUserHasSavedExport() { + return this.userHasSavedExport; + } + + setUserHasSavedExport(value) { + this.userHasSavedExport = value; + } + /** * * @returns {string} diff --git a/loc/es.js b/loc/es.js index edaa2bde3..89eaf0f37 100644 --- a/loc/es.js +++ b/loc/es.js @@ -18,7 +18,7 @@ module.exports = { header: 'Un Monedero esta representado con secreto (clave privada) y una dirección' + 'que puedes compartir para recibir monedas.', add: 'Añadir Carterqa', create_a_wallet: 'Crear una billetera', - create_a_wallet1: 'Es gratis y puedes crear cuantas deseas', + create_a_wallet1: 'Es gratis y puedes crear', create_a_wallet2: 'cuantas usted quiera', latest_transaction: 'última transacción', empty_txs1: 'Sus transacciones aparecerán aquí,', diff --git a/screen/lnd/lndCreateInvoice.js b/screen/lnd/lndCreateInvoice.js index 65415be1a..1ce9ee5ef 100644 --- a/screen/lnd/lndCreateInvoice.js +++ b/screen/lnd/lndCreateInvoice.js @@ -10,7 +10,13 @@ import { TouchableOpacity, Text, } from 'react-native'; -import { BlueNavigationStyle, BlueButton, BlueBitcoinAmount, BlueDismissKeyboardInputAccessory } from '../../BlueComponents'; +import { + BlueNavigationStyle, + BlueButton, + BlueBitcoinAmount, + BlueDismissKeyboardInputAccessory, + BlueAlertWalletExportReminder, +} from '../../BlueComponents'; import { LightningCustodianWallet } from '../../class/lightning-custodian-wallet'; import PropTypes from 'prop-types'; import bech32 from 'bech32'; @@ -47,16 +53,36 @@ export default class LNDCreateInvoice extends Component { fromWallet, amount: '', description: '', - isLoading: false, lnurl: '', lnurlParams: null, + isLoading: true, }; } - componentDidMount() { + renderReceiveDetails = async () => { + this.state.fromWallet.setUserHasSavedExport(true); + await BlueApp.saveToDisk(); if (this.props.navigation.state.params.uri) { this.processLnurl(this.props.navigation.getParam('uri')); } + this.setState({ isLoading: false }); + }; + + componentDidMount() { + if (this.state.fromWallet.getUserHasSavedExport()) { + this.renderReceiveDetails(); + } else { + BlueAlertWalletExportReminder({ + onSuccess: this.renderReceiveDetails, + onFailure: () => { + this.props.navigation.dismiss(); + this.props.navigation.navigate('WalletExport', { + address: this.state.fromWallet.getAddress(), + secret: this.state.fromWallet.getSecret(), + }); + }, + }); + } } async createInvoice() { @@ -266,6 +292,7 @@ export default class LNDCreateInvoice extends Component { LNDCreateInvoice.propTypes = { navigation: PropTypes.shape({ goBack: PropTypes.func, + dismiss: PropTypes.func, navigate: PropTypes.func, getParam: PropTypes.func, state: PropTypes.shape({ diff --git a/screen/receive/details.js b/screen/receive/details.js index d5f58496a..68a671587 100644 --- a/screen/receive/details.js +++ b/screen/receive/details.js @@ -13,6 +13,7 @@ import { BlueBitcoinAmount, BlueText, BlueSpacing20, + BlueAlertWalletExportReminder, } from '../../BlueComponents'; import PropTypes from 'prop-types'; import Privacy from '../../Privacy'; @@ -45,64 +46,78 @@ export default class ReceiveDetails extends Component { }; } - async componentDidMount() { - Privacy.enableBlur(); - console.log('receive/details - componentDidMount'); - - { - let address; - let wallet; - for (let w of BlueApp.getWallets()) { - if (w.getSecret() === this.state.secret) { - // found our wallet - wallet = w; - } - } - if (wallet) { - if (wallet.getAddressAsync) { - if (wallet.chain === Chain.ONCHAIN) { - try { - address = await Promise.race([wallet.getAddressAsync(), BlueApp.sleep(1000)]); - } catch (_) {} - if (!address) { - // either sleep expired or getAddressAsync threw an exception - console.warn('either sleep expired or getAddressAsync threw an exception'); - address = wallet._getExternalAddressByIndex(wallet.next_free_address_index); - } else { - BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally - } - this.setState({ - address: address, - }); - } else if (wallet.chain === Chain.OFFCHAIN) { - try { - await Promise.race([wallet.getAddressAsync(), BlueApp.sleep(1000)]); - address = wallet.getAddress(); - } catch (_) {} - if (!address) { - // either sleep expired or getAddressAsync threw an exception - console.warn('either sleep expired or getAddressAsync threw an exception'); - address = wallet.getAddress(); - } else { - BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally - } - } - console.warn(address) - this.setState({ - address: address, - }); - } else if (wallet.getAddress) { - this.setState({ - address: wallet.getAddress(), - }); + renderReceiveDetails = async () => { + this.wallet.setUserHasSavedExport(true); + await BlueApp.saveToDisk(); + let address; + if (this.wallet.getAddressAsync) { + if (this.wallet.chain === Chain.ONCHAIN) { + try { + address = await Promise.race([this.wallet.getAddressAsync(), BlueApp.sleep(1000)]); + } catch (_) {} + if (!address) { + // either sleep expired or getAddressAsync threw an exception + console.warn('either sleep expired or getAddressAsync threw an exception'); + address = this.wallet._getExternalAddressByIndex(this.wallet.next_free_address_index); + } else { + BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally + } + this.setState({ + address: address, + }); + } else if (this.wallet.chain === Chain.OFFCHAIN) { + try { + await Promise.race([this.wallet.getAddressAsync(), BlueApp.sleep(1000)]); + address = this.wallet.getAddress(); + } catch (_) {} + if (!address) { + // either sleep expired or getAddressAsync threw an exception + console.warn('either sleep expired or getAddressAsync threw an exception'); + address = this.wallet.getAddress(); + } else { + BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally } } + this.setState({ + address: address, + }); + } else if (this.wallet.getAddress) { + this.setState({ + address: this.wallet.getAddress(), + }); } - InteractionManager.runAfterInteractions(async () => { const bip21encoded = bip21.encode(this.state.address); this.setState({ bip21encoded }); }); + }; + + componentDidMount() { + Privacy.enableBlur(); + console.log('receive/details - componentDidMount'); + + for (let w of BlueApp.getWallets()) { + if (w.getSecret() === this.state.secret) { + // found our wallet + this.wallet = w; + } + } + if (this.wallet) { + if (!this.wallet.getUserHasSavedExport()) { + BlueAlertWalletExportReminder({ + onSuccess: this.renderReceiveDetails, + onFailure: () => { + this.props.navigation.goBack(); + this.props.navigation.navigate('WalletExport', { + address: this.wallet.getAddress(), + secret: this.wallet.getSecret(), + }); + }, + }); + } else { + this.renderReceiveDetails(); + } + } } componentWillUnmount() { diff --git a/screen/wallets/export.js b/screen/wallets/export.js index 9c00240ec..e2655f418 100644 --- a/screen/wallets/export.js +++ b/screen/wallets/export.js @@ -48,9 +48,15 @@ export default class WalletExport extends Component { } } - this.setState({ - isLoading: false, - }); + this.setState( + { + isLoading: false, + }, + () => { + this.state.wallet.setUserHasSavedExport(true); + BlueApp.saveToDisk(); + }, + ); } async componentWillUnmount() { diff --git a/screen/wallets/import.js b/screen/wallets/import.js index e952200a5..ab590a75c 100644 --- a/screen/wallets/import.js +++ b/screen/wallets/import.js @@ -65,6 +65,7 @@ export default class WalletsImport extends Component { alert(loc.wallets.import.success); ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false }); w.setLabel(loc.wallets.import.imported + ' ' + w.typeReadable); + w.setUserHasSavedExport(true); BlueApp.wallets.push(w); await BlueApp.saveToDisk(); EV(EV.enum.WALLETS_COUNT_CHANGED); diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js index 0a279498b..b1ef7ba0c 100644 --- a/screen/wallets/transactions.js +++ b/screen/wallets/transactions.js @@ -25,6 +25,7 @@ import { BlueReceiveButtonIcon, BlueTransactionListItem, BlueWalletNavigationHeader, + BlueAlertWalletExportReminder, } from '../../BlueComponents'; import WalletGradient from '../../class/walletGradient'; import { Icon } from 'react-native-elements'; @@ -433,7 +434,24 @@ export default class WalletTransactions extends Component { this.setState({ wallet }, () => InteractionManager.runAfterInteractions(() => BlueApp.saveToDisk())); }) } - onManageFundsPressed={() => this.setState({ isManageFundsModalVisible: true })} + onManageFundsPressed={() => { + if (this.state.wallet.getUserHasSavedExport()) { + this.setState({ isManageFundsModalVisible: true }); + } else { + BlueAlertWalletExportReminder({ + onSuccess: async () => { + this.state.wallet.setUserHasSavedExport(true); + await BlueApp.saveToDisk(); + this.setState({ isManageFundsModalVisible: true }); + }, + onFailure: () => + this.props.navigation.navigate('WalletExport', { + address: this.state.wallet.getAddress(), + secret: this.state.wallet.getSecret(), + }), + }); + } + }} />