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
[version]
^0.85.0
^0.86.0

4
.gitignore vendored
View file

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

View file

@ -4,7 +4,7 @@
import React, { Component } from 'react';
import Ionicons from 'react-native-vector-icons/Ionicons';
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 {
TouchableOpacity,
TouchableWithoutFeedback,
@ -16,9 +16,9 @@ import {
Dimensions,
Image,
SafeAreaView,
InputAccessoryView,
Clipboard,
Platform,
LayoutAnimation,
TextInput,
} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
@ -44,10 +44,11 @@ if (aspectRatio > 1.6) {
export class BlueButton extends Component {
render() {
const disabled = this.props.hasOwnProperty('disabled');
let backgroundColor = '#ccddf9';
if (disabled === true) {
backgroundColor = '#99a0ab';
let fontColor = '#0c2550';
if (this.props.hasOwnProperty('disabled') && this.props.disabled === true) {
backgroundColor = '#eef0f4';
fontColor = '#9aa0aa';
}
return (
<TouchableOpacity
@ -55,7 +56,7 @@ export class BlueButton extends Component {
flex: 1,
borderWidth: 0.7,
borderColor: 'transparent',
backgroundColor: this.props.hasOwnProperty('backgroundColor') ? this.props.backgroundColor : backgroundColor,
backgroundColor: backgroundColor,
minHeight: 45,
height: 45,
maxHeight: 45,
@ -68,7 +69,7 @@ export class BlueButton extends Component {
>
<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.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>
</TouchableOpacity>
);
@ -145,26 +146,18 @@ export class LightningButton extends Component {
export class BlueButtonLink extends Component {
render() {
// eslint-disable-next-line
this.props.buttonStyle = this.props.buttonStyle || {};
return (
<Button
activeOpacity={0.1}
delayPressIn={0}
{...this.props}
<TouchableOpacity
style={{
marginTop: 20,
borderWidth: 0.7,
borderColor: 'transparent',
minHeight: 60,
minWidth: 100,
height: 60,
justifyContent: 'center',
}}
buttonStyle={{
height: 45,
width: width / 1.5,
}}
backgroundColor="transparent"
color="#0c2550"
/>
{...this.props}
>
<Text style={{ color: '#0c2550', textAlign: 'center', fontSize: 16 }}>{this.props.title}</Text>
</TouchableOpacity>
);
}
}
@ -208,36 +201,32 @@ export class BlueCopyTextToClipboard extends Component {
text: '',
};
state = { hasTappedText: false };
constructor() {
super();
if (Platform.OS === 'android') UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
constructor(props) {
super(props);
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
}
this.state = { hasTappedText: false, address: props.text };
}
copyToClipboard = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring, () => {
this.setState({ hasTappedText: true }, () => {
Clipboard.setString(this.props.text);
this.setState({ address: loc.wallets.xpub.copiedToClipboard }, () => {
setTimeout(() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
this.setState({ hasTappedText: false });
this.setState({ hasTappedText: false, address: this.props.text });
}, 1000);
});
this.setState({ hasTappedText: true });
});
};
render() {
return (
<View style={{ justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<TouchableOpacity onPress={this.copyToClipboard} disabled={this.state.hasTappedText}>
<Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{this.props.text}
</Text>
{this.state.hasTappedText && (
<Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{loc.wallets.xpub.copiedToClipboard}
</Text>
)}
<Animated.Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{this.state.address}
</Animated.Text>
</TouchableOpacity>
</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 {
render() {
return (
@ -580,7 +590,7 @@ const stylesBlueIcon = StyleSheet.create({
paddingHorizontal: 14,
paddingTop: 8,
},
boxIncomming: {
boxIncoming: {
position: 'relative',
},
ball: {
@ -589,14 +599,14 @@ const stylesBlueIcon = StyleSheet.create({
borderRadius: 15,
backgroundColor: '#ccddf9',
},
ballIncomming: {
ballIncoming: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: '#d2f8d6',
transform: [{ rotate: '-45deg' }],
},
ballIncommingWithoutRotate: {
ballIncomingWithoutRotate: {
width: 30,
height: 30,
borderRadius: 15,
@ -659,12 +669,12 @@ export class BluePlusIcon extends Component {
}
}
export class BlueTransactionIncommingIcon extends Component {
export class BlueTransactionIncomingIcon extends Component {
render() {
return (
<View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}>
<View style={stylesBlueIcon.ballIncomming}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncoming}>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 8 }} />
</View>
</View>
@ -677,7 +687,7 @@ export class BlueTransactionPendingIcon extends Component {
render() {
return (
<View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ball}>
<Icon
{...this.props}
@ -698,7 +708,7 @@ export class BlueTransactionExpiredIcon extends Component {
render() {
return (
<View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoingWithoutRotate}>
<Icon {...this.props} name="hourglass-end" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 6 }} />
</View>
@ -712,8 +722,8 @@ export class BlueTransactionOnchainIcon extends Component {
render() {
return (
<View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}>
<View style={stylesBlueIcon.ballIncomming}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncoming}>
<Icon
{...this.props}
name="link"
@ -733,7 +743,7 @@ export class BlueTransactionOffchainIcon extends Component {
render() {
return (
<View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoingWithoutRotate}>
<Icon {...this.props} name="bolt" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 7 }} />
</View>
@ -747,8 +757,8 @@ export class BlueTransactionOffchainIncomingIcon extends Component {
render() {
return (
<View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}>
<View style={stylesBlueIcon.ballIncommingWithoutRotate}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncomingWithoutRotate}>
<Icon {...this.props} name="bolt" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 7 }} />
</View>
</View>
@ -761,7 +771,7 @@ export class BlueTransactionOutgoingIcon extends Component {
render() {
return (
<View {...this.props}>
<View style={stylesBlueIcon.boxIncomming}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoing}>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 8 }} />
</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 itemWidth = width * 0.82;
const sliderHeight = 190;
@ -1003,12 +1393,17 @@ export class WalletsCarousel extends Component {
_renderItem({ item, index }) {
let scaleValue = new Animated.Value(1.0);
let props = { duration: 50 };
if (Platform.OS === 'android') {
props['useNativeDriver'] = true;
}
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 = () => {
Animated.spring(scaleValue, { toValue: 1.0, duration: 100, useNativeDriver: Platform.OS === 'android' }).start();
props.toValue = 1.0;
Animated.spring(scaleValue, props).start();
};
if (!item) {
@ -1262,7 +1657,6 @@ export class BlueBitcoinAmount extends Component {
ref={textInput => (this.textInput = textInput)}
editable={!this.props.isLoading && !this.props.disabled}
value={amount}
autoFocus={this.props.pointerEvents !== 'none'}
placeholderTextColor={this.props.disabled ? '#99a0ab' : '#0f5cc0'}
style={{
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 About from './screen/settings/about';
import Releasenotes from './screen/settings/releasenotes';
import ReleaseNotes from './screen/settings/releasenotes';
import Selftest from './screen/selftest';
import Language from './screen/settings/language';
import Currency from './screen/settings/currency';
@ -87,9 +87,9 @@ const WalletsStackNavigator = createStackNavigator(
screen: About,
path: 'About',
},
Releasenotes: {
screen: Releasenotes,
path: 'Releasenotes',
ReleaseNotes: {
screen: ReleaseNotes,
path: 'ReleaseNotes',
},
Selftest: {
screen: Selftest,
@ -185,6 +185,15 @@ const CreateWalletStackNavigator = createStackNavigator({
},
});
const LightningScanInvoiceStackNavigator = createStackNavigator({
ScanLndInvoice: {
screen: ScanLndInvoice,
},
Success: {
screen: Success,
},
});
const MainBottomTabs = createStackNavigator(
{
Wallets: {
@ -241,7 +250,10 @@ const MainBottomTabs = createStackNavigator(
},
},
ScanLndInvoice: {
screen: ScanLndInvoice,
screen: LightningScanInvoiceStackNavigator,
navigationOptions: {
header: null,
},
},
ScanQrAddress: {
screen: sendScanQrAddress,

View file

@ -108,7 +108,10 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<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/lint_jar" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />

View file

@ -24,13 +24,13 @@ class BlueAppComponent extends React.Component {
this.state = { isMigratingData: true };
}
async setIsMigratingData() {
setIsMigratingData = async () => {
await BlueApp.startAndDecrypt();
this.setState({ isMigratingData: false });
}
};
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;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.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 */; };
2DF0FFEE2056DD460020B375 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.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 */; };
3EEBC6F85642487DA7C4EE35 /* AntDesign.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C4496FB303574862B40A878A /* AntDesign.ttf */; };
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 */; };
F21429E1449249038A7F3444 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 334051161886419EA186F4BA /* FontAwesome.ttf */; };
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 */
/* Begin PBXContainerItemProxy section */
@ -524,6 +525,13 @@
remoteGlobalIDString = 32D980DD1BE9F11C00FA27E5;
remoteInfo = RCTQRCodeLocalImage;
};
B47720652202510900DD0E81 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = TcpSockets;
};
/* End PBXContainerItemProxy 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>"; };
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>"; };
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>"; };
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>"; };
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>"; };
@ -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>"; };
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>"; };
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 */
/* Begin PBXFrameworksBuildPhase section */
@ -976,6 +984,7 @@
E7078D2FED444DA4B0BD57F9 /* libRCTWKWebView.a */,
FC98DC24A81A463AB8B2E6B1 /* libRNImagePicker.a */,
B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */,
9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */,
);
name = "Recovered References";
sourceTree = "<group>";
@ -1123,6 +1132,14 @@
name = Products;
sourceTree = "<group>";
};
B47720622202510900DD0E81 /* Products */ = {
isa = PBXGroup;
children = (
B47720662202510900DD0E81 /* libTcpSockets.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -1356,6 +1373,10 @@
ProductGroup = B40FE5CC21FAD27D005D5578 /* Products */;
ProjectRef = 9EA3788F4C6643B7B0182587 /* RNVectorIcons.xcodeproj */;
},
{
ProductGroup = B47720622202510900DD0E81 /* Products */;
ProjectRef = 910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */;
},
);
projectRoot = "";
targets = (
@ -1795,6 +1816,13 @@
remoteRef = B4327F5021FC1B9300F7ADFA /* PBXContainerItemProxy */;
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 */
/* Begin PBXResourcesBuildPhase section */
@ -2111,7 +2139,7 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet;
PRODUCT_NAME = BlueWallet;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
@ -2153,7 +2181,7 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet;
PRODUCT_NAME = BlueWallet;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";

View file

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

View file

@ -56,9 +56,11 @@
<key>NSCalendarsUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<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>
<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>
<string>This alert should not show up as we do not require this data</string>
<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>
<key>NSSpeechRecognitionUsageDescription</key>
<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>
<array>
<string>AntDesign.ttf</string>

View file

@ -217,6 +217,7 @@ module.exports = {
refill_lnd_balance: 'Lade deine Lightning Wallet auf',
refill: 'Aufladen',
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' },
JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP' },
MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX' },
MYR: { endPointKey: 'MYR', symbol: 'RM', locale: 'ms-MY' },
PLN: { endPointKey: 'PLN', symbol: 'zł', locale: 'pl-PL' },
RUB: { endPointKey: 'RUB', symbol: '₽', locale: 'ru-RU' },
SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' },

View file

@ -1,6 +1,8 @@
import Frisbee from 'frisbee';
export class NetworkTransactionFee {
static StorageKey = 'NetworkTransactionFee';
constructor(fastestFee = 1, halfHourFee = 1, hourFee = 1) {
this.fastestFee = fastestFee;
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": {
"babel-eslint": "^10.0.1",
"babel-jest": "^24.0.0",
"eslint": "^5.12.1",
"eslint": "^5.13.0",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-import": "^2.15.0",
"eslint-plugin-node": "^8.0.1",
@ -47,31 +47,31 @@
"crypto-js": "^3.1.9-1",
"dayjs": "^1.8.0",
"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-react": "^7.0.2",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-standard": "^4.0.0",
"frisbee": "^1.6.4",
"frisbee": "^2.0.5",
"intl": "^1.2.5",
"mocha": "^5.2.0",
"node-libs-react-native": "^1.0.1",
"path-browserify": "0.0.0",
"prettier": "^1.16.1",
"path-browserify": "^1.0.0",
"prettier": "^1.16.3",
"process": "^0.11.10",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-localization": "^1.0.10",
"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-device-info": "^0.25.1",
"react-native-device-info": "^0.26.1",
"react-native-elements": "^0.19.0",
"react-native-flexi-radio-button": "^0.2.2",
"react-native-fs": "^2.13.3",
"react-native-gesture-handler": "^1.0.15",
"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-level-fs": "^3.0.1",
"react-native-linear-gradient": "^2.5.3",
@ -81,20 +81,20 @@
"react-native-qrcode": "^0.2.7",
"react-native-randombytes": "^3.5.2",
"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-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-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-navigation": "^3.0.9",
"react-navigation": "^3.1.2",
"react-test-render": "^1.1.1",
"readable-stream": "^1.1.14",
"readable-stream": "^3.1.1",
"request-promise-native": "^1.0.5",
"secure-random": "^1.1.1",
"stream-browserify": "^1.0.0",
"stream-browserify": "^2.0.2",
"util": "^0.11.1",
"wif": "^2.0.1"
},

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -18,7 +18,9 @@ export default class Success extends Component {
this.state = {
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,9 +53,10 @@ export default class Success extends Component {
alignSelf: 'flex-end',
}}
>
{' ' + BitcoinUnit.BTC}
{' ' + this.state.amountUnit}
</Text>
</View>
{this.state.fee > 0 && (
<Text
style={{
color: '#37c0a1',
@ -66,6 +69,22 @@ export default class Success extends Component {
>
{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>
<View
style={{
@ -87,7 +106,6 @@ export default class Success extends Component {
this.props.navigation.dismiss();
}}
title={loc.send.success.done}
style={{ maxWidth: 263, paddingHorizontal: 56 }}
/>
</BlueCard>
</SafeBlueArea>
@ -97,10 +115,10 @@ export default class Success extends Component {
Success.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,
getParam: PropTypes.function,
navigate: PropTypes.function,
dismiss: PropTypes.function,
goBack: PropTypes.func,
getParam: PropTypes.func,
navigate: PropTypes.func,
dismiss: PropTypes.func,
state: PropTypes.shape({
params: PropTypes.shape({
amount: PropTypes.string,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -72,7 +72,7 @@ export default class WalletsAdd extends Component {
return (
<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>
<BlueFormLabel>{loc.wallets.add.wallet_name}</BlueFormLabel>
<View

View file

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

View file

@ -215,6 +215,9 @@ export default class WalletsImport extends Component {
this.setLabel(text);
}}
/>
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
<BlueSpacing20 />
<View
style={{
@ -245,8 +248,6 @@ export default class WalletsImport extends Component {
}}
/>
</View>
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
</SafeBlueArea>
);
}

View file

@ -1,20 +1,6 @@
import React, { Component } from 'react';
import { View, TouchableOpacity, Text, FlatList, RefreshControl, ScrollView } from 'react-native';
import {
BlueTransactionOnchainIcon,
BlueLoading,
SafeBlueArea,
WalletsCarousel,
BlueTransactionIncommingIcon,
BlueTransactionOutgoingIcon,
BlueTransactionPendingIcon,
BlueTransactionOffchainIcon,
BlueTransactionExpiredIcon,
BlueList,
BlueListItem,
BlueHeaderDefaultMain,
BlueTransactionOffchainIncomingIcon,
} from '../../BlueComponents';
import { BlueLoading, SafeBlueArea, WalletsCarousel, BlueList, BlueHeaderDefaultMain, BlueListTransactionItem } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
import { NavigationEvents } from 'react-navigation';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
@ -232,60 +218,9 @@ export default class WalletsList extends Component {
}
};
rowTitle = 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, 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,
};
};
_renderItem = data => <BlueListTransactionItem item={data.item} itemPriceUnit={data.item.walletPreferredBalanceUnit} />;
render() {
const { navigate } = this.props.navigation;
if (this.state.isLoading) {
return <BlueLoading />;
}
@ -338,117 +273,7 @@ export default class WalletsList extends Component {
data={this.state.dataSource}
extraData={this.state.dataSource}
keyExtractor={this._keyExtractor}
renderItem={rowData => {
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)}
/>
);
}}
renderItem={this._renderItem}
/>
</BlueList>
</ScrollView>

View file

@ -28,6 +28,7 @@ export default class ScanQrWif extends React.Component {
};
async onBarCodeScanned(ret) {
this.setState({ isLoading: true });
if (+new Date() - this.lastTimeIveBeenHere < 6000) {
this.lastTimeIveBeenHere = +new Date();
return;
@ -57,16 +58,17 @@ export default class ScanQrWif extends React.Component {
ret.data = wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed);
} catch (e) {
console.log(e.message);
this.setState({ message: false });
this.setState({ message: false, isLoading: false });
return alert(loc.wallets.scanQrWif.bad_password);
}
this.setState({ message: false });
this.setState({ message: false, isLoading: false });
}
for (let w of BlueApp.wallets) {
if (w.getSecret() === ret.data) {
// lookig for duplicates
this.setState({ isLoading: false });
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) {
if (w.getSecret() === hd.getSecret()) {
// lookig for duplicates
this.setState({ isLoading: false });
return alert(loc.wallets.scanQrWif.wallet_already_exists); // duplicate, not adding
}
}
this.setState({ isLoading: true });
await hd.fetchTransactions();
if (hd.getTransactions().length !== 0) {
await hd.fetchBalance();
@ -91,6 +93,7 @@ export default class ScanQrWif extends React.Component {
alert(loc.wallets.import.success);
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
return;
}
}
@ -103,6 +106,7 @@ export default class ScanQrWif extends React.Component {
for (let w of BlueApp.wallets) {
if (w.getSecret() === hd.getSecret()) {
// lookig for duplicates
this.setState({ isLoading: false });
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);
this.props.navigation.popToTop();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
return;
}
// nope
@ -130,6 +135,7 @@ export default class ScanQrWif extends React.Component {
await lnd.fetchBalance();
} catch (Err) {
console.log(Err);
this.setState({ isLoading: false });
return;
}
@ -138,6 +144,7 @@ export default class ScanQrWif extends React.Component {
this.props.navigation.popToTop();
alert(loc.wallets.import.success);
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
return;
}
// nope
@ -165,6 +172,7 @@ export default class ScanQrWif extends React.Component {
await watchOnly.fetchTransactions();
await BlueApp.saveToDisk();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
this.setState({ isLoading: false });
return;
}
// nope
@ -176,6 +184,7 @@ export default class ScanQrWif extends React.Component {
if (newWallet.getAddress() === false || newLegacyWallet.getAddress() === false) {
alert(loc.wallets.scanQrWif.bad_wif);
this.setState({ isLoading: false });
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());
}
await BlueApp.saveToDisk();
this.props.navigation.popToTop();
this.props.navigation.dismiss();
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500);
} // end
@ -210,7 +219,7 @@ export default class ScanQrWif extends React.Component {
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 20 }}>
<View style={{ flex: 1, paddingTop: 20, justifyContent: 'center', alignContent: 'center' }}>
<ActivityIndicator />
</View>
);
@ -275,6 +284,7 @@ ScanQrWif.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.func,
popToTop: PropTypes.func,
dismiss: 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 PropTypes from 'prop-types';
import { NavigationEvents } from 'react-navigation';
import {
BlueText,
BlueTransactionOnchainIcon,
ManageFundsBigButton,
BlueTransactionExpiredIcon,
BlueTransactionIncommingIcon,
BlueTransactionOutgoingIcon,
BlueTransactionPendingIcon,
BlueTransactionOffchainIcon,
BlueSendButtonIcon,
BlueReceiveButtonIcon,
BlueListItem,
BlueTransactionOffchainIncomingIcon,
} from '../../BlueComponents';
import { BlueText, ManageFundsBigButton, BlueSendButtonIcon, BlueReceiveButtonIcon, BlueTransactionListItem } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
import { BitcoinUnit } from '../../models/bitcoinUnits';
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();
renderListHeaderComponent = () => {
@ -308,55 +288,8 @@ export default class WalletTransactions extends Component {
this.onWillBlur();
}
rowTitle = 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.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,
};
renderItem = item => {
return <BlueTransactionListItem item={item.item} itemPriceUnit={this.state.wallet.getPreferredBalanceUnit()} />;
};
render() {
@ -416,8 +349,9 @@ export default class WalletTransactions extends Component {
<FlatList
ListHeaderComponent={this.renderListHeaderComponent}
ListEmptyComponent={
<View style={{ top: 50, minHeight: 200 }}>
<View style={{ top: 50, minHeight: 200, paddingHorizontal: 16 }}>
<Text
numberOfLines={0}
style={{
fontSize: 18,
color: '#9aa0aa',
@ -425,7 +359,7 @@ export default class WalletTransactions extends Component {
}}
>
{(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}
</Text>
<Text
@ -435,7 +369,7 @@ export default class WalletTransactions extends Component {
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}
</Text>
@ -465,110 +399,8 @@ export default class WalletTransactions extends Component {
refreshControl={<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.isTransactionsLoading} />}
data={this.state.dataSource}
keyExtractor={this._keyExtractor}
renderItem={rowData => {
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'
) {
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)}
/>
);
}}
initialNumToRender={10}
renderItem={this.renderItem}
/>
</View>
<View