mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 15:04:50 +01:00
ADD: New transaction Confirm UI REF: Improve broadcasting experience #66 FIX: Satoshis to local currency in Send screen FIX: Fixed Wallet export QR code width FIX: Invalid BIP21 URI at value #108 FIX: #109 TypeError: undefined is not an object (evaluating 'this.state.tx.inputs.length')
This commit is contained in:
parent
a43b4b99a1
commit
6bc435fd48
16 changed files with 472 additions and 92 deletions
|
@ -21,6 +21,8 @@ import receiveDetails from './screen/receive/details';
|
|||
import sendDetails from './screen/send/details';
|
||||
import sendScanQrAddress from './screen/send/scanQrAddress';
|
||||
import sendCreate from './screen/send/create';
|
||||
import Confirm from './screen/send/confirm';
|
||||
import Success from './screen/send/success';
|
||||
|
||||
import ManageFunds from './screen/lnd/manageFunds';
|
||||
import ScanLndInvoice from './screen/lnd/scanLndInvoice';
|
||||
|
@ -55,10 +57,22 @@ const WalletsStackNavigator = createStackNavigator({
|
|||
},
|
||||
});
|
||||
|
||||
const SuccessTransactionStackNavigation = createStackNavigator(
|
||||
{
|
||||
Success: {
|
||||
screen: Success,
|
||||
},
|
||||
},
|
||||
{ mode: 'modal', headerMode: 'none' },
|
||||
);
|
||||
|
||||
const CreateTransactionStackNavigator = createStackNavigator({
|
||||
SendDetails: {
|
||||
screen: sendDetails,
|
||||
},
|
||||
Confirm: {
|
||||
screen: Confirm,
|
||||
},
|
||||
CreateTransaction: {
|
||||
screen: sendCreate,
|
||||
navigationOptions: {
|
||||
|
@ -109,6 +123,13 @@ const Tabs = createStackNavigator(
|
|||
TransactionDetails: {
|
||||
screen: details,
|
||||
},
|
||||
Success: {
|
||||
screen: SuccessTransactionStackNavigation,
|
||||
navigationOptions: {
|
||||
header: null,
|
||||
gesturesEnabled: false,
|
||||
},
|
||||
},
|
||||
RBF: {
|
||||
screen: rbf,
|
||||
},
|
||||
|
|
11
loc/en.js
11
loc/en.js
|
@ -89,7 +89,7 @@ module.exports = {
|
|||
conf: 'conf',
|
||||
},
|
||||
details: {
|
||||
title: 'transaction details',
|
||||
title: 'Transaction details',
|
||||
from: 'Input',
|
||||
to: 'Output',
|
||||
copy: 'Copy',
|
||||
|
@ -103,6 +103,7 @@ module.exports = {
|
|||
fee_field_is_not_valid: 'Fee field is not valid',
|
||||
address_field_is_not_valid: 'Address field is not valid',
|
||||
total_exceeds_balance: 'The sending amount exceeds the available balance.',
|
||||
create_tx_error: 'There was an error creating the transaction. Please, make sure the address is valid.',
|
||||
address: 'address',
|
||||
amount_placeholder: 'amount to send (in BTC)',
|
||||
fee_placeholder: 'plus transaction fee (in BTC)',
|
||||
|
@ -113,7 +114,15 @@ module.exports = {
|
|||
create: 'Create',
|
||||
remaining_balance: 'Remaining balance',
|
||||
},
|
||||
confirm: {
|
||||
header: 'Confirm',
|
||||
sendNow: 'Send now',
|
||||
},
|
||||
success: {
|
||||
done: 'Done',
|
||||
},
|
||||
create: {
|
||||
details: 'Details',
|
||||
title: 'create transaction',
|
||||
error: 'Error creating transaction. Invalid address or send amount?',
|
||||
go_back: 'Go Back',
|
||||
|
|
|
@ -97,6 +97,13 @@ module.exports = {
|
|||
},
|
||||
send: {
|
||||
header: 'enviar',
|
||||
confirm: {
|
||||
header: 'Confirm',
|
||||
sendNow: 'Send now',
|
||||
},
|
||||
success: {
|
||||
done: 'Done',
|
||||
},
|
||||
details: {
|
||||
title: 'Crear Transaccion',
|
||||
amount_field_is_not_valid: 'La cantidad no es válida',
|
||||
|
@ -106,6 +113,7 @@ module.exports = {
|
|||
amount_placeholder: 'cantidad para enviar (in BTC)',
|
||||
fee_placeholder: 'más tasa de transaccion (in BTC)',
|
||||
note_placeholder: 'comentario (para ti mismo)',
|
||||
create_tx_error: 'Se ha producido un error al crear la transacción. Por favor, asegúrese de que la dirección es válida.',
|
||||
cancel: 'Cancelar',
|
||||
scan: 'Escaniar',
|
||||
address: 'Direccion',
|
||||
|
@ -116,6 +124,7 @@ module.exports = {
|
|||
},
|
||||
create: {
|
||||
title: 'Crear una Transaccion',
|
||||
details: 'Detalles',
|
||||
error: 'Error al crear una transacción. ¿Dirección o cantidad estan invalidas?',
|
||||
go_back: 'Regresar',
|
||||
this_is_hex: 'Este es representacion hex de transacción, firmado y listo para ser transmitido a la red. ¿Continuar?',
|
||||
|
|
|
@ -79,6 +79,8 @@ strings.formatBalance = (balance, unit) => {
|
|||
return b.multipliedBy(1000000).toString() + ' ' + BitcoinUnit.BITS;
|
||||
} else if (unit === BitcoinUnit.SATOSHIS) {
|
||||
return (b.times(conversion).toString() + ' ' + BitcoinUnit.SATOSHIS).replace(/\./g, '');
|
||||
} else if (unit === BitcoinUnit.SATS) {
|
||||
return (b.times(conversion).toString() + ' ' + BitcoinUnit.SATS).replace(/\./g, '');
|
||||
}
|
||||
}
|
||||
return balance + ' ' + BitcoinUnit.BTC;
|
||||
|
|
|
@ -98,6 +98,13 @@ module.exports = {
|
|||
},
|
||||
send: {
|
||||
header: 'Enviar',
|
||||
confirm: {
|
||||
header: 'Confirm',
|
||||
sendNow: 'Send now',
|
||||
},
|
||||
success: {
|
||||
done: 'Done',
|
||||
},
|
||||
details: {
|
||||
title: 'Criar Transacção',
|
||||
amount_field_is_not_valid: 'Campo de quantia não é válido',
|
||||
|
@ -106,6 +113,7 @@ module.exports = {
|
|||
receiver_placeholder: 'endereço de envio aqui',
|
||||
amount_placeholder: 'quantia a enviar (em BTC)',
|
||||
fee_placeholder: 'mais a taxa de transacção (em BTC)',
|
||||
create_tx_error: 'There was an error creating the transaction. Please, make sure the address is valid.',
|
||||
note_placeholder: 'Nota pessoal',
|
||||
cancel: 'Cancelar',
|
||||
scan: 'Scanear',
|
||||
|
@ -117,6 +125,7 @@ module.exports = {
|
|||
},
|
||||
create: {
|
||||
title: 'Criar Transacção',
|
||||
details: 'Details',
|
||||
error: 'Erro ao criar transação. Endereço inválido ou quantia?',
|
||||
go_back: 'Voltar',
|
||||
this_is_hex: 'Este é o hex da transação, assinado e pronto para ser difundido para a network. Continuar?',
|
||||
|
|
|
@ -97,6 +97,13 @@ module.exports = {
|
|||
},
|
||||
send: {
|
||||
header: 'Enviar',
|
||||
confirm: {
|
||||
header: 'Confirm',
|
||||
sendNow: 'Send now',
|
||||
},
|
||||
success: {
|
||||
done: 'Done',
|
||||
},
|
||||
details: {
|
||||
title: 'Criar Transacção',
|
||||
amount_field_is_not_valid: 'Campo de quantia não é válido',
|
||||
|
@ -105,6 +112,7 @@ module.exports = {
|
|||
receiver_placeholder: 'endereço de envio aqui',
|
||||
amount_placeholder: 'quantia a enviar (em BTC)',
|
||||
fee_placeholder: 'mais a taxa de transacção (em BTC)',
|
||||
create_tx_error: 'There was an error creating the transaction. Please, make sure the address is valid.',
|
||||
note_placeholder: 'Nota pessoal',
|
||||
total_exceeds_balance: 'Total amount exceeds available balance.',
|
||||
cancel: 'Cancelar',
|
||||
|
@ -116,6 +124,7 @@ module.exports = {
|
|||
},
|
||||
create: {
|
||||
title: 'Criar Transacção',
|
||||
details: 'Details',
|
||||
error: 'Erro ao criar transacção. Endereço inválido ou quantia?',
|
||||
go_back: 'Voltar',
|
||||
this_is_hex: 'Este é o hex da transacção, assinado e pronto para ser difundido para a network. Continuar?',
|
||||
|
|
|
@ -96,6 +96,13 @@ module.exports = {
|
|||
},
|
||||
send: {
|
||||
header: 'Отправить',
|
||||
confirm: {
|
||||
header: 'Confirm',
|
||||
sendNow: 'Send now',
|
||||
},
|
||||
success: {
|
||||
done: 'Done',
|
||||
},
|
||||
details: {
|
||||
title: 'Создать Транзакцию',
|
||||
amount_field_is_not_valid: 'Поле не валидно',
|
||||
|
@ -105,6 +112,7 @@ module.exports = {
|
|||
amount_placeholder: 'сколько отправить (в BTC)',
|
||||
fee_placeholder: 'плюс комиссия за перевод (в BTC)',
|
||||
note_placeholder: 'примечание платежа',
|
||||
create_tx_error: 'There was an error creating the transaction. Please, make sure the address is valid.',
|
||||
cancel: 'Отмена',
|
||||
scan: 'Скан QR',
|
||||
create: 'Создать',
|
||||
|
@ -115,6 +123,7 @@ module.exports = {
|
|||
},
|
||||
create: {
|
||||
title: 'Создать Транзакцию',
|
||||
details: 'Details',
|
||||
error: 'Ошибка при создании транзакции. Неправильный адрес назначения или недостаточно средств?',
|
||||
go_back: 'Назад',
|
||||
this_is_hex: 'Это данные транзакции. Транзакция подписана и готова быть транслирована в сеть. Продолжить?',
|
||||
|
|
|
@ -104,6 +104,7 @@ module.exports = {
|
|||
receiver_placeholder: 'Адреса одержувача',
|
||||
amount_placeholder: 'скільки відправити (в BTC)',
|
||||
fee_placeholder: 'плюс комісія за переказ (в BTC)',
|
||||
create_tx_error: 'There was an error creating the transaction. Please, make sure the address is valid.',
|
||||
note_placeholder: 'примітка платежу',
|
||||
cancel: 'Відміна',
|
||||
scan: 'Скан QR',
|
||||
|
@ -113,8 +114,16 @@ module.exports = {
|
|||
create: 'Створити',
|
||||
remaining_balance: 'Залишок балансу',
|
||||
},
|
||||
confirm: {
|
||||
header: 'Confirm',
|
||||
sendNow: 'Send now',
|
||||
},
|
||||
success: {
|
||||
done: 'Done',
|
||||
},
|
||||
create: {
|
||||
title: 'Створити Транзакцію',
|
||||
details: 'Details',
|
||||
error: 'Помилка при створенні транзакції. Невiрна адреса призначення або недостатньо коштiв?',
|
||||
go_back: 'Назад',
|
||||
this_is_hex: 'Це дані транзакції. Транзакція підписана і готова бути трансльована в мережу. Продовжити?',
|
||||
|
|
|
@ -3,4 +3,5 @@ export const BitcoinUnit = Object.freeze({
|
|||
MBTC: 'mBTC',
|
||||
BITS: 'bits',
|
||||
SATOSHIS: 'satoshis',
|
||||
SATS: 'sats',
|
||||
});
|
||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "BlueWallet",
|
||||
"version": "3.0.0",
|
||||
"version": "3.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
173
screen/send/confirm.js
Normal file
173
screen/send/confirm.js
Normal file
|
@ -0,0 +1,173 @@
|
|||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import { ActivityIndicator, TouchableOpacity, StyleSheet, View } from 'react-native';
|
||||
import { Text } from 'react-native-elements';
|
||||
import { BlueButton, SafeBlueArea, BlueCard, BlueSpacing40, BlueHeaderDefaultSub } from '../../BlueComponents';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import PropTypes from 'prop-types';
|
||||
let loc = require('../../loc');
|
||||
let EV = require('../../events');
|
||||
|
||||
export default class Confirm extends Component {
|
||||
static navigationOptions = {
|
||||
headerStyle: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderBottomWidth: 0,
|
||||
},
|
||||
headerTintColor: '#0c2550',
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
console.log('send/create constructor');
|
||||
|
||||
this.state = {
|
||||
isLoading: false,
|
||||
amount: props.navigation.getParam('amount'),
|
||||
fee: props.navigation.getParam('fee'),
|
||||
address: props.navigation.getParam('address'),
|
||||
memo: props.navigation.getParam('memo'),
|
||||
size: Math.round(props.navigation.getParam('tx').length / 2),
|
||||
tx: props.navigation.getParam('tx'),
|
||||
satoshiPerByte: props.navigation.getParam('satoshiPerByte'),
|
||||
fromWallet: props.navigation.getParam('fromWallet'),
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
console.log('send/create - componentDidMount');
|
||||
console.log('address = ', this.state.address);
|
||||
}
|
||||
|
||||
broadcast() {
|
||||
this.setState({ isLoading: true }, async () => {
|
||||
let result = await this.state.fromWallet.broadcastTx(this.state.tx);
|
||||
console.log('broadcast result = ', result);
|
||||
if (typeof result === 'string') {
|
||||
result = JSON.parse(result);
|
||||
}
|
||||
this.setState({ isLoading: false });
|
||||
if (result && result.error) {
|
||||
alert(JSON.stringify(result.error));
|
||||
} else {
|
||||
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // someone should fetch txs
|
||||
this.props.navigation.navigate('Success', {
|
||||
fee: Number(this.state.fee),
|
||||
amount: this.state.amount,
|
||||
address: this.state.address,
|
||||
dismissModal: () => this.props.navigation.dismiss(),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 19 }}>
|
||||
<BlueHeaderDefaultSub leftText={loc.send.confirm.header.toLowerCase()} rightComponent={null} />
|
||||
<BlueCard style={{ alignItems: 'center', flex: 1 }}>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'center', paddingTop: 16, paddingBottom: 16 }}>
|
||||
<Text
|
||||
style={{
|
||||
color: '#0f5cc0',
|
||||
fontSize: 36,
|
||||
fontWeight: '600',
|
||||
}}
|
||||
>
|
||||
{this.state.amount}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: '#0f5cc0',
|
||||
fontSize: 16,
|
||||
marginHorizontal: 4,
|
||||
paddingBottom: 6,
|
||||
fontWeight: '600',
|
||||
alignSelf: 'flex-end',
|
||||
}}
|
||||
>
|
||||
{' ' + BitcoinUnit.BTC}
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
color: '#37c0a1',
|
||||
fontSize: 14,
|
||||
marginHorizontal: 4,
|
||||
paddingBottom: 6,
|
||||
fontWeight: '500',
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
>
|
||||
{loc.send.create.fee}: {loc.formatBalance(this.state.fee, BitcoinUnit.SATS)}
|
||||
</Text>
|
||||
</BlueCard>
|
||||
<BlueCard>
|
||||
<Text style={styles.transactionDetailsTitle}>{loc.send.create.to}</Text>
|
||||
<Text style={styles.transactionDetailsSubtitle}>{this.state.address}</Text>
|
||||
<BlueSpacing40 />
|
||||
{this.state.isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<BlueButton
|
||||
onPress={() => this.broadcast()}
|
||||
title={loc.send.confirm.sendNow}
|
||||
style={{ maxWidth: 263, paddingHorizontal: 56 }}
|
||||
/>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
style={{ marginVertical: 24 }}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('CreateTransaction', {
|
||||
amount: this.state.amount,
|
||||
fee: this.state.fee,
|
||||
address: this.state.address,
|
||||
memo: this.state.memo,
|
||||
tx: this.state.tx,
|
||||
satoshiPerByte: this.state.satoshiPerByte,
|
||||
})
|
||||
}
|
||||
>
|
||||
<Text style={{ color: '#0c2550', fontSize: 15, fontWeight: '500', alignSelf: 'center' }}>{loc.transactions.details.title}</Text>
|
||||
</TouchableOpacity>
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
transactionDetailsTitle: {
|
||||
color: '#0c2550',
|
||||
fontWeight: '500',
|
||||
fontSize: 17,
|
||||
marginBottom: 2,
|
||||
},
|
||||
transactionDetailsSubtitle: {
|
||||
color: '#9aa0aa',
|
||||
fontWeight: '500',
|
||||
fontSize: 15,
|
||||
marginBottom: 20,
|
||||
},
|
||||
});
|
||||
|
||||
Confirm.propTypes = {
|
||||
navigation: PropTypes.shape({
|
||||
goBack: PropTypes.function,
|
||||
getParam: PropTypes.function,
|
||||
navigate: PropTypes.function,
|
||||
dismiss: PropTypes.function,
|
||||
state: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
amount: PropTypes.string,
|
||||
fee: PropTypes.number,
|
||||
address: PropTypes.string,
|
||||
memo: PropTypes.string,
|
||||
fromWallet: PropTypes.shape({
|
||||
fromAddress: PropTypes.string,
|
||||
fromSecret: PropTypes.string,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
};
|
|
@ -1,13 +1,9 @@
|
|||
/* global alert */
|
||||
import React, { Component } from 'react';
|
||||
import { TextInput, ActivityIndicator, TouchableOpacity, Clipboard, StyleSheet, ScrollView } from 'react-native';
|
||||
import { TextInput, TouchableOpacity, Clipboard, StyleSheet, ScrollView } from 'react-native';
|
||||
import { Text } from 'react-native-elements';
|
||||
import { BlueButton, SafeBlueArea, BlueCard, BlueText } from '../../BlueComponents';
|
||||
import { BlueHeaderDefaultSub, SafeBlueArea, BlueCard, BlueText } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
/** @type {AppStorage} */
|
||||
// let BlueApp = require('../../BlueApp');
|
||||
let loc = require('../../loc');
|
||||
let EV = require('../../events');
|
||||
|
||||
export default class SendCreate extends Component {
|
||||
constructor(props) {
|
||||
|
@ -16,14 +12,13 @@ export default class SendCreate extends Component {
|
|||
|
||||
this.state = {
|
||||
isLoading: false,
|
||||
amount: props.navigation.state.params.amount,
|
||||
fee: props.navigation.state.params.fee,
|
||||
address: props.navigation.state.params.address,
|
||||
memo: props.navigation.state.params.memo,
|
||||
amount: props.navigation.getParam('amount'),
|
||||
fee: props.navigation.getParam('fee'),
|
||||
address: props.navigation.getParam('address'),
|
||||
memo: props.navigation.getParam('memo'),
|
||||
size: Math.round(props.navigation.getParam('tx').length / 2),
|
||||
tx: props.navigation.getParam('tx'),
|
||||
satoshiPerByte: props.navigation.getParam('satoshiPerByte'),
|
||||
fromWallet: props.navigation.getParam('fromWallet'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -32,27 +27,10 @@ export default class SendCreate extends Component {
|
|||
console.log('address = ', this.state.address);
|
||||
}
|
||||
|
||||
broadcast() {
|
||||
this.setState({ isLoading: true }, async () => {
|
||||
let result = await this.state.fromWallet.broadcastTx(this.state.tx);
|
||||
console.log('broadcast result = ', result);
|
||||
if (typeof result === 'string') {
|
||||
result = JSON.parse(result);
|
||||
}
|
||||
this.setState({ isLoading: false });
|
||||
if (result && result.error) {
|
||||
alert(JSON.stringify(result.error));
|
||||
} else {
|
||||
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // someone should fetch txs
|
||||
alert('Transaction has been successfully broadcasted. Your transaction ID is: ' + JSON.stringify(result.result));
|
||||
this.props.navigation.navigate('Wallets');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 19 }}>
|
||||
<BlueHeaderDefaultSub leftText={loc.send.create.details.toLowerCase()} rightComponent={null} />
|
||||
<ScrollView>
|
||||
<BlueCard style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueText style={{ color: '#0c2550', fontWeight: '500' }}>{loc.send.create.this_is_hex}</BlueText>
|
||||
|
@ -79,11 +57,6 @@ export default class SendCreate extends Component {
|
|||
<TouchableOpacity style={{ marginVertical: 24 }} onPress={() => Clipboard.setString(this.state.tx)}>
|
||||
<Text style={{ color: '#0c2550', fontSize: 15, fontWeight: '500', alignSelf: 'center' }}>Copy and broadcast later</Text>
|
||||
</TouchableOpacity>
|
||||
{this.state.isLoading ? (
|
||||
<ActivityIndicator />
|
||||
) : (
|
||||
<BlueButton onPress={() => this.broadcast()} title={loc.send.details.send} style={{ maxWidth: 263, paddingHorizontal: 56 }} />
|
||||
)}
|
||||
</BlueCard>
|
||||
<BlueCard>
|
||||
<Text style={styles.transactionDetailsTitle}>{loc.send.create.to}</Text>
|
||||
|
@ -130,16 +103,13 @@ SendCreate.propTypes = {
|
|||
goBack: PropTypes.function,
|
||||
getParam: PropTypes.function,
|
||||
navigate: PropTypes.function,
|
||||
dismiss: PropTypes.function,
|
||||
state: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
amount: PropTypes.string,
|
||||
fee: PropTypes.number,
|
||||
address: PropTypes.string,
|
||||
memo: PropTypes.string,
|
||||
fromWallet: PropTypes.shape({
|
||||
fromAddress: PropTypes.string,
|
||||
fromSecret: PropTypes.string,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
|
|
@ -25,6 +25,7 @@ let BigNumber = require('bignumber.js');
|
|||
let BlueApp = require('../../BlueApp');
|
||||
let loc = require('../../loc');
|
||||
let bitcoin = require('bitcoinjs-lib');
|
||||
let currency = require('../../currency');
|
||||
|
||||
const btcAddressRx = /^[a-zA-Z0-9]{26,35}$/;
|
||||
|
||||
|
@ -46,7 +47,7 @@ export default class SendDetails extends Component {
|
|||
let fromAddress;
|
||||
if (props.navigation.state.params) fromAddress = props.navigation.state.params.fromAddress;
|
||||
let fromSecret;
|
||||
if (props.navigation.state.params.fromSecret) fromSecret = props.navigation.state.params.fromSecret;
|
||||
if (props.navigation.state.params) fromSecret = props.navigation.state.params.fromSecret;
|
||||
let fromWallet = {};
|
||||
|
||||
let startTime2 = Date.now();
|
||||
|
@ -204,7 +205,14 @@ export default class SendDetails extends Component {
|
|||
}
|
||||
|
||||
let startTime = Date.now();
|
||||
tx = this.state.fromWallet.createTx(utxo, this.state.amount, fee, this.state.address, this.state.memo);
|
||||
try {
|
||||
tx = this.state.fromWallet.createTx(utxo, this.state.amount, fee, this.state.address, this.state.memo);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
alert(loc.send.details.create_tx_error);
|
||||
this.setState({ isLoading: false });
|
||||
return;
|
||||
}
|
||||
let endTime = Date.now();
|
||||
console.log('create tx ', (endTime - startTime) / 1000, 'sec');
|
||||
|
||||
|
@ -238,15 +246,15 @@ export default class SendDetails extends Component {
|
|||
await BlueApp.saveToDisk();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
alert(err);
|
||||
alert(loc.send.details.create_tx_error);
|
||||
this.setState({ isLoading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ isLoading: false }, () =>
|
||||
this.props.navigation.navigate('CreateTransaction', {
|
||||
this.props.navigation.navigate('Confirm', {
|
||||
amount: this.state.amount,
|
||||
fee: fee.toFixed(8),
|
||||
fee: Number(fee.toFixed(8)),
|
||||
address: this.state.address,
|
||||
memo: this.state.memo,
|
||||
fromWallet: this.state.fromWallet,
|
||||
|
@ -376,6 +384,11 @@ export default class SendDetails extends Component {
|
|||
{' ' + BitcoinUnit.BTC}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{ alignItems: 'center', marginBottom: 22, marginTop: 4 }}>
|
||||
<Text style={{ fontSize: 18, color: '#d4d4d4', fontWeight: '600' }}>
|
||||
{currency.satoshiToLocalCurrency(loc.formatBalanceWithoutSuffix(this.state.amount || 0, BitcoinUnit.SATOSHIS))}
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
|
@ -538,7 +551,8 @@ SendDetails.propTypes = {
|
|||
params: PropTypes.shape({
|
||||
address: PropTypes.string,
|
||||
fromAddress: PropTypes.string,
|
||||
fromSecret: PropTypes.string,
|
||||
satoshiPerByte: PropTypes.string,
|
||||
fromSecret: PropTypes.fromSecret,
|
||||
memo: PropTypes.string,
|
||||
}),
|
||||
}),
|
||||
|
|
109
screen/send/success.js
Normal file
109
screen/send/success.js
Normal file
|
@ -0,0 +1,109 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Haptic } from 'expo';
|
||||
import { View } from 'react-native';
|
||||
import { Text, Icon } from 'react-native-elements';
|
||||
import { BlueButton, SafeBlueArea, BlueCard } from '../../BlueComponents';
|
||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||
import PropTypes from 'prop-types';
|
||||
let loc = require('../../loc');
|
||||
|
||||
export default class Success extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
console.log('send/create constructor');
|
||||
|
||||
this.state = {
|
||||
amount: props.navigation.getParam('amount'),
|
||||
address: props.navigation.getParam('address'),
|
||||
fee: props.navigation.getParam('fee'),
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
console.log('send/create - componentDidMount');
|
||||
console.log('address = ', this.state.address);
|
||||
Haptic.notification(Haptic.NotificationTypes.Success);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 19 }}>
|
||||
<BlueCard style={{ alignItems: 'center', flex: 1 }}>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'center', paddingTop: 76, paddingBottom: 16 }}>
|
||||
<Text
|
||||
style={{
|
||||
color: '#0f5cc0',
|
||||
fontSize: 36,
|
||||
fontWeight: '600',
|
||||
}}
|
||||
>
|
||||
{this.state.amount}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: '#0f5cc0',
|
||||
fontSize: 16,
|
||||
marginHorizontal: 4,
|
||||
paddingBottom: 6,
|
||||
fontWeight: '600',
|
||||
alignSelf: 'flex-end',
|
||||
}}
|
||||
>
|
||||
{' ' + BitcoinUnit.BTC}
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
color: '#37c0a1',
|
||||
fontSize: 14,
|
||||
marginHorizontal: 4,
|
||||
paddingBottom: 6,
|
||||
fontWeight: '500',
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
>
|
||||
{loc.send.create.fee}: {loc.formatBalance(this.state.fee, BitcoinUnit.SATS)}
|
||||
</Text>
|
||||
</BlueCard>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: '#ccddf9',
|
||||
width: 120,
|
||||
height: 120,
|
||||
borderRadius: 60,
|
||||
alignSelf: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: 43,
|
||||
marginBottom: 53,
|
||||
}}
|
||||
>
|
||||
<Icon name="check" size={50} type="font-awesome" color="#0f5cc0" />
|
||||
</View>
|
||||
<BlueCard>
|
||||
<BlueButton
|
||||
onPress={() => {
|
||||
this.props.navigation.getParam('dismissModal')();
|
||||
}}
|
||||
title={loc.send.success.done}
|
||||
style={{ maxWidth: 263, paddingHorizontal: 56 }}
|
||||
/>
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Success.propTypes = {
|
||||
navigation: PropTypes.shape({
|
||||
goBack: PropTypes.function,
|
||||
getParam: PropTypes.function,
|
||||
navigate: PropTypes.function,
|
||||
state: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
amount: PropTypes.string,
|
||||
fee: PropTypes.number,
|
||||
address: PropTypes.string,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
};
|
|
@ -39,7 +39,7 @@ function formatTime(time) {
|
|||
export default class TransactionsDetails extends Component {
|
||||
static navigationOptions = {
|
||||
header: ({ navigation }) => {
|
||||
return <BlueHeaderDefaultSub leftText={loc.transactions.details.title} onClose={() => navigation.goBack(null)} />;
|
||||
return <BlueHeaderDefaultSub leftText={loc.transactions.details.title.toLowerCase()} onClose={() => navigation.goBack(null)} />;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -78,7 +78,7 @@ export default class TransactionsDetails extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (this.state.isLoading) {
|
||||
if (this.state.isLoading || !this.state.hasOwnProperty('tx')) {
|
||||
return <BlueLoading />;
|
||||
}
|
||||
|
||||
|
@ -99,51 +99,82 @@ export default class TransactionsDetails extends Component {
|
|||
}
|
||||
})()}
|
||||
|
||||
<View style={{ flex: 1, flexDirection: 'row', marginBottom: 4, justifyContent: 'space-between' }}>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>{loc.transactions.details.from}</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.from.filter(onlyUnique).join(', ')} />
|
||||
</View>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.from.filter(onlyUnique).join(', ')}</BlueText>
|
||||
{this.state.hasOwnProperty('from') && (
|
||||
<React.Fragment>
|
||||
<View style={{ flex: 1, flexDirection: 'row', marginBottom: 4, justifyContent: 'space-between' }}>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>{loc.transactions.details.from}</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.from.filter(onlyUnique).join(', ')} />
|
||||
</View>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.from.filter(onlyUnique).join(', ')}</BlueText>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<View style={{ flex: 1, flexDirection: 'row', marginBottom: 4, justifyContent: 'space-between' }}>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>{loc.transactions.details.to}</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.to.filter(onlyUnique).join(', ')} />
|
||||
</View>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>
|
||||
{arrDiff(this.state.from, this.state.to.filter(onlyUnique)).join(', ')}
|
||||
</BlueText>
|
||||
{this.state.hasOwnProperty('to') && (
|
||||
<React.Fragment>
|
||||
<View style={{ flex: 1, flexDirection: 'row', marginBottom: 4, justifyContent: 'space-between' }}>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>{loc.transactions.details.to}</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.to.filter(onlyUnique).join(', ')} />
|
||||
</View>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>
|
||||
{arrDiff(this.state.from, this.state.to.filter(onlyUnique)).join(', ')}
|
||||
</BlueText>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<View style={{ flex: 1, flexDirection: 'row', marginBottom: 4, justifyContent: 'space-between' }}>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500' }}>Txid</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.tx.hash} />
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
const url = `https://live.blockcypher.com/btc/tx/${this.state.tx.hash}`;
|
||||
Linking.canOpenURL(url).then(supported => {
|
||||
if (supported) {
|
||||
Linking.openURL(url);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.hash}</BlueText>
|
||||
</TouchableOpacity>
|
||||
{this.state.tx.hasOwnProperty('hash') && (
|
||||
<React.Fragment>
|
||||
<View style={{ flex: 1, flexDirection: 'row', marginBottom: 4, justifyContent: 'space-between' }}>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500' }}>Txid</BlueText>
|
||||
<BlueCopyToClipboardButton stringToCopy={this.state.tx.hash} />
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
const url = `https://live.blockcypher.com/btc/tx/${this.state.tx.hash}`;
|
||||
Linking.canOpenURL(url).then(supported => {
|
||||
if (supported) {
|
||||
Linking.openURL(url);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.hash}</BlueText>
|
||||
</TouchableOpacity>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{this.state.tx.hasOwnProperty('received') && (
|
||||
<React.Fragment>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Received</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{formatTime(this.state.tx.received)}</BlueText>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Received</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{formatTime(this.state.tx.received)}</BlueText>
|
||||
{this.state.tx.hasOwnProperty('block_height') && (
|
||||
<React.Fragment>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Block Height</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{formatTime(this.state.tx.block_height)}</BlueText>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Block Height</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{formatTime(this.state.tx.block_height)}</BlueText>
|
||||
{this.state.tx.hasOwnProperty('confirmations') && (
|
||||
<React.Fragment>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Confirmations</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.confirmations}</BlueText>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Confirmations</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.confirmations}</BlueText>
|
||||
{this.state.tx.hasOwnProperty('inputs') && (
|
||||
<React.Fragment>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Inputs</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.inputs.length}</BlueText>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Inputs</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.inputs.length}</BlueText>
|
||||
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Outputs</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.outputs.length}</BlueText>
|
||||
{this.state.tx.hasOwnProperty('outputs') && (
|
||||
<React.Fragment>
|
||||
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Outputs</BlueText>
|
||||
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.outputs.length}</BlueText>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</BlueCard>
|
||||
|
||||
{(() => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Dimensions, ActivityIndicator, View } from 'react-native';
|
||||
import QRCode from 'react-native-qrcode';
|
||||
import { BlueSpacing, BlueSpacing40, SafeBlueArea, BlueCard, BlueText, BlueHeaderDefaultSub } from '../../BlueComponents';
|
||||
import { BlueSpacing40, SafeBlueArea, BlueCard, BlueText, BlueHeaderDefaultSub } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../../BlueApp');
|
||||
|
@ -48,6 +48,13 @@ export default class WalletExport extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
determineSize = () => {
|
||||
if (width > 312) {
|
||||
return width - 48;
|
||||
}
|
||||
return 312;
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.isLoading) {
|
||||
return (
|
||||
|
@ -72,8 +79,6 @@ export default class WalletExport extends Component {
|
|||
{(() => {
|
||||
if (isIpad) {
|
||||
return <BlueSpacing40 />;
|
||||
} else {
|
||||
return <BlueSpacing />;
|
||||
}
|
||||
})()}
|
||||
<BlueCard style={{ alignItems: 'center', flex: 1 }}>
|
||||
|
@ -88,11 +93,11 @@ export default class WalletExport extends Component {
|
|||
})()}
|
||||
<QRCode
|
||||
value={this.state.wallet.getSecret()}
|
||||
size={312}
|
||||
size={this.determineSize()}
|
||||
bgColor={BlueApp.settings.foregroundColor}
|
||||
fgColor={BlueApp.settings.brandingColor}
|
||||
/>
|
||||
<BlueText>{this.state.wallet.getSecret()}</BlueText>
|
||||
<BlueText style={{ marginVertical: 8 }}>{this.state.wallet.getSecret()}</BlueText>
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue