Merge branch 'master' into biometrics

This commit is contained in:
Marcos Rodriguez 2019-10-01 15:24:55 -04:00
commit c930e818ac
7 changed files with 204 additions and 166 deletions

View file

@ -244,6 +244,10 @@ export class BlueWalletNavigationHeader extends Component {
}); });
} }
manageFundsPressed = () => {
this.props.onManageFundsPressed();
};
render() { render() {
return ( return (
<LinearGradient <LinearGradient
@ -310,7 +314,7 @@ export class BlueWalletNavigationHeader extends Component {
)} )}
</TouchableOpacity> </TouchableOpacity>
{this.state.wallet.type === LightningCustodianWallet.type && ( {this.state.wallet.type === LightningCustodianWallet.type && (
<TouchableOpacity onPress={() => NavigationService.navigate('ManageFunds', { fromWallet: this.state.wallet })}> <TouchableOpacity onPress={this.manageFundsPressed}>
<View <View
style={{ style={{
marginTop: 14, marginTop: 14,

View file

@ -41,7 +41,6 @@ import sendCreate from './screen/send/create';
import Confirm from './screen/send/confirm'; import Confirm from './screen/send/confirm';
import Success from './screen/send/success'; import Success from './screen/send/success';
import ManageFunds from './screen/lnd/manageFunds';
import ScanLndInvoice from './screen/lnd/scanLndInvoice'; import ScanLndInvoice from './screen/lnd/scanLndInvoice';
import LappBrowser from './screen/lnd/browser'; import LappBrowser from './screen/lnd/browser';
import LNDCreateInvoice from './screen/lnd/lndCreateInvoice'; import LNDCreateInvoice from './screen/lnd/lndCreateInvoice';
@ -170,21 +169,6 @@ const CreateTransactionStackNavigator = createStackNavigator({
}, },
}); });
const ManageFundsStackNavigator = createStackNavigator({
ManageFunds: {
screen: ManageFunds,
},
SelectWallet: {
screen: SelectWallet,
},
SendDetails: {
screen: CreateTransactionStackNavigator,
navigationOptions: {
header: null,
},
},
});
const LNDCreateInvoiceStackNavigator = createStackNavigator({ const LNDCreateInvoiceStackNavigator = createStackNavigator({
LNDCreateInvoice: { LNDCreateInvoice: {
screen: LNDCreateInvoice, screen: LNDCreateInvoice,
@ -260,6 +244,12 @@ const MainBottomTabs = createStackNavigator(
header: null, header: null,
}, },
}, },
SelectWallet: {
screen: SelectWallet,
navigationOptions: {
headerLeft: null,
}
},
// //
@ -275,12 +265,6 @@ const MainBottomTabs = createStackNavigator(
// LND: // LND:
ManageFunds: {
screen: ManageFundsStackNavigator,
navigationOptions: {
header: null,
},
},
ScanLndInvoice: { ScanLndInvoice: {
screen: LightningScanInvoiceStackNavigator, screen: LightningScanInvoiceStackNavigator,
navigationOptions: { navigationOptions: {

View file

@ -44,7 +44,11 @@ export class LightningCustodianWallet extends LegacyWallet {
} }
getAddress() { getAddress() {
return ''; if (this.refill_addressess.length > 0) {
return this.refill_addressess[0];
} else {
return undefined;
}
} }
getSecret() { getSecret() {
@ -358,6 +362,10 @@ export class LightningCustodianWallet extends LegacyWallet {
} }
} }
async getAddressAsync() {
return this.fetchBtcAddress();
}
getTransactions() { getTransactions() {
let txs = []; let txs = [];
this.pending_transactions_raw = this.pending_transactions_raw || []; this.pending_transactions_raw = this.pending_transactions_raw || [];

View file

@ -1,98 +0,0 @@
/* global alert */
import React, { Component } from 'react';
import { TouchableOpacity, Linking, View } from 'react-native';
import { BlueSpacingVariable, BlueNavigationStyle, SafeBlueArea, BlueCard } from '../../BlueComponents';
import { ListItem } from 'react-native-elements';
import PropTypes from 'prop-types';
import { Chain } from '../../models/bitcoinUnits';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
export default class ManageFunds extends Component {
static navigationOptions = ({ navigation }) => ({
...BlueNavigationStyle(navigation, true),
title: loc.lnd.title,
headerLeft: null,
});
constructor(props) {
super(props);
this.onWalletSelect = this.onWalletSelect.bind(this);
if (!props.navigation.getParam('fromWallet')) throw new Error('Invalid param');
this.state = { fromWallet: props.navigation.getParam('fromWallet') };
}
async onWalletSelect(wallet) {
this.props.navigation.dismiss();
/** @type {LightningCustodianWallet} */
let toAddress = false;
if (this.state.fromWallet.refill_addressess.length > 0) {
toAddress = this.state.fromWallet.refill_addressess[0];
} else {
try {
await this.state.fromWallet.fetchBtcAddress();
toAddress = this.state.fromWallet.refill_addressess[0];
} catch (Err) {
return alert(Err.message);
}
}
if (wallet) {
setTimeout(() => {
this.props.navigation.navigate('SendDetails', {
memo: loc.lnd.refill_lnd_balance,
fromSecret: wallet.getSecret(),
address: toAddress,
fromWallet: wallet,
});
}, 100);
} else {
return alert('Internal error');
}
}
render() {
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}>
<BlueSpacingVariable />
<BlueCard>
<ListItem
titleStyle={{ color: BlueApp.settings.foregroundColor }}
component={TouchableOpacity}
onPress={a => {
this.props.navigation.navigate('SelectWallet', { onWalletSelect: this.onWalletSelect, chainType: Chain.ONCHAIN });
}}
title={loc.lnd.refill}
/>
<ListItem
titleStyle={{ color: BlueApp.settings.foregroundColor }}
component={TouchableOpacity}
onPress={a => {
Linking.openURL('https://zigzag.io/?utm_source=integration&utm_medium=bluewallet&utm_campaign=withdrawLink');
}}
title={loc.lnd.withdraw}
/>
<View />
</BlueCard>
</SafeBlueArea>
);
}
}
ManageFunds.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.func,
dismiss: PropTypes.func,
navigate: PropTypes.func,
getParam: PropTypes.func,
state: PropTypes.shape({
params: PropTypes.shape({
fromSecret: PropTypes.string,
}),
}),
}),
};

View file

@ -1,4 +1,3 @@
/* global alert */
import React, { Component } from 'react'; import React, { Component } from 'react';
import { View, InteractionManager } from 'react-native'; import { View, InteractionManager } from 'react-native';
import QRCode from 'react-native-qrcode-svg'; import QRCode from 'react-native-qrcode-svg';
@ -17,6 +16,7 @@ import Privacy from '../../Privacy';
import Share from 'react-native-share'; import Share from 'react-native-share';
import { ScrollView } from 'react-native-gesture-handler'; import { ScrollView } from 'react-native-gesture-handler';
import SystemSetting from 'react-native-system-setting'; import SystemSetting from 'react-native-system-setting';
import { Chain } from '../../models/bitcoinUnits';
/** @type {AppStorage} */ /** @type {AppStorage} */
let BlueApp = require('../../BlueApp'); let BlueApp = require('../../BlueApp');
let loc = require('../../loc'); let loc = require('../../loc');
@ -30,65 +30,83 @@ export default class ReceiveDetails extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
let address = props.navigation.state.params.address; let address = props.navigation.state.params.address || '';
let secret = props.navigation.state.params.secret; let secret = props.navigation.state.params.secret || '';
this.state = { this.state = {
address: address, address: address,
secret: secret, secret: secret,
addressText: '', addressText: address,
bip21encoded: undefined, bip21encoded: undefined,
}; };
} }
async componentDidMount() { async componentDidMount() {
Privacy.enableBlur(); Privacy.enableBlur();
await SystemSetting.saveBrightness();
await SystemSetting.setAppBrightness(1.0);
console.log('receive/details - componentDidMount'); console.log('receive/details - componentDidMount');
/** @type {AbstractWallet} */ /** @type {AbstractWallet} */
let wallet;
let address = this.state.address; let address = this.state.address;
for (let w of BlueApp.getWallets()) {
if ((address && w.getAddress() === this.state.address) || w.getSecret() === this.state.secret) { if (address.trim().length === 0) {
// found our wallet let wallet;
wallet = w; for (let w of BlueApp.getWallets()) {
} if ((address && w.getAddress() === this.state.address) || w.getSecret() === this.state.secret) {
} // found our wallet
if (wallet) { wallet = w;
if (wallet.getAddressAsync) { }
try { }
address = await Promise.race([wallet.getAddressAsync(), BlueApp.sleep(1000)]); if (wallet) {
} catch (_) {} if (wallet.getAddressAsync) {
if (!address) { if (wallet.chain === Chain.ONCHAIN) {
// either sleep expired or getAddressAsync threw an exception try {
console.warn('either sleep expired or getAddressAsync threw an exception'); address = await Promise.race([wallet.getAddressAsync(), BlueApp.sleep(1000)]);
address = wallet._getExternalAddressByIndex(wallet.next_free_address_index); } catch (_) {}
} else { if (!address) {
BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally // 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,
addressText: 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
}
}
this.setState({
address: address,
addressText: address,
});
} else if (wallet.getAddress) {
this.setState({
address: address,
addressText: address,
});
} else {
this.setState({
address,
addressText: address,
});
} }
this.setState({
address: address,
addressText: address,
});
} else if (wallet.getAddress) {
address = wallet.getAddress();
this.setState({
address: address,
addressText: address,
});
} else {
alert('There was a problem obtaining your receive address. Please, try again.');
this.props.navigation.goBack();
this.setState({
address,
addressText: address,
});
} }
} }
InteractionManager.runAfterInteractions(async () => { InteractionManager.runAfterInteractions(async () => {
await SystemSetting.saveBrightness();
await SystemSetting.setAppBrightness(1.0);
const bip21encoded = bip21.encode(this.state.address); const bip21encoded = bip21.encode(this.state.address);
this.setState({ bip21encoded }); this.setState({ bip21encoded });
}); });

View file

@ -37,8 +37,8 @@ import BitcoinBIP70TransactionDecode from '../../bip70/bip70';
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits'; import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
import { HDLegacyP2PKHWallet, HDSegwitBech32Wallet, HDSegwitP2SHWallet, LightningCustodianWallet } from '../../class'; import { HDLegacyP2PKHWallet, HDSegwitBech32Wallet, HDSegwitP2SHWallet, LightningCustodianWallet } from '../../class';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
const bitcoin = require('bitcoinjs-lib');
import { BitcoinTransaction } from '../../models/bitcoinTransactionInfo'; import { BitcoinTransaction } from '../../models/bitcoinTransactionInfo';
const bitcoin = require('bitcoinjs-lib');
const bip21 = require('bip21'); const bip21 = require('bip21');
let BigNumber = require('bignumber.js'); let BigNumber = require('bignumber.js');
const { width } = Dimensions.get('window'); const { width } = Dimensions.get('window');

View file

@ -4,21 +4,33 @@ import { Chain } from '../../models/bitcoinUnits';
import { import {
Text, Text,
Platform, Platform,
StyleSheet,
View, View,
Keyboard,
ActivityIndicator, ActivityIndicator,
InteractionManager, InteractionManager,
FlatList, FlatList,
RefreshControl, RefreshControl,
TouchableOpacity, TouchableOpacity,
StatusBar, StatusBar,
Linking,
KeyboardAvoidingView,
} from 'react-native'; } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { NavigationEvents } from 'react-navigation'; import { NavigationEvents } from 'react-navigation';
import { BlueSendButtonIcon, BlueReceiveButtonIcon, BlueTransactionListItem, BlueWalletNavigationHeader } from '../../BlueComponents'; import {
BlueSendButtonIcon,
BlueListItem,
BlueReceiveButtonIcon,
BlueTransactionListItem,
BlueWalletNavigationHeader,
} from '../../BlueComponents';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
import { LightningCustodianWallet } from '../../class'; import { LightningCustodianWallet } from '../../class';
import Handoff from 'react-native-handoff'; import Handoff from 'react-native-handoff';
import { ScrollView } from 'react-native-gesture-handler'; import { ScrollView } from 'react-native-gesture-handler';
import Modal from 'react-native-modal';
import NavigationService from '../../NavigationService';
/** @type {AppStorage} */ /** @type {AppStorage} */
let BlueApp = require('../../BlueApp'); let BlueApp = require('../../BlueApp');
let loc = require('../../loc'); let loc = require('../../loc');
@ -63,6 +75,7 @@ export default class WalletTransactions extends Component {
this.state = { this.state = {
showMarketplace: Platform.OS !== 'ios', showMarketplace: Platform.OS !== 'ios',
isLoading: true, isLoading: true,
isManageFundsModalVisible: false,
showShowFlatListRefreshControl: false, showShowFlatListRefreshControl: false,
wallet: wallet, wallet: wallet,
dataSource: this.getTransactions(15), dataSource: this.getTransactions(15),
@ -206,7 +219,88 @@ export default class WalletTransactions extends Component {
</View> </View>
); );
}; };
renderManageFundsModal = () => {
return (
<Modal
isVisible={this.state.isManageFundsModalVisible}
style={styles.bottomModal}
onBackdropPress={() => {
Keyboard.dismiss();
this.setState({ isManageFundsModalVisible: false });
}}
>
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'position' : null}>
<View style={styles.advancedTransactionOptionsModalContent}>
<BlueListItem
hideChevron
component={TouchableOpacity}
onPress={a => {
const wallets = [...BlueApp.getWallets().filter(item => item.chain === Chain.ONCHAIN && item.allowSend())];
if (wallets.length === 0) {
alert('In order to proceed, please create a Bitcoin wallet to refill with.');
} else {
this.setState({ isManageFundsModalVisible: false });
this.props.navigation.navigate('SelectWallet', { onWalletSelect: this.onWalletSelect, chainType: Chain.ONCHAIN });
}
}}
title={loc.lnd.refill}
/>
<BlueListItem
hideChevron
component={TouchableOpacity}
onPress={a => {
this.setState({ isManageFundsModalVisible: false }, () =>
this.props.navigation.navigate('ReceiveDetails', {
secret: this.state.wallet.getSecret(),
}),
);
}}
title={'Refill with External Wallet'}
/>
<BlueListItem
title={loc.lnd.withdraw}
hideChevron
component={TouchableOpacity}
onPress={a => {
this.setState({ isManageFundsModalVisible: false });
Linking.openURL('https://zigzag.io/?utm_source=integration&utm_medium=bluewallet&utm_campaign=withdrawLink');
}}
/>
</View>
</KeyboardAvoidingView>
</Modal>
);
};
onWalletSelect = async wallet => {
NavigationService.navigate('WalletTransactions');
/** @type {LightningCustodianWallet} */
let toAddress = false;
if (this.state.wallet.refill_addressess.length > 0) {
toAddress = this.state.wallet.refill_addressess[0];
} else {
try {
await this.state.wallet.fetchBtcAddress();
toAddress = this.state.wallet.refill_addressess[0];
} catch (Err) {
return alert(Err.message);
}
}
if (wallet) {
this.props.navigation.navigate('SendDetails', {
memo: loc.lnd.refill_lnd_balance,
fromSecret: wallet.getSecret(),
address: toAddress,
fromWallet: wallet,
});
} else {
return alert('Internal error');
}
};
async onWillBlur() { async onWillBlur() {
StatusBar.setBarStyle('dark-content'); StatusBar.setBarStyle('dark-content');
} }
@ -245,6 +339,7 @@ export default class WalletTransactions extends Component {
this.setState({ wallet }, () => InteractionManager.runAfterInteractions(() => BlueApp.saveToDisk())); this.setState({ wallet }, () => InteractionManager.runAfterInteractions(() => BlueApp.saveToDisk()));
}) })
} }
onManageFundsPressed={() => this.setState({ isManageFundsModalVisible: true })}
/> />
<View style={{ flex: 1, backgroundColor: '#FFFFFF' }}> <View style={{ flex: 1, backgroundColor: '#FFFFFF' }}>
{this.state.showMarketplace && ( {this.state.showMarketplace && (
@ -343,6 +438,7 @@ export default class WalletTransactions extends Component {
renderItem={this.renderItem} renderItem={this.renderItem}
contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }} contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }}
/> />
{this.renderManageFundsModal()}
</View> </View>
<View <View
style={{ style={{
@ -397,6 +493,32 @@ export default class WalletTransactions extends Component {
} }
} }
const styles = StyleSheet.create({
modalContent: {
backgroundColor: '#FFFFFF',
padding: 22,
justifyContent: 'center',
alignItems: 'center',
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
borderColor: 'rgba(0, 0, 0, 0.1)',
minHeight: 200,
height: 200,
},
advancedTransactionOptionsModalContent: {
backgroundColor: '#FFFFFF',
padding: 22,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
borderColor: 'rgba(0, 0, 0, 0.1)',
minHeight: 130,
},
bottomModal: {
justifyContent: 'flex-end',
margin: 0,
},
});
WalletTransactions.propTypes = { WalletTransactions.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
navigate: PropTypes.func, navigate: PropTypes.func,