Merge branch 'master' into electrum-improve

This commit is contained in:
Igor Korsakov 2019-02-03 22:17:57 +00:00 committed by GitHub
commit 4089933dbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 3287 additions and 2579 deletions

View file

@ -67,4 +67,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[version] [version]
^0.85.0 ^0.86.0

4
.gitignore vendored
View file

@ -54,3 +54,7 @@ buck-out/
# Bundle artifact # Bundle artifact
*.jsbundle *.jsbundle
#BlueWallet
release-notes.json
release-notes.txt

View file

@ -4,7 +4,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import Ionicons from 'react-native-vector-icons/Ionicons'; import Ionicons from 'react-native-vector-icons/Ionicons';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Icon, Button, FormLabel, FormInput, Text, Header, List, ListItem } from 'react-native-elements'; import { Icon, FormLabel, FormInput, Text, Header, List, ListItem } from 'react-native-elements';
import { import {
TouchableOpacity, TouchableOpacity,
TouchableWithoutFeedback, TouchableWithoutFeedback,
@ -16,9 +16,9 @@ import {
Dimensions, Dimensions,
Image, Image,
SafeAreaView, SafeAreaView,
InputAccessoryView,
Clipboard, Clipboard,
Platform, Platform,
LayoutAnimation,
TextInput, TextInput,
} from 'react-native'; } from 'react-native';
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
@ -44,10 +44,11 @@ if (aspectRatio > 1.6) {
export class BlueButton extends Component { export class BlueButton extends Component {
render() { render() {
const disabled = this.props.hasOwnProperty('disabled');
let backgroundColor = '#ccddf9'; let backgroundColor = '#ccddf9';
if (disabled === true) { let fontColor = '#0c2550';
backgroundColor = '#99a0ab'; if (this.props.hasOwnProperty('disabled') && this.props.disabled === true) {
backgroundColor = '#eef0f4';
fontColor = '#9aa0aa';
} }
return ( return (
<TouchableOpacity <TouchableOpacity
@ -55,7 +56,7 @@ export class BlueButton extends Component {
flex: 1, flex: 1,
borderWidth: 0.7, borderWidth: 0.7,
borderColor: 'transparent', borderColor: 'transparent',
backgroundColor: this.props.hasOwnProperty('backgroundColor') ? this.props.backgroundColor : backgroundColor, backgroundColor: backgroundColor,
minHeight: 45, minHeight: 45,
height: 45, height: 45,
maxHeight: 45, maxHeight: 45,
@ -68,7 +69,7 @@ export class BlueButton extends Component {
> >
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}> <View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
{this.props.icon && <Icon name={this.props.icon.name} type={this.props.icon.type} color={this.props.icon.color} />} {this.props.icon && <Icon name={this.props.icon.name} type={this.props.icon.type} color={this.props.icon.color} />}
{this.props.title && <Text style={{ marginHorizontal: 8, fontSize: 16, color: '#0c2550' }}>{this.props.title}</Text>} {this.props.title && <Text style={{ marginHorizontal: 8, fontSize: 16, color: fontColor }}>{this.props.title}</Text>}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
); );
@ -145,26 +146,18 @@ export class LightningButton extends Component {
export class BlueButtonLink extends Component { export class BlueButtonLink extends Component {
render() { render() {
// eslint-disable-next-line
this.props.buttonStyle = this.props.buttonStyle || {};
return ( return (
<Button <TouchableOpacity
activeOpacity={0.1}
delayPressIn={0}
{...this.props}
style={{ style={{
marginTop: 20, minHeight: 60,
borderWidth: 0.7, minWidth: 100,
borderColor: 'transparent', height: 60,
justifyContent: 'center',
}} }}
buttonStyle={{ {...this.props}
height: 45, >
width: width / 1.5, <Text style={{ color: '#0c2550', textAlign: 'center', fontSize: 16 }}>{this.props.title}</Text>
}} </TouchableOpacity>
backgroundColor="transparent"
color="#0c2550"
/>
); );
} }
} }
@ -208,36 +201,32 @@ export class BlueCopyTextToClipboard extends Component {
text: '', text: '',
}; };
state = { hasTappedText: false }; constructor(props) {
super(props);
constructor() { if (Platform.OS === 'android') {
super(); UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
if (Platform.OS === 'android') UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true); }
this.state = { hasTappedText: false, address: props.text };
} }
copyToClipboard = () => { copyToClipboard = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring, () => { this.setState({ hasTappedText: true }, () => {
Clipboard.setString(this.props.text); Clipboard.setString(this.props.text);
setTimeout(() => { this.setState({ address: loc.wallets.xpub.copiedToClipboard }, () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring); setTimeout(() => {
this.setState({ hasTappedText: false }); this.setState({ hasTappedText: false, address: this.props.text });
}, 1000); }, 1000);
});
}); });
this.setState({ hasTappedText: true });
}; };
render() { render() {
return ( return (
<View style={{ justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}> <View style={{ justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<TouchableOpacity onPress={this.copyToClipboard} disabled={this.state.hasTappedText}> <TouchableOpacity onPress={this.copyToClipboard} disabled={this.state.hasTappedText}>
<Text style={styleCopyTextToClipboard.address} numberOfLines={0}> <Animated.Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{this.props.text} {this.state.address}
</Text> </Animated.Text>
{this.state.hasTappedText && (
<Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{loc.wallets.xpub.copiedToClipboard}
</Text>
)}
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); );
@ -555,6 +544,27 @@ export class BlueList extends Component {
} }
} }
export class BlueUseAllFundsButton extends Component {
static InputAccessoryViewID = 'useMaxInputAccessoryViewID';
static propTypes = {
wallet: PropTypes.shape().isRequired,
onUseAllPressed: PropTypes.func.isRequired,
};
render() {
return (
<InputAccessoryView nativeID={BlueUseAllFundsButton.InputAccessoryViewID}>
<View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
<Text style={{ color: '#9aa0aa', fontSize: 16, marginHorizontal: 8 }}>
Total: {this.props.wallet.getBalance()} {BitcoinUnit.BTC}
</Text>
<BlueButtonLink title="Use All" onPress={this.props.onUseAllPressed} />
</View>
</InputAccessoryView>
);
}
}
export class BlueLoading extends Component { export class BlueLoading extends Component {
render() { render() {
return ( return (
@ -580,7 +590,7 @@ const stylesBlueIcon = StyleSheet.create({
paddingHorizontal: 14, paddingHorizontal: 14,
paddingTop: 8, paddingTop: 8,
}, },
boxIncomming: { boxIncoming: {
position: 'relative', position: 'relative',
}, },
ball: { ball: {
@ -589,14 +599,14 @@ const stylesBlueIcon = StyleSheet.create({
borderRadius: 15, borderRadius: 15,
backgroundColor: '#ccddf9', backgroundColor: '#ccddf9',
}, },
ballIncomming: { ballIncoming: {
width: 30, width: 30,
height: 30, height: 30,
borderRadius: 15, borderRadius: 15,
backgroundColor: '#d2f8d6', backgroundColor: '#d2f8d6',
transform: [{ rotate: '-45deg' }], transform: [{ rotate: '-45deg' }],
}, },
ballIncommingWithoutRotate: { ballIncomingWithoutRotate: {
width: 30, width: 30,
height: 30, height: 30,
borderRadius: 15, borderRadius: 15,
@ -659,12 +669,12 @@ export class BluePlusIcon extends Component {
} }
} }
export class BlueTransactionIncommingIcon extends Component { export class BlueTransactionIncomingIcon extends Component {
render() { render() {
return ( return (
<View {...this.props}> <View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}> <View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncomming}> <View style={stylesBlueIcon.ballIncoming}>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 8 }} /> <Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 8 }} />
</View> </View>
</View> </View>
@ -677,7 +687,7 @@ export class BlueTransactionPendingIcon extends Component {
render() { render() {
return ( return (
<View {...this.props}> <View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}> <View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ball}> <View style={stylesBlueIcon.ball}>
<Icon <Icon
{...this.props} {...this.props}
@ -698,7 +708,7 @@ export class BlueTransactionExpiredIcon extends Component {
render() { render() {
return ( return (
<View {...this.props}> <View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}> <View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoingWithoutRotate}> <View style={stylesBlueIcon.ballOutgoingWithoutRotate}>
<Icon {...this.props} name="hourglass-end" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 6 }} /> <Icon {...this.props} name="hourglass-end" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 6 }} />
</View> </View>
@ -712,8 +722,8 @@ export class BlueTransactionOnchainIcon extends Component {
render() { render() {
return ( return (
<View {...this.props}> <View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}> <View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncomming}> <View style={stylesBlueIcon.ballIncoming}>
<Icon <Icon
{...this.props} {...this.props}
name="link" name="link"
@ -733,7 +743,7 @@ export class BlueTransactionOffchainIcon extends Component {
render() { render() {
return ( return (
<View {...this.props}> <View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}> <View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoingWithoutRotate}> <View style={stylesBlueIcon.ballOutgoingWithoutRotate}>
<Icon {...this.props} name="bolt" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 7 }} /> <Icon {...this.props} name="bolt" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 7 }} />
</View> </View>
@ -747,8 +757,8 @@ export class BlueTransactionOffchainIncomingIcon extends Component {
render() { render() {
return ( return (
<View {...this.props}> <View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}> <View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncommingWithoutRotate}> <View style={stylesBlueIcon.ballIncomingWithoutRotate}>
<Icon {...this.props} name="bolt" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 7 }} /> <Icon {...this.props} name="bolt" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 7 }} />
</View> </View>
</View> </View>
@ -761,7 +771,7 @@ export class BlueTransactionOutgoingIcon extends Component {
render() { render() {
return ( return (
<View {...this.props}> <View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}> <View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoing}> <View style={stylesBlueIcon.ballOutgoing}>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 8 }} /> <Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 8 }} />
</View> </View>
@ -987,6 +997,386 @@ export class NewWalletPanel extends Component {
} }
} }
export class BlueTransactionListItem extends Component {
static propTypes = {
item: PropTypes.shape().isRequired,
itemPriceUnit: PropTypes.string,
};
static defaultProps = {
itemPriceUnit: BitcoinUnit.BTC,
};
txMemo = () => {
if (BlueApp.tx_metadata[this.props.item.hash] && BlueApp.tx_metadata[this.props.item.hash]['memo']) {
return BlueApp.tx_metadata[this.props.item.hash]['memo'];
}
return '';
};
rowTitle = () => {
const item = this.props.item;
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (isNaN(item.value)) {
item.value = '0';
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
}
};
rowTitleStyle = () => {
const item = this.props.item;
let color = '#37c0a1';
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = '#37c0a1';
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = '#37c0a1';
} else {
color = '#FF0000';
}
}
} else if (item.value / 100000000 < 0) {
color = BlueApp.settings.foregroundColor;
}
return {
fontWeight: '600',
fontSize: 16,
color: color,
};
};
avatar = () => {
// is it lightning refill tx?
if (this.props.item.category === 'receive' && this.props.item.confirmations < 3) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
}
if (this.props.item.type && this.props.item.type === 'bitcoind_tx') {
return (
<View style={{ width: 25 }}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (this.props.item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (this.props.item.type === 'user_invoice' || this.props.item.type === 'payment_request') {
if (!this.props.item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = this.props.item.timestamp + this.props.item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={{ width: 25 }}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!this.props.item.confirmations) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
} else if (this.props.item.value < 0) {
return (
<View style={{ width: 25 }}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionIncomingIcon />
</View>
);
}
};
subtitle = () => {
return (
(this.props.item.confirmations < 7 ? loc.transactions.list.conf + ': ' + this.props.item.confirmations + ' ' : '') +
this.txMemo() +
(this.props.item.memo || '')
);
};
onPress = () => {
if (this.props.item.hash) {
NavigationService.navigate('TransactionDetails', { hash: this.props.item.hash });
} else if (
this.props.item.type === 'user_invoice' ||
this.props.item.type === 'payment_request' ||
this.props.item.type === 'paid_invoice'
) {
const lightningWallet = BlueApp.getWallets().filter(wallet => {
if (typeof wallet === 'object') {
if (wallet.hasOwnProperty('secret')) {
return wallet.getSecret() === this.props.item.fromWallet;
}
}
});
if (lightningWallet.length === 1) {
NavigationService.navigate('LNDViewInvoice', {
invoice: this.props.item,
fromWallet: lightningWallet[0],
isModal: false,
});
}
}
};
render() {
return (
<BlueListItem
avatar={this.avatar()}
title={loc.transactionTimeToReadable(this.props.item.received)}
subtitle={this.subtitle()}
onPress={this.onPress}
badge={{
value: 3,
textStyle: { color: 'orange' },
containerStyle: { marginTop: 0 },
}}
hideChevron
rightTitle={this.rowTitle()}
rightTitleStyle={this.rowTitleStyle()}
/>
);
}
}
export class BlueListTransactionItem extends Component {
static propTypes = {
item: PropTypes.shape().isRequired,
itemPriceUnit: PropTypes.string,
};
static defaultProps = {
itemPriceUnit: BitcoinUnit.BTC,
};
txMemo = () => {
if (BlueApp.tx_metadata[this.props.item.hash] && BlueApp.tx_metadata[this.props.item.hash]['memo']) {
return BlueApp.tx_metadata[this.props.item.hash]['memo'];
}
return '';
};
rowTitle = () => {
const item = this.props.item;
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (isNaN(item.value)) {
item.value = '0';
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.props.itemPriceUnit, true).toString();
}
};
rowTitleStyle = () => {
const item = this.props.item;
let color = '#37c0a1';
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = '#37c0a1';
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = '#37c0a1';
} else {
color = '#FF0000';
}
}
} else if (item.value / 100000000 < 0) {
color = BlueApp.settings.foregroundColor;
}
return {
fontWeight: '600',
fontSize: 16,
color: color,
};
};
avatar = () => {
// is it lightning refill tx?
if (this.props.item.category === 'receive' && this.props.item.confirmations < 3) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
}
if (this.props.item.type && this.props.item.type === 'bitcoind_tx') {
return (
<View style={{ width: 25 }}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (this.props.item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (this.props.item.type === 'user_invoice' || this.props.item.type === 'payment_request') {
if (!this.props.item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = this.props.item.timestamp + this.props.item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={{ width: 25 }}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!this.props.item.confirmations) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
} else if (this.props.item.value < 0) {
return (
<View style={{ width: 25 }}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionIncomingIcon />
</View>
);
}
};
subtitle = () => {
return (
(this.props.item.confirmations < 7 ? loc.transactions.list.conf + ': ' + this.props.item.confirmations + ' ' : '') +
this.txMemo() +
(this.props.item.memo || '')
);
};
onPress = () => {
if (this.props.item.hash) {
NavigationService.navigate('TransactionDetails', { hash: this.props.item.hash });
} else if (
this.props.item.type === 'user_invoice' ||
this.props.item.type === 'payment_request' ||
this.props.item.type === 'paid_invoice'
) {
const lightningWallet = BlueApp.getWallets().filter(wallet => {
if (typeof wallet === 'object') {
if (wallet.hasOwnProperty('secret')) {
return wallet.getSecret() === this.props.item.fromWallet;
}
}
});
NavigationService.navigate('LNDViewInvoice', {
invoice: this.props.item,
fromWallet: lightningWallet[0],
isModal: false,
});
}
};
render() {
return (
<BlueListItem
avatar={this.avatar()}
title={loc.transactionTimeToReadable(this.props.item.received)}
subtitle={this.subtitle()}
onPress={this.onPress}
badge={{
value: 3,
textStyle: { color: 'orange' },
containerStyle: { marginTop: 0 },
}}
hideChevron
rightTitle={this.rowTitle()}
rightTitleStyle={this.rowTitleStyle()}
/>
);
}
}
const sliderWidth = width * 1; const sliderWidth = width * 1;
const itemWidth = width * 0.82; const itemWidth = width * 0.82;
const sliderHeight = 190; const sliderHeight = 190;
@ -1003,12 +1393,17 @@ export class WalletsCarousel extends Component {
_renderItem({ item, index }) { _renderItem({ item, index }) {
let scaleValue = new Animated.Value(1.0); let scaleValue = new Animated.Value(1.0);
let props = { duration: 50 };
if (Platform.OS === 'android') {
props['useNativeDriver'] = true;
}
this.onPressedIn = () => { this.onPressedIn = () => {
Animated.spring(scaleValue, { toValue: 0.9, duration: 100, useNativeDriver: Platform.OS === 'android' }).start(); props.toValue = 0.9;
Animated.spring(scaleValue, props).start();
}; };
this.onPressedOut = () => { this.onPressedOut = () => {
Animated.spring(scaleValue, { toValue: 1.0, duration: 100, useNativeDriver: Platform.OS === 'android' }).start(); props.toValue = 1.0;
Animated.spring(scaleValue, props).start();
}; };
if (!item) { if (!item) {
@ -1262,7 +1657,6 @@ export class BlueBitcoinAmount extends Component {
ref={textInput => (this.textInput = textInput)} ref={textInput => (this.textInput = textInput)}
editable={!this.props.isLoading && !this.props.disabled} editable={!this.props.isLoading && !this.props.disabled}
value={amount} value={amount}
autoFocus={this.props.pointerEvents !== 'none'}
placeholderTextColor={this.props.disabled ? '#99a0ab' : '#0f5cc0'} placeholderTextColor={this.props.disabled ? '#99a0ab' : '#0f5cc0'}
style={{ style={{
color: this.props.disabled ? '#99a0ab' : '#0f5cc0', color: this.props.disabled ? '#99a0ab' : '#0f5cc0',

View file

@ -2,7 +2,7 @@ import { createStackNavigator, createAppContainer } from 'react-navigation';
import Settings from './screen/settings/settings'; import Settings from './screen/settings/settings';
import About from './screen/settings/about'; import About from './screen/settings/about';
import Releasenotes from './screen/settings/releasenotes'; import ReleaseNotes from './screen/settings/releasenotes';
import Selftest from './screen/selftest'; import Selftest from './screen/selftest';
import Language from './screen/settings/language'; import Language from './screen/settings/language';
import Currency from './screen/settings/currency'; import Currency from './screen/settings/currency';
@ -87,9 +87,9 @@ const WalletsStackNavigator = createStackNavigator(
screen: About, screen: About,
path: 'About', path: 'About',
}, },
Releasenotes: { ReleaseNotes: {
screen: Releasenotes, screen: ReleaseNotes,
path: 'Releasenotes', path: 'ReleaseNotes',
}, },
Selftest: { Selftest: {
screen: Selftest, screen: Selftest,
@ -185,6 +185,15 @@ const CreateWalletStackNavigator = createStackNavigator({
}, },
}); });
const LightningScanInvoiceStackNavigator = createStackNavigator({
ScanLndInvoice: {
screen: ScanLndInvoice,
},
Success: {
screen: Success,
},
});
const MainBottomTabs = createStackNavigator( const MainBottomTabs = createStackNavigator(
{ {
Wallets: { Wallets: {
@ -241,7 +250,10 @@ const MainBottomTabs = createStackNavigator(
}, },
}, },
ScanLndInvoice: { ScanLndInvoice: {
screen: ScanLndInvoice, screen: LightningScanInvoiceStackNavigator,
navigationOptions: {
header: null,
},
}, },
ScanQrAddress: { ScanQrAddress: {
screen: sendScanQrAddress, screen: sendScanQrAddress,

View file

@ -108,7 +108,10 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/legacy_multidex_aapt_derived_proguard_rules" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/legacy_multidex_main_dex_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/linked_res_for_bundle" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/linked_res_for_bundle" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint_jar" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />

View file

@ -24,13 +24,13 @@ class BlueAppComponent extends React.Component {
this.state = { isMigratingData: true }; this.state = { isMigratingData: true };
} }
async setIsMigratingData() { setIsMigratingData = async () => {
await BlueApp.startAndDecrypt(); await BlueApp.startAndDecrypt();
this.setState({ isMigratingData: false }); this.setState({ isMigratingData: false });
} };
render() { render() {
return this.state.isMigratingData ? <WalletMigrate onComplete={() => this.setIsMigratingData()} /> : <App />; return this.state.isMigratingData ? <WalletMigrate onComplete={this.setIsMigratingData} /> : <App />;
} }
} }

View file

@ -5,6 +5,7 @@
}; };
objectVersion = 46; objectVersion = 46;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
@ -42,6 +43,7 @@
2DCD954D1E0B4F2C00145EB5 /* BlueWalletTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* BlueWalletTests.m */; }; 2DCD954D1E0B4F2C00145EB5 /* BlueWalletTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* BlueWalletTests.m */; };
2DF0FFEE2056DD460020B375 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; }; 2DF0FFEE2056DD460020B375 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; };
2F707BDB2EF14D17AF9A2908 /* libReactNativePermissions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DD63E4B5C8344BB9880C9EC /* libReactNativePermissions.a */; }; 2F707BDB2EF14D17AF9A2908 /* libReactNativePermissions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DD63E4B5C8344BB9880C9EC /* libReactNativePermissions.a */; };
34582CAA4AD140F7B80C961A /* libTcpSockets.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */; };
34CC55B441594DBB95AD1B50 /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E8E8CE89B3D142C6A8A56C34 /* Octicons.ttf */; }; 34CC55B441594DBB95AD1B50 /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E8E8CE89B3D142C6A8A56C34 /* Octicons.ttf */; };
3EEBC6F85642487DA7C4EE35 /* AntDesign.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C4496FB303574862B40A878A /* AntDesign.ttf */; }; 3EEBC6F85642487DA7C4EE35 /* AntDesign.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C4496FB303574862B40A878A /* AntDesign.ttf */; };
4D6390DDA5B7485F91A6C750 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2654894D4DE44A4C8F71773D /* CoreData.framework */; }; 4D6390DDA5B7485F91A6C750 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2654894D4DE44A4C8F71773D /* CoreData.framework */; };
@ -79,7 +81,6 @@
ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2971642150620600B7C4FE /* JavaScriptCore.framework */; }; ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2971642150620600B7C4FE /* JavaScriptCore.framework */; };
F21429E1449249038A7F3444 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 334051161886419EA186F4BA /* FontAwesome.ttf */; }; F21429E1449249038A7F3444 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 334051161886419EA186F4BA /* FontAwesome.ttf */; };
FBB34FB8F9B248A89346FE61 /* libRNDeviceInfo-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EB3338E347F4AFAA8C85C04 /* libRNDeviceInfo-tvOS.a */; }; FBB34FB8F9B248A89346FE61 /* libRNDeviceInfo-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EB3338E347F4AFAA8C85C04 /* libRNDeviceInfo-tvOS.a */; };
34582CAA4AD140F7B80C961A /* libTcpSockets.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -524,6 +525,13 @@
remoteGlobalIDString = 32D980DD1BE9F11C00FA27E5; remoteGlobalIDString = 32D980DD1BE9F11C00FA27E5;
remoteInfo = RCTQRCodeLocalImage; remoteInfo = RCTQRCodeLocalImage;
}; };
B47720652202510900DD0E81 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = TcpSockets;
};
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@ -579,8 +587,10 @@
7EA61BC8FF6E4AD2A67F1557 /* RCTQRCodeLocalImage.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTQRCodeLocalImage.xcodeproj; path = "../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage.xcodeproj"; sourceTree = "<group>"; }; 7EA61BC8FF6E4AD2A67F1557 /* RCTQRCodeLocalImage.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTQRCodeLocalImage.xcodeproj; path = "../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
8637D4B5E14D443A9031DA95 /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFS.a; sourceTree = "<group>"; }; 8637D4B5E14D443A9031DA95 /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFS.a; sourceTree = "<group>"; };
910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = TcpSockets.xcodeproj; path = "../node_modules/react-native-tcp/ios/TcpSockets.xcodeproj"; sourceTree = "<group>"; };
94565BFC6A0C4235B3EC7B01 /* libRNSVG.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSVG.a; sourceTree = "<group>"; }; 94565BFC6A0C4235B3EC7B01 /* libRNSVG.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSVG.a; sourceTree = "<group>"; };
95208B2A05884A76B5BB99C0 /* libRCTGoogleAnalyticsBridge.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTGoogleAnalyticsBridge.a; sourceTree = "<group>"; }; 95208B2A05884A76B5BB99C0 /* libRCTGoogleAnalyticsBridge.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTGoogleAnalyticsBridge.a; sourceTree = "<group>"; };
9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libTcpSockets.a; sourceTree = "<group>"; };
9EA3788F4C6643B7B0182587 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; }; 9EA3788F4C6643B7B0182587 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; };
9F1F51A83D044F3BB26A35FC /* libRNSVG-tvOS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = "libRNSVG-tvOS.a"; sourceTree = "<group>"; }; 9F1F51A83D044F3BB26A35FC /* libRNSVG-tvOS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = "libRNSVG-tvOS.a"; sourceTree = "<group>"; };
A71D2FDE64CF4F729C7298EA /* RNFS.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = "<group>"; }; A71D2FDE64CF4F729C7298EA /* RNFS.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = "<group>"; };
@ -613,8 +623,6 @@
F9065403A26440679749C7AA /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = BVLinearGradient.xcodeproj; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = "<group>"; }; F9065403A26440679749C7AA /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = BVLinearGradient.xcodeproj; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = "<group>"; };
FC98DC24A81A463AB8B2E6B1 /* libRNImagePicker.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNImagePicker.a; sourceTree = "<group>"; }; FC98DC24A81A463AB8B2E6B1 /* libRNImagePicker.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNImagePicker.a; sourceTree = "<group>"; };
FD7977067E1A496F94D8B1B7 /* libRNDeviceInfo.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNDeviceInfo.a; sourceTree = "<group>"; }; FD7977067E1A496F94D8B1B7 /* libRNDeviceInfo.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNDeviceInfo.a; sourceTree = "<group>"; };
910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */ = {isa = PBXFileReference; name = "TcpSockets.xcodeproj"; path = "../node_modules/react-native-tcp/ios/TcpSockets.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */ = {isa = PBXFileReference; name = "libTcpSockets.a"; path = "libTcpSockets.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -976,6 +984,7 @@
E7078D2FED444DA4B0BD57F9 /* libRCTWKWebView.a */, E7078D2FED444DA4B0BD57F9 /* libRCTWKWebView.a */,
FC98DC24A81A463AB8B2E6B1 /* libRNImagePicker.a */, FC98DC24A81A463AB8B2E6B1 /* libRNImagePicker.a */,
B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */, B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */,
9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */,
); );
name = "Recovered References"; name = "Recovered References";
sourceTree = "<group>"; sourceTree = "<group>";
@ -1123,6 +1132,14 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
B47720622202510900DD0E81 /* Products */ = {
isa = PBXGroup;
children = (
B47720662202510900DD0E81 /* libTcpSockets.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -1356,6 +1373,10 @@
ProductGroup = B40FE5CC21FAD27D005D5578 /* Products */; ProductGroup = B40FE5CC21FAD27D005D5578 /* Products */;
ProjectRef = 9EA3788F4C6643B7B0182587 /* RNVectorIcons.xcodeproj */; ProjectRef = 9EA3788F4C6643B7B0182587 /* RNVectorIcons.xcodeproj */;
}, },
{
ProductGroup = B47720622202510900DD0E81 /* Products */;
ProjectRef = 910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */;
},
); );
projectRoot = ""; projectRoot = "";
targets = ( targets = (
@ -1795,6 +1816,13 @@
remoteRef = B4327F5021FC1B9300F7ADFA /* PBXContainerItemProxy */; remoteRef = B4327F5021FC1B9300F7ADFA /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
B47720662202510900DD0E81 /* libTcpSockets.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libTcpSockets.a;
remoteRef = B47720652202510900DD0E81 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */ /* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
@ -2111,7 +2139,7 @@
"-ObjC", "-ObjC",
"-lc++", "-lc++",
); );
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet;
PRODUCT_NAME = BlueWallet; PRODUCT_NAME = BlueWallet;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
@ -2153,7 +2181,7 @@
"-ObjC", "-ObjC",
"-lc++", "-lc++",
); );
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet;
PRODUCT_NAME = BlueWallet; PRODUCT_NAME = BlueWallet;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";

View file

@ -28,7 +28,7 @@
moduleName:@"BlueWallet" moduleName:@"BlueWallet"
initialProperties:nil initialProperties:nil
launchOptions:launchOptions]; launchOptions:launchOptions];
rootView.backgroundColor = [UIColor blackColor]; rootView.backgroundColor = [UIColor whiteColor];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new]; UIViewController *rootViewController = [UIViewController new];

View file

@ -56,9 +56,11 @@
<key>NSCalendarsUsageDescription</key> <key>NSCalendarsUsageDescription</key>
<string>This alert should not show up as we do not require this data</string> <string>This alert should not show up as we do not require this data</string>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>In order to quickly scan the recipient&apos;s address, we need your permission to use the camera to scan their QR Code.</string> <string>In order to quickly scan the recipient's address, we need your permission to use the camera to scan their QR Code.</string>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string>This alert should not show up as we do not require this data</string> <string>This alert should not show up as we do not require this data</string>
<key>NSMicrophoneUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSMotionUsageDescription</key> <key>NSMotionUsageDescription</key>
<string>This alert should not show up as we do not require this data</string> <string>This alert should not show up as we do not require this data</string>
<key>NSPhotoLibraryAddUsageDescription</key> <key>NSPhotoLibraryAddUsageDescription</key>
@ -67,8 +69,6 @@
<string>In order to import an image for scanning, we need your permission to access your photo library.</string> <string>In order to import an image for scanning, we need your permission to access your photo library.</string>
<key>NSSpeechRecognitionUsageDescription</key> <key>NSSpeechRecognitionUsageDescription</key>
<string>This alert should not show up as we do not require this data</string> <string>This alert should not show up as we do not require this data</string>
<key>NSMicrophoneUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>UIAppFonts</key> <key>UIAppFonts</key>
<array> <array>
<string>AntDesign.ttf</string> <string>AntDesign.ttf</string>

View file

@ -217,6 +217,7 @@ module.exports = {
refill_lnd_balance: 'Lade deine Lightning Wallet auf', refill_lnd_balance: 'Lade deine Lightning Wallet auf',
refill: 'Aufladen', refill: 'Aufladen',
withdraw: 'Abheben', withdraw: 'Abheben',
sameWalletAsInvoiceError: 'Du kannst nicht die Rechnung mit der Wallet begleichen, die du für die Erstellung dieser Rechnung verwendet hast.', sameWalletAsInvoiceError:
'Du kannst nicht die Rechnung mit der Wallet begleichen, die du für die Erstellung dieser Rechnung verwendet hast.',
}, },
}; };

View file

@ -11,6 +11,7 @@ export const FiatUnit = Object.freeze({
INR: { endPointKey: 'INR', symbol: '₹', locale: 'hi-HN' }, INR: { endPointKey: 'INR', symbol: '₹', locale: 'hi-HN' },
JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP' }, JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP' },
MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX' }, MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX' },
MYR: { endPointKey: 'MYR', symbol: 'RM', locale: 'ms-MY' },
PLN: { endPointKey: 'PLN', symbol: 'zł', locale: 'pl-PL' }, PLN: { endPointKey: 'PLN', symbol: 'zł', locale: 'pl-PL' },
RUB: { endPointKey: 'RUB', symbol: '₽', locale: 'ru-RU' }, RUB: { endPointKey: 'RUB', symbol: '₽', locale: 'ru-RU' },
SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' }, SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' },

View file

@ -1,6 +1,8 @@
import Frisbee from 'frisbee'; import Frisbee from 'frisbee';
export class NetworkTransactionFee { export class NetworkTransactionFee {
static StorageKey = 'NetworkTransactionFee';
constructor(fastestFee = 1, halfHourFee = 1, hourFee = 1) { constructor(fastestFee = 1, halfHourFee = 1, hourFee = 1) {
this.fastestFee = fastestFee; this.fastestFee = fastestFee;
this.halfHourFee = halfHourFee; this.halfHourFee = halfHourFee;

4471
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
"devDependencies": { "devDependencies": {
"babel-eslint": "^10.0.1", "babel-eslint": "^10.0.1",
"babel-jest": "^24.0.0", "babel-jest": "^24.0.0",
"eslint": "^5.12.1", "eslint": "^5.13.0",
"eslint-plugin-babel": "^5.3.0", "eslint-plugin-babel": "^5.3.0",
"eslint-plugin-import": "^2.15.0", "eslint-plugin-import": "^2.15.0",
"eslint-plugin-node": "^8.0.1", "eslint-plugin-node": "^8.0.1",
@ -47,31 +47,31 @@
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"dayjs": "^1.8.0", "dayjs": "^1.8.0",
"electrum-client": "git+https://github.com/Overtorment/node-electrum-client.git", "electrum-client": "git+https://github.com/Overtorment/node-electrum-client.git",
"eslint-config-prettier": "^3.6.0", "eslint-config-prettier": "^4.0.0",
"eslint-config-standard": "^12.0.0", "eslint-config-standard": "^12.0.0",
"eslint-config-standard-react": "^7.0.2", "eslint-config-standard-react": "^7.0.2",
"eslint-plugin-prettier": "^3.0.1", "eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-standard": "^4.0.0", "eslint-plugin-standard": "^4.0.0",
"frisbee": "^1.6.4", "frisbee": "^2.0.5",
"intl": "^1.2.5", "intl": "^1.2.5",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"node-libs-react-native": "^1.0.1", "node-libs-react-native": "^1.0.1",
"path-browserify": "0.0.0", "path-browserify": "^1.0.0",
"prettier": "^1.16.1", "prettier": "^1.16.3",
"process": "^0.11.10", "process": "^0.11.10",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.7.0", "react": "^16.7.0",
"react-localization": "^1.0.10", "react-localization": "^1.0.10",
"react-native": "^0.58.1", "react-native": "^0.58.1",
"react-native-camera": "^1.9.2", "react-native-camera": "^1.10.0",
"react-native-custom-qr-codes": "^2.0.0", "react-native-custom-qr-codes": "^2.0.0",
"react-native-device-info": "^0.25.1", "react-native-device-info": "^0.26.1",
"react-native-elements": "^0.19.0", "react-native-elements": "^0.19.0",
"react-native-flexi-radio-button": "^0.2.2", "react-native-flexi-radio-button": "^0.2.2",
"react-native-fs": "^2.13.3", "react-native-fs": "^2.13.3",
"react-native-gesture-handler": "^1.0.15", "react-native-gesture-handler": "^1.0.15",
"react-native-google-analytics-bridge": "^7.0.0", "react-native-google-analytics-bridge": "^7.0.0",
"react-native-haptic-feedback": "^1.4.2", "react-native-haptic-feedback": "^1.5.0",
"react-native-image-picker": "^0.28.0", "react-native-image-picker": "^0.28.0",
"react-native-level-fs": "^3.0.1", "react-native-level-fs": "^3.0.1",
"react-native-linear-gradient": "^2.5.3", "react-native-linear-gradient": "^2.5.3",
@ -81,20 +81,20 @@
"react-native-qrcode": "^0.2.7", "react-native-qrcode": "^0.2.7",
"react-native-randombytes": "^3.5.2", "react-native-randombytes": "^3.5.2",
"react-native-rate": "^1.1.6", "react-native-rate": "^1.1.6",
"react-native-sentry": "^0.40.2", "react-native-sentry": "^0.41.1",
"react-native-snap-carousel": "^3.7.5", "react-native-snap-carousel": "^3.7.5",
"react-native-sortable-list": "0.0.22", "react-native-sortable-list": "0.0.22",
"react-native-svg": "^9.0.4", "react-native-svg": "^9.1.1",
"react-native-tcp": "^3.3.0", "react-native-tcp": "^3.3.0",
"react-native-vector-icons": "^6.2.0", "react-native-vector-icons": "^6.2.0",
"react-native-webview": "^3.2.1", "react-native-webview": "4.1.0",
"react-native-wkwebview-reborn": "^2.0.0", "react-native-wkwebview-reborn": "^2.0.0",
"react-navigation": "^3.0.9", "react-navigation": "^3.1.2",
"react-test-render": "^1.1.1", "react-test-render": "^1.1.1",
"readable-stream": "^1.1.14", "readable-stream": "^3.1.1",
"request-promise-native": "^1.0.5", "request-promise-native": "^1.0.5",
"secure-random": "^1.1.1", "secure-random": "^1.1.1",
"stream-browserify": "^1.0.0", "stream-browserify": "^2.0.2",
"util": "^0.11.1", "util": "^0.11.1",
"wif": "^2.0.1" "wif": "^2.0.1"
}, },

View file

@ -495,7 +495,7 @@ export default class Browser extends Component {
Browser.propTypes = { Browser.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
getParam: PropTypes.function, getParam: PropTypes.func,
navigate: PropTypes.func, navigate: PropTypes.func,
}), }),
}; };

View file

@ -116,7 +116,7 @@ export default class LNDCreateInvoice extends Component {
LNDCreateInvoice.propTypes = { LNDCreateInvoice.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
navigate: PropTypes.func, navigate: PropTypes.func,
getParam: PropTypes.func, getParam: PropTypes.func,
}), }),

View file

@ -82,8 +82,8 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
LNDViewAdditionalInvoiceInformation.propTypes = { LNDViewAdditionalInvoiceInformation.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
getParam: PropTypes.function, getParam: PropTypes.func,
dismiss: PropTypes.function, dismiss: PropTypes.func,
}), }),
}; };

View file

@ -236,9 +236,9 @@ export default class LNDViewInvoice extends Component {
LNDViewInvoice.propTypes = { LNDViewInvoice.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
navigate: PropTypes.function, navigate: PropTypes.func,
getParam: PropTypes.function, getParam: PropTypes.func,
dismiss: PropTypes.function, dismiss: PropTypes.func,
}), }),
}; };

View file

@ -83,10 +83,10 @@ export default class ManageFunds extends Component {
ManageFunds.propTypes = { ManageFunds.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
dismiss: PropTypes.function, dismiss: PropTypes.func,
navigate: PropTypes.function, navigate: PropTypes.func,
getParam: PropTypes.function, getParam: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({
fromSecret: PropTypes.string, fromSecret: PropTypes.string,

View file

@ -72,12 +72,6 @@ export default class ScanLndInvoice extends React.Component {
processInvoice = data => { processInvoice = data => {
this.setState({ isLoading: true }, async () => { this.setState({ isLoading: true }, async () => {
if (this.ignoreRead) return;
this.ignoreRead = true;
setTimeout(() => {
this.ignoreRead = false;
}, 6000);
if (!this.state.fromWallet) { if (!this.state.fromWallet) {
alert('Before paying a Lightning invoice, you must first add a Lightning wallet.'); alert('Before paying a Lightning invoice, you must first add a Lightning wallet.');
return this.props.navigation.goBack(); return this.props.navigation.goBack();
@ -149,11 +143,8 @@ export default class ScanLndInvoice extends React.Component {
return alert(loc.lnd.sameWalletAsInvoiceError); return alert(loc.lnd.sameWalletAsInvoiceError);
} }
let start = +new Date();
let end;
try { try {
await fromWallet.payInvoice(this.state.invoice, this.state.decoded.num_satoshis); await fromWallet.payInvoice(this.state.invoice, this.state.decoded.num_satoshis);
end = +new Date();
} catch (Err) { } catch (Err) {
console.log(Err.message); console.log(Err.message);
this.setState({ isLoading: false }); this.setState({ isLoading: false });
@ -161,11 +152,12 @@ export default class ScanLndInvoice extends React.Component {
return alert('Error'); return alert('Error');
} }
console.log('payInvoice took', (end - start) / 1000, 'sec');
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // someone should fetch txs EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // someone should fetch txs
this.props.navigation.navigate('Success', {
alert('Success'); amount: this.state.decoded.num_satoshis,
this.props.navigation.goBack(); amountUnit: BitcoinUnit.SATS,
invoiceDescription: this.state.decoded.description,
});
}, },
); );
} }
@ -237,24 +229,26 @@ export default class ScanLndInvoice extends React.Component {
)} )}
<BlueSpacing20 /> <BlueSpacing20 />
<BlueSpacing20 /> <BlueSpacing20 />
{this.state.isLoading ? ( <BlueCard>
<View> {this.state.isLoading ? (
<ActivityIndicator /> <View>
</View> <ActivityIndicator />
) : ( </View>
<BlueButton ) : (
icon={{ <BlueButton
name: 'bolt', icon={{
type: 'font-awesome', name: 'bolt',
color: BlueApp.settings.buttonTextColor, type: 'font-awesome',
}} color: BlueApp.settings.buttonTextColor,
title={'Pay'} }}
onPress={() => { title={'Pay'}
this.pay(); onPress={() => {
}} this.pay();
disabled={this.shouldDisablePayButton()} }}
/> disabled={this.shouldDisablePayButton()}
)} />
)}
</BlueCard>
</BlueCard> </BlueCard>
</SafeBlueArea> </SafeBlueArea>
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
@ -264,10 +258,10 @@ export default class ScanLndInvoice extends React.Component {
ScanLndInvoice.propTypes = { ScanLndInvoice.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
navigate: PropTypes.function, navigate: PropTypes.func,
getParam: PropTypes.function, getParam: PropTypes.func,
dismiss: PropTypes.function, dismiss: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({
uri: PropTypes.string, uri: PropTypes.string,

View file

@ -10,7 +10,6 @@ import {
BlueButtonLink, BlueButtonLink,
BlueNavigationStyle, BlueNavigationStyle,
is, is,
BlueSpacing20,
} from '../../BlueComponents'; } from '../../BlueComponents';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/** @type {AppStorage} */ /** @type {AppStorage} */
@ -107,7 +106,6 @@ export default class ReceiveDetails extends Component {
}); });
}} }}
/> />
<BlueSpacing20 />
<BlueButton <BlueButton
icon={{ icon={{
name: 'share-alternative', name: 'share-alternative',
@ -130,8 +128,8 @@ export default class ReceiveDetails extends Component {
ReceiveDetails.propTypes = { ReceiveDetails.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
navigate: PropTypes.function, navigate: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({
address: PropTypes.string, address: PropTypes.string,

View file

@ -146,10 +146,10 @@ const styles = StyleSheet.create({
Confirm.propTypes = { Confirm.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
getParam: PropTypes.function, getParam: PropTypes.func,
navigate: PropTypes.function, navigate: PropTypes.func,
dismiss: PropTypes.function, dismiss: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({
amount: PropTypes.string, amount: PropTypes.string,

View file

@ -108,10 +108,10 @@ const styles = StyleSheet.create({
SendCreate.propTypes = { SendCreate.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
getParam: PropTypes.function, getParam: PropTypes.func,
navigate: PropTypes.function, navigate: PropTypes.func,
dismiss: PropTypes.function, dismiss: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({
amount: PropTypes.string, amount: PropTypes.string,

View file

@ -11,6 +11,7 @@ import {
StyleSheet, StyleSheet,
Platform, Platform,
Slider, Slider,
AsyncStorage,
Text, Text,
} from 'react-native'; } from 'react-native';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
@ -71,13 +72,14 @@ export default class SendDetails extends Component {
fromAddress, fromAddress,
fromWallet, fromWallet,
fromSecret, fromSecret,
isLoading: true, isLoading: false,
address, address,
memo, memo,
fee: 1, fee: 1,
networkTransactionFees: new NetworkTransactionFee(1, 1, 1), networkTransactionFees: new NetworkTransactionFee(1, 1, 1),
feeSliderValue: 1, feeSliderValue: 1,
bip70TransactionExpiration: null, bip70TransactionExpiration: null,
renderWalletSelectionButtonHidden: false,
}; };
} }
@ -91,7 +93,7 @@ export default class SendDetails extends Component {
const dataWithoutSchema = data.replace('bitcoin:', ''); const dataWithoutSchema = data.replace('bitcoin:', '');
if (btcAddressRx.test(dataWithoutSchema) || dataWithoutSchema.indexOf('bc1') === 0) { if (btcAddressRx.test(dataWithoutSchema) || dataWithoutSchema.indexOf('bc1') === 0) {
this.setState({ this.setState({
address: data, address: dataWithoutSchema,
bip70TransactionExpiration: null, bip70TransactionExpiration: null,
isLoading: false, isLoading: false,
}); });
@ -126,20 +128,27 @@ export default class SendDetails extends Component {
}; };
async componentDidMount() { async componentDidMount() {
let recommendedFees = await NetworkTransactionFees.recommendedFees().catch(response => { this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.setState({ this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
fee: response.halfHourFee, try {
networkTransactionFees: response, const cachedNetworkTransactionFees = JSON.parse(await AsyncStorage.getItem(NetworkTransactionFee.StorageKey));
feeSliderValue: response.halfHourFee,
isLoading: false, if (cachedNetworkTransactionFees && cachedNetworkTransactionFees.hasOwnProperty('halfHourFee')) {
}); this.setState({
}); fee: cachedNetworkTransactionFees.halfHourFee,
if (recommendedFees) { networkTransactionFees: cachedNetworkTransactionFees,
feeSliderValue: cachedNetworkTransactionFees.halfHourFee,
});
}
} catch (_) {}
let recommendedFees = await NetworkTransactionFees.recommendedFees();
if (recommendedFees && recommendedFees.hasOwnProperty('halfHourFee')) {
await AsyncStorage.setItem(NetworkTransactionFee.StorageKey, JSON.stringify(recommendedFees));
this.setState({ this.setState({
fee: recommendedFees.halfHourFee, fee: recommendedFees.halfHourFee,
networkTransactionFees: recommendedFees, networkTransactionFees: recommendedFees,
feeSliderValue: recommendedFees.halfHourFee, feeSliderValue: recommendedFees.halfHourFee,
isLoading: false,
}); });
if (this.props.navigation.state.params.uri) { if (this.props.navigation.state.params.uri) {
@ -148,16 +157,34 @@ export default class SendDetails extends Component {
} else { } else {
try { try {
const { address, amount, memo } = this.decodeBitcoinUri(this.props.navigation.getParam('uri')); const { address, amount, memo } = this.decodeBitcoinUri(this.props.navigation.getParam('uri'));
this.setState({ address, amount, memo }); this.setState({ address, amount, memo, isLoading: false });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
this.setState({ isLoading: false });
alert('Error: Unable to decode Bitcoin address'); alert('Error: Unable to decode Bitcoin address');
} }
} }
} else {
this.setState({ isLoading: false });
} }
} else {
this.setState({ isLoading: false });
} }
} }
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow = () => {
this.setState({ renderWalletSelectionButtonHidden: true });
};
_keyboardDidHide = () => {
this.setState({ renderWalletSelectionButtonHidden: false });
};
decodeBitcoinUri(uri) { decodeBitcoinUri(uri) {
try { try {
let amount = ''; let amount = '';
@ -382,9 +409,9 @@ export default class SendDetails extends Component {
} }
onWalletSelect = wallet => { onWalletSelect = wallet => {
this.setState({ fromAddress: wallet.getAddress(), fromSecret: wallet.getSecret(), fromWallet: wallet }, () => this.setState({ fromAddress: wallet.getAddress(), fromSecret: wallet.getSecret(), fromWallet: wallet }, () => {
this.props.navigation.goBack(null), this.props.navigation.pop();
); });
}; };
renderFeeSelectionModal = () => { renderFeeSelectionModal = () => {
@ -392,7 +419,13 @@ export default class SendDetails extends Component {
<Modal <Modal
isVisible={this.state.isFeeSelectionModalVisible} isVisible={this.state.isFeeSelectionModalVisible}
style={styles.bottomModal} style={styles.bottomModal}
onBackdropPress={() => this.setState({ isFeeSelectionModalVisible: false })} onBackdropPress={() => {
if (this.state.fee < 1 || this.state.feeSliderValue < 1) {
this.setState({ fee: Number(1), feeSliderValue: Number(1) });
}
Keyboard.dismiss();
this.setState({ isFeeSelectionModalVisible: false });
}}
> >
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'position' : null}> <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'position' : null}>
<View style={styles.modalContent}> <View style={styles.modalContent}>
@ -403,12 +436,14 @@ export default class SendDetails extends Component {
this.textInput = ref; this.textInput = ref;
}} }}
value={this.state.fee.toString()} value={this.state.fee.toString()}
onEndEditing={() => {
if (this.state.fee < 1 || this.state.feeSliderValue < 1) {
this.setState({ fee: Number(1), feeSliderValue: Number(1) });
}
}}
onChangeText={value => { onChangeText={value => {
let newValue = value.replace(/\D/g, ''); let newValue = value.replace(/\D/g, '');
if (newValue.length === 0) { this.setState({ fee: Number(newValue), feeSliderValue: Number(newValue) });
newValue = 1;
}
this.setState({ fee: newValue, feeSliderValue: newValue });
}} }}
maxLength={9} maxLength={9}
editable={!this.state.isLoading} editable={!this.state.isLoading}
@ -466,6 +501,7 @@ export default class SendDetails extends Component {
}; };
renderWalletSelectionButton = () => { renderWalletSelectionButton = () => {
if (this.state.renderWalletSelectionButtonHidden) return;
return ( return (
<View style={{ marginBottom: 24, alignItems: 'center' }}> <View style={{ marginBottom: 24, alignItems: 'center' }}>
{!this.state.isLoading && ( {!this.state.isLoading && (
@ -500,7 +536,6 @@ export default class SendDetails extends Component {
</View> </View>
); );
} }
return ( return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}> <TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<View style={{ flex: 1, justifyContent: 'space-between' }}> <View style={{ flex: 1, justifyContent: 'space-between' }}>
@ -620,7 +655,7 @@ const styles = StyleSheet.create({
SendDetails.propTypes = { SendDetails.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.func, pop: PropTypes.func,
navigate: PropTypes.func, navigate: PropTypes.func,
getParam: PropTypes.func, getParam: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({

View file

@ -16,10 +16,12 @@ export default class CameraExample extends React.Component {
}; };
onBarCodeScanned(ret) { onBarCodeScanned(ret) {
console.warn(ret); if (this.state.isLoading) return;
const onBarScanned = this.props.navigation.getParam('onBarScanned'); this.setState({ isLoading: true }, () => {
onBarScanned(ret.data); const onBarScanned = this.props.navigation.getParam('onBarScanned');
this.props.navigation.goBack(null); this.props.navigation.goBack();
onBarScanned(ret.data);
});
} // end } // end
componentDidMount() { componentDidMount() {

View file

@ -18,7 +18,9 @@ export default class Success extends Component {
this.state = { this.state = {
amount: props.navigation.getParam('amount'), amount: props.navigation.getParam('amount'),
fee: props.navigation.getParam('fee'), fee: props.navigation.getParam('fee') || 0,
amountUnit: props.navigation.getParam('amountUnit') || BitcoinUnit.BTC,
invoiceDescription: props.navigation.getParam('invoiceDescription') || '',
}; };
} }
@ -51,21 +53,38 @@ export default class Success extends Component {
alignSelf: 'flex-end', alignSelf: 'flex-end',
}} }}
> >
{' ' + BitcoinUnit.BTC} {' ' + this.state.amountUnit}
</Text> </Text>
</View> </View>
<Text {this.state.fee > 0 && (
style={{ <Text
color: '#37c0a1', style={{
fontSize: 14, color: '#37c0a1',
marginHorizontal: 4, fontSize: 14,
paddingBottom: 6, marginHorizontal: 4,
fontWeight: '500', paddingBottom: 6,
alignSelf: 'center', fontWeight: '500',
}} alignSelf: 'center',
> }}
{loc.send.create.fee}: {loc.formatBalance(this.state.fee, BitcoinUnit.SATS)} >
</Text> {loc.send.create.fee}: {loc.formatBalance(this.state.fee, BitcoinUnit.SATS)}
</Text>
)}
{this.state.fee <= 0 && (
<Text
numberOfLines={0}
style={{
color: '#37c0a1',
fontSize: 14,
marginHorizontal: 4,
paddingBottom: 6,
fontWeight: '500',
alignSelf: 'center',
}}
>
{this.state.invoiceDescription}
</Text>
)}
</BlueCard> </BlueCard>
<View <View
style={{ style={{
@ -87,7 +106,6 @@ export default class Success extends Component {
this.props.navigation.dismiss(); this.props.navigation.dismiss();
}} }}
title={loc.send.success.done} title={loc.send.success.done}
style={{ maxWidth: 263, paddingHorizontal: 56 }}
/> />
</BlueCard> </BlueCard>
</SafeBlueArea> </SafeBlueArea>
@ -97,10 +115,10 @@ export default class Success extends Component {
Success.propTypes = { Success.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
getParam: PropTypes.function, getParam: PropTypes.func,
navigate: PropTypes.function, navigate: PropTypes.func,
dismiss: PropTypes.function, dismiss: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({
amount: PropTypes.string, amount: PropTypes.string,

View file

@ -129,7 +129,7 @@ export default class About extends Component {
<BlueButton <BlueButton
onPress={() => { onPress={() => {
this.props.navigation.navigate('Releasenotes'); this.props.navigation.navigate('ReleaseNotes');
}} }}
title="Release notes" title="Release notes"
/> />

View file

@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
/** @type {AppStorage} */ /** @type {AppStorage} */
const notes = require('../../release-notes'); const notes = require('../../release-notes');
export default class Releasenotes extends Component { export default class ReleaseNotes extends Component {
static navigationOptions = () => ({ static navigationOptions = () => ({
...BlueNavigationStyle(), ...BlueNavigationStyle(),
title: 'Release notes', title: 'Release notes',
@ -43,7 +43,7 @@ export default class Releasenotes extends Component {
} }
} }
Releasenotes.propTypes = { ReleaseNotes.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
navigate: PropTypes.func, navigate: PropTypes.func,
goBack: PropTypes.func, goBack: PropTypes.func,

View file

@ -234,7 +234,7 @@ export default class SendCreate extends Component {
SendCreate.propTypes = { SendCreate.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
navigate: PropTypes.func, navigate: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({

View file

@ -191,7 +191,7 @@ export default class RBF extends Component {
RBF.propTypes = { RBF.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
navigate: PropTypes.func, navigate: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({

View file

@ -89,22 +89,25 @@ export default class TransactionsDetails extends Component {
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}> <SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}>
<BlueHeaderDefaultSub leftText={loc.transactions.details.title} rightComponent={null} /> <BlueHeaderDefaultSub leftText={loc.transactions.details.title} rightComponent={null} />
<ScrollView style={{ flex: 1 }}> <ScrollView style={{ flex: 1 }}>
{(() => {
if (this.state.tx.confirmations === 0 && this.state.wallet && this.state.wallet.allowRBF()) {
return (
<BlueButton
onPress={() =>
this.props.navigation.navigate('RBF', {
txid: this.state.tx.hash,
})
}
title="Replace-By-Fee (RBF)"
/>
);
}
})()}
<BlueCard> <BlueCard>
{(() => {
if (this.state.tx.confirmations === 0 && this.state.wallet && this.state.wallet.allowRBF()) {
return (
<React.Fragment>
<BlueButton
onPress={() =>
this.props.navigation.navigate('RBF', {
txid: this.state.tx.hash,
})
}
title="Replace-By-Fee (RBF)"
/>
<BlueSpacing20 />
</React.Fragment>
);
}
})()}
{(() => { {(() => {
if (BlueApp.tx_metadata[this.state.tx.hash]) { if (BlueApp.tx_metadata[this.state.tx.hash]) {
if (BlueApp.tx_metadata[this.state.tx.hash]['memo']) { if (BlueApp.tx_metadata[this.state.tx.hash]['memo']) {
@ -176,14 +179,14 @@ export default class TransactionsDetails extends Component {
</React.Fragment> </React.Fragment>
)} )}
{this.state.tx.hasOwnProperty('block_height') && this.state.block_height > 0 && ( {this.state.tx.hasOwnProperty('block_height') && this.state.tx.block_height > 0 && (
<React.Fragment> <React.Fragment>
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Block Height</BlueText> <BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Block Height</BlueText>
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.block_height}</BlueText> <BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.block_height}</BlueText>
</React.Fragment> </React.Fragment>
)} )}
{this.state.tx.hasOwnProperty('confirmations') && ( {this.state.tx.hasOwnProperty('confirmations') && this.state.tx.confirmations > 0 && (
<React.Fragment> <React.Fragment>
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Confirmations</BlueText> <BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Confirmations</BlueText>
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.confirmations}</BlueText> <BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.confirmations}</BlueText>
@ -197,7 +200,7 @@ export default class TransactionsDetails extends Component {
</React.Fragment> </React.Fragment>
)} )}
{this.state.tx.hasOwnProperty('outputs') && ( {this.state.tx.hasOwnProperty('outputs') && this.state.tx.outputs.length > 0 && (
<React.Fragment> <React.Fragment>
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Outputs</BlueText> <BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Outputs</BlueText>
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.outputs.length}</BlueText> <BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.outputs.length}</BlueText>
@ -212,7 +215,7 @@ export default class TransactionsDetails extends Component {
TransactionsDetails.propTypes = { TransactionsDetails.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
navigate: PropTypes.func, navigate: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({

View file

@ -72,7 +72,7 @@ export default class WalletsAdd extends Component {
return ( return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1, paddingTop: 40 }}> <SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1, paddingTop: 40 }}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}> <TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false} style={{ flex: 1 }}>
<BlueCard> <BlueCard>
<BlueFormLabel>{loc.wallets.add.wallet_name}</BlueFormLabel> <BlueFormLabel>{loc.wallets.add.wallet_name}</BlueFormLabel>
<View <View

View file

@ -107,7 +107,7 @@ export default class BuyBitcoin extends Component {
BuyBitcoin.propTypes = { BuyBitcoin.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.function, goBack: PropTypes.func,
state: PropTypes.shape({ state: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({
address: PropTypes.string, address: PropTypes.string,

View file

@ -215,38 +215,39 @@ export default class WalletsImport extends Component {
this.setLabel(text); this.setLabel(text);
}} }}
/> />
<BlueSpacing20 />
<View
style={{
alignItems: 'center',
}}
>
<BlueButton
disabled={!this.state.label}
title={loc.wallets.import.do_import}
buttonStyle={{
width: width / 1.5,
}}
onPress={async () => {
if (!this.state.label) {
return;
}
this.setState({ isLoading: true });
setTimeout(async () => {
await this.importMnemonic(this.state.label.trim());
this.setState({ isLoading: false });
}, 1);
}}
/>
<BlueButtonLink
title={loc.wallets.import.scan_qr}
onPress={() => {
this.props.navigation.navigate('ScanQrWif');
}}
/>
</View>
</KeyboardAvoidingView> </KeyboardAvoidingView>
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
<BlueSpacing20 />
<View
style={{
alignItems: 'center',
}}
>
<BlueButton
disabled={!this.state.label}
title={loc.wallets.import.do_import}
buttonStyle={{
width: width / 1.5,
}}
onPress={async () => {
if (!this.state.label) {
return;
}
this.setState({ isLoading: true });
setTimeout(async () => {
await this.importMnemonic(this.state.label.trim());
this.setState({ isLoading: false });
}, 1);
}}
/>
<BlueButtonLink
title={loc.wallets.import.scan_qr}
onPress={() => {
this.props.navigation.navigate('ScanQrWif');
}}
/>
</View>
</SafeBlueArea> </SafeBlueArea>
); );
} }

View file

@ -1,20 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { View, TouchableOpacity, Text, FlatList, RefreshControl, ScrollView } from 'react-native'; import { View, TouchableOpacity, Text, FlatList, RefreshControl, ScrollView } from 'react-native';
import { import { BlueLoading, SafeBlueArea, WalletsCarousel, BlueList, BlueHeaderDefaultMain, BlueListTransactionItem } from '../../BlueComponents';
BlueTransactionOnchainIcon,
BlueLoading,
SafeBlueArea,
WalletsCarousel,
BlueTransactionIncommingIcon,
BlueTransactionOutgoingIcon,
BlueTransactionPendingIcon,
BlueTransactionOffchainIcon,
BlueTransactionExpiredIcon,
BlueList,
BlueListItem,
BlueHeaderDefaultMain,
BlueTransactionOffchainIncomingIcon,
} from '../../BlueComponents';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
import { NavigationEvents } from 'react-navigation'; import { NavigationEvents } from 'react-navigation';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
@ -232,60 +218,9 @@ export default class WalletsList extends Component {
} }
}; };
rowTitle = item => { _renderItem = data => <BlueListTransactionItem item={data.item} itemPriceUnit={data.item.walletPreferredBalanceUnit} />;
if (item.type === 'user_invoice' || item.type === 'payment_request') {
if (isNaN(item.value)) {
item.value = '0';
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, item.walletPreferredBalanceUnit, true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, item.walletPreferredBalanceUnit, true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return loc.formatBalanceWithoutSuffix(item.value && item.value, item.walletPreferredBalanceUnit, true).toString();
}
};
rowTitleStyle = item => {
let color = '#37c0a1';
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = '#37c0a1';
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = '#37c0a1';
} else {
color = '#FF0000';
}
}
} else if (item.value / 100000000 < 0) {
color = BlueApp.settings.foregroundColor;
}
return {
fontWeight: '600',
fontSize: 16,
color: color,
};
};
render() { render() {
const { navigate } = this.props.navigation;
if (this.state.isLoading) { if (this.state.isLoading) {
return <BlueLoading />; return <BlueLoading />;
} }
@ -338,117 +273,7 @@ export default class WalletsList extends Component {
data={this.state.dataSource} data={this.state.dataSource}
extraData={this.state.dataSource} extraData={this.state.dataSource}
keyExtractor={this._keyExtractor} keyExtractor={this._keyExtractor}
renderItem={rowData => { renderItem={this._renderItem}
return (
<BlueListItem
avatar={(() => {
// is it lightning refill tx?
if (rowData.item.category === 'receive' && rowData.item.confirmations < 3) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
}
if (rowData.item.type && rowData.item.type === 'bitcoind_tx') {
return (
<View style={{ width: 25 }}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (rowData.item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (rowData.item.type === 'user_invoice' || rowData.item.type === 'payment_request') {
if (!rowData.item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = rowData.item.timestamp + rowData.item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={{ width: 25 }}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!rowData.item.confirmations) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
} else if (rowData.item.value < 0) {
return (
<View style={{ width: 25 }}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionIncommingIcon />
</View>
);
}
})()}
title={loc.transactionTimeToReadable(rowData.item.received)}
subtitle={
(rowData.item.confirmations < 7 ? loc.transactions.list.conf + ': ' + rowData.item.confirmations + ' ' : '') +
this.txMemo(rowData.item.hash) +
(rowData.item.memo || '')
}
onPress={() => {
if (rowData.item.hash) {
navigate('TransactionDetails', {
hash: rowData.item.hash,
});
} else if (
rowData.item.type === 'user_invoice' ||
rowData.item.type === 'payment_request' ||
rowData.item.type === 'paid_invoice'
) {
const lightningWallet = this.state.wallets.filter(wallet => {
if (typeof wallet === 'object') {
if (wallet.hasOwnProperty('secret')) {
return wallet.getSecret() === rowData.item.fromWallet;
}
}
});
this.props.navigation.navigate('LNDViewInvoice', {
invoice: rowData.item,
fromWallet: lightningWallet[0],
isModal: false,
});
}
}}
badge={{
value: 3,
textStyle: { color: 'orange' },
containerStyle: { marginTop: 0 },
}}
hideChevron
rightTitle={this.rowTitle(rowData.item)}
rightTitleStyle={this.rowTitleStyle(rowData.item)}
/>
);
}}
/> />
</BlueList> </BlueList>
</ScrollView> </ScrollView>

View file

@ -28,6 +28,7 @@ export default class ScanQrWif extends React.Component {
}; };
async onBarCodeScanned(ret) { async onBarCodeScanned(ret) {
this.setState({ isLoading: true });
if (+new Date() - this.lastTimeIveBeenHere < 6000) { if (+new Date() - this.lastTimeIveBeenHere < 6000) {
this.lastTimeIveBeenHere = +new Date(); this.lastTimeIveBeenHere = +new Date();
return; return;
@ -57,16 +58,17 @@ export default class ScanQrWif extends React.Component {
ret.data = wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed); ret.data = wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed);
} catch (e) { } catch (e) {
console.log(e.message); console.log(e.message);
this.setState({ message: false }); this.setState({ message: false, isLoading: false });
return alert(loc.wallets.scanQrWif.bad_password); return alert(loc.wallets.scanQrWif.bad_password);
} }
this.setState({ message: false }); this.setState({ message: false, isLoading: false });
} }
for (let w of BlueApp.wallets) { for (let w of BlueApp.wallets) {
if (w.getSecret() === ret.data) { if (w.getSecret() === ret.data) {
// lookig for duplicates // lookig for duplicates
this.setState({ isLoading: false });
return alert(loc.wallets.scanQrWif.wallet_already_exists); // duplicate, not adding return alert(loc.wallets.scanQrWif.wallet_already_exists); // duplicate, not adding
} }
} }
@ -78,10 +80,10 @@ export default class ScanQrWif extends React.Component {
for (let w of BlueApp.wallets) { for (let w of BlueApp.wallets) {
if (w.getSecret() === hd.getSecret()) { if (w.getSecret() === hd.getSecret()) {
// lookig for duplicates // lookig for duplicates
this.setState({ isLoading: false });
return alert(loc.wallets.scanQrWif.wallet_already_exists); // duplicate, not adding return alert(loc.wallets.scanQrWif.wallet_already_exists); // duplicate, not adding
} }
} }
this.setState({ isLoading: true });
await hd.fetchTransactions(); await hd.fetchTransactions();
if (hd.getTransactions().length !== 0) { if (hd.getTransactions().length !== 0) {
await hd.fetchBalance(); await hd.fetchBalance();
@ -91,6 +93,7 @@ export default class ScanQrWif extends React.Component {
alert(loc.wallets.import.success); alert(loc.wallets.import.success);
this.props.navigation.popToTop(); this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500); setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
return; return;
} }
} }
@ -103,6 +106,7 @@ export default class ScanQrWif extends React.Component {
for (let w of BlueApp.wallets) { for (let w of BlueApp.wallets) {
if (w.getSecret() === hd.getSecret()) { if (w.getSecret() === hd.getSecret()) {
// lookig for duplicates // lookig for duplicates
this.setState({ isLoading: false });
return alert(loc.wallets.scanQrWif.wallet_already_exists); // duplicate, not adding return alert(loc.wallets.scanQrWif.wallet_already_exists); // duplicate, not adding
} }
} }
@ -115,6 +119,7 @@ export default class ScanQrWif extends React.Component {
alert(loc.wallets.import.success); alert(loc.wallets.import.success);
this.props.navigation.popToTop(); this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500); setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
return; return;
} }
// nope // nope
@ -130,6 +135,7 @@ export default class ScanQrWif extends React.Component {
await lnd.fetchBalance(); await lnd.fetchBalance();
} catch (Err) { } catch (Err) {
console.log(Err); console.log(Err);
this.setState({ isLoading: false });
return; return;
} }
@ -138,6 +144,7 @@ export default class ScanQrWif extends React.Component {
this.props.navigation.popToTop(); this.props.navigation.popToTop();
alert(loc.wallets.import.success); alert(loc.wallets.import.success);
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500); setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
return; return;
} }
// nope // nope
@ -165,6 +172,7 @@ export default class ScanQrWif extends React.Component {
await watchOnly.fetchTransactions(); await watchOnly.fetchTransactions();
await BlueApp.saveToDisk(); await BlueApp.saveToDisk();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500); setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
return; return;
} }
// nope // nope
@ -176,6 +184,7 @@ export default class ScanQrWif extends React.Component {
if (newWallet.getAddress() === false || newLegacyWallet.getAddress() === false) { if (newWallet.getAddress() === false || newLegacyWallet.getAddress() === false) {
alert(loc.wallets.scanQrWif.bad_wif); alert(loc.wallets.scanQrWif.bad_wif);
this.setState({ isLoading: false });
return; return;
} }
@ -196,7 +205,7 @@ export default class ScanQrWif extends React.Component {
alert(loc.wallets.scanQrWif.imported_wif + ret.data + loc.wallets.scanQrWif.with_address + newWallet.getAddress()); alert(loc.wallets.scanQrWif.imported_wif + ret.data + loc.wallets.scanQrWif.with_address + newWallet.getAddress());
} }
await BlueApp.saveToDisk(); await BlueApp.saveToDisk();
this.props.navigation.popToTop(); this.props.navigation.dismiss();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500); setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
} // end } // end
@ -210,7 +219,7 @@ export default class ScanQrWif extends React.Component {
render() { render() {
if (this.state.isLoading) { if (this.state.isLoading) {
return ( return (
<View style={{ flex: 1, paddingTop: 20 }}> <View style={{ flex: 1, paddingTop: 20, justifyContent: 'center', alignContent: 'center' }}>
<ActivityIndicator /> <ActivityIndicator />
</View> </View>
); );
@ -275,6 +284,7 @@ ScanQrWif.propTypes = {
navigation: PropTypes.shape({ navigation: PropTypes.shape({
goBack: PropTypes.func, goBack: PropTypes.func,
popToTop: PropTypes.func, popToTop: PropTypes.func,
dismiss: PropTypes.func,
navigate: PropTypes.func, navigate: PropTypes.func,
}), }),
}; };

View file

@ -3,20 +3,7 @@ import { Text, View, Image, FlatList, RefreshControl, TouchableOpacity, StatusBa
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { NavigationEvents } from 'react-navigation'; import { NavigationEvents } from 'react-navigation';
import { import { BlueText, ManageFundsBigButton, BlueSendButtonIcon, BlueReceiveButtonIcon, BlueTransactionListItem } from '../../BlueComponents';
BlueText,
BlueTransactionOnchainIcon,
ManageFundsBigButton,
BlueTransactionExpiredIcon,
BlueTransactionIncommingIcon,
BlueTransactionOutgoingIcon,
BlueTransactionPendingIcon,
BlueTransactionOffchainIcon,
BlueSendButtonIcon,
BlueReceiveButtonIcon,
BlueListItem,
BlueTransactionOffchainIncomingIcon,
} from '../../BlueComponents';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
import { BitcoinUnit } from '../../models/bitcoinUnits'; import { BitcoinUnit } from '../../models/bitcoinUnits';
import { LightningCustodianWallet } from '../../class'; import { LightningCustodianWallet } from '../../class';
@ -272,13 +259,6 @@ export default class WalletTransactions extends Component {
); );
}; };
txMemo(hash) {
if (BlueApp.tx_metadata[hash] && BlueApp.tx_metadata[hash]['memo']) {
return BlueApp.tx_metadata[hash]['memo'];
}
return '';
}
_keyExtractor = (_item, index) => index.toString(); _keyExtractor = (_item, index) => index.toString();
renderListHeaderComponent = () => { renderListHeaderComponent = () => {
@ -308,55 +288,8 @@ export default class WalletTransactions extends Component {
this.onWillBlur(); this.onWillBlur();
} }
rowTitle = item => { renderItem = item => {
if (item.type === 'user_invoice' || item.type === 'payment_request') { return <BlueTransactionListItem item={item.item} itemPriceUnit={this.state.wallet.getPreferredBalanceUnit()} />;
if (isNaN(item.value)) {
item.value = 0;
}
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.state.wallet.getPreferredBalanceUnit(), true).toString();
} else if (invoiceExpiration < now) {
if (item.ispaid) {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.state.wallet.getPreferredBalanceUnit(), true).toString();
} else {
return loc.lnd.expired;
}
}
} else {
return loc.formatBalanceWithoutSuffix(item.value && item.value, this.state.wallet.getPreferredBalanceUnit(), true).toString();
}
};
rowTitleStyle = item => {
let color = '#37c0a1';
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = '#37c0a1';
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = '#37c0a1';
} else {
color = '#FF0000';
}
}
} else if (item.value / 100000000 < 0) {
color = BlueApp.settings.foregroundColor;
}
return {
fontWeight: '600',
fontSize: 16,
color: color,
};
}; };
render() { render() {
@ -416,8 +349,9 @@ export default class WalletTransactions extends Component {
<FlatList <FlatList
ListHeaderComponent={this.renderListHeaderComponent} ListHeaderComponent={this.renderListHeaderComponent}
ListEmptyComponent={ ListEmptyComponent={
<View style={{ top: 50, minHeight: 200 }}> <View style={{ top: 50, minHeight: 200, paddingHorizontal: 16 }}>
<Text <Text
numberOfLines={0}
style={{ style={{
fontSize: 18, fontSize: 18,
color: '#9aa0aa', color: '#9aa0aa',
@ -425,7 +359,7 @@ export default class WalletTransactions extends Component {
}} }}
> >
{(this.isLightning() && {(this.isLightning() &&
'Lightning wallet should be used for your daily\ntransactions. Fees are unfairly cheap and\nspeed is blazing fast.') || 'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.') ||
loc.wallets.list.empty_txs1} loc.wallets.list.empty_txs1}
</Text> </Text>
<Text <Text
@ -435,7 +369,7 @@ export default class WalletTransactions extends Component {
textAlign: 'center', textAlign: 'center',
}} }}
> >
{(this.isLightning() && '\nTo start using it tap on "manage funds"\nand topup your balance') || {(this.isLightning() && '\nTo start using it tap on "manage funds" and topup your balance') ||
loc.wallets.list.empty_txs2} loc.wallets.list.empty_txs2}
</Text> </Text>
@ -465,110 +399,8 @@ export default class WalletTransactions extends Component {
refreshControl={<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.isTransactionsLoading} />} refreshControl={<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.isTransactionsLoading} />}
data={this.state.dataSource} data={this.state.dataSource}
keyExtractor={this._keyExtractor} keyExtractor={this._keyExtractor}
renderItem={rowData => { initialNumToRender={10}
return ( renderItem={this.renderItem}
<BlueListItem
avatar={(() => {
// is it lightning refill tx?
if (rowData.item.category === 'receive' && rowData.item.confirmations < 3) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
}
if (rowData.item.type && rowData.item.type === 'bitcoind_tx') {
return (
<View style={{ width: 25 }}>
<BlueTransactionOnchainIcon />
</View>
);
}
if (rowData.item.type === 'paid_invoice') {
// is it lightning offchain payment?
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIcon />
</View>
);
}
if (rowData.item.type === 'user_invoice' || rowData.item.type === 'payment_request') {
if (!rowData.item.ispaid) {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
const invoiceExpiration = rowData.item.timestamp + rowData.item.expire_time;
if (invoiceExpiration < now) {
return (
<View style={{ width: 25 }}>
<BlueTransactionExpiredIcon />
</View>
);
}
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionOffchainIncomingIcon />
</View>
);
}
}
if (!rowData.item.confirmations) {
return (
<View style={{ width: 25 }}>
<BlueTransactionPendingIcon />
</View>
);
} else if (rowData.item.value < 0) {
return (
<View style={{ width: 25 }}>
<BlueTransactionOutgoingIcon />
</View>
);
} else {
return (
<View style={{ width: 25 }}>
<BlueTransactionIncommingIcon />
</View>
);
}
})()}
title={loc.transactionTimeToReadable(rowData.item.received)}
subtitle={
(rowData.item.confirmations < 7 ? loc.transactions.list.conf + ': ' + rowData.item.confirmations + ' ' : '') +
this.txMemo(rowData.item.hash) +
(rowData.item.memo || '')
}
onPress={() => {
if (rowData.item.hash) {
navigate('TransactionDetails', {
hash: rowData.item.hash,
});
} else if (
rowData.item.type === 'user_invoice' ||
rowData.item.type === 'payment_request' ||
rowData.item.type === 'paid_invoice'
) {
this.props.navigation.navigate('LNDViewInvoice', {
invoice: rowData.item,
fromWallet: this.state.wallet,
isModal: false,
});
}
}}
badge={{
value: 3,
textStyle: { color: 'orange' },
containerStyle: { marginTop: 0 },
}}
hideChevron
rightTitle={this.rowTitle(rowData.item)}
rightTitleStyle={this.rowTitleStyle(rowData.item)}
/>
);
}}
/> />
</View> </View>
<View <View