import React, { Component } from 'react'; import { View, ActivityIndicator, Text, TouchableOpacity } from 'react-native'; import { BlueButton, SafeBlueArea, BlueTransactionOutgoingIcon, BlueTransactionPendingIcon, BlueTransactionIncomingIcon, BlueCard, BlueText, BlueLoading, BlueSpacing20, BlueNavigationStyle, } from '../../BlueComponents'; import PropTypes from 'prop-types'; import { HDSegwitBech32Transaction } from '../../class'; import { BitcoinUnit } from '../../models/bitcoinUnits'; import { Icon } from 'react-native-elements'; import Handoff from 'react-native-handoff'; import HandoffSettings from '../../class/handoff'; /** @type {AppStorage} */ let BlueApp = require('../../BlueApp'); let loc = require('../../loc'); const buttonStatus = Object.freeze({ possible: 1, unknown: 2, notPossible: 3, }); export default class TransactionsStatus extends Component { static navigationOptions = () => ({ ...BlueNavigationStyle(), }); constructor(props) { super(props); let hash = props.navigation.state.params.hash; let foundTx = {}; let from = []; let to = []; for (let tx of BlueApp.getTransactions()) { if (tx.hash === hash) { foundTx = tx; for (let input of foundTx.inputs) { from = from.concat(input.addresses); } for (let output of foundTx.outputs) { if (output.addresses) to = to.concat(output.addresses); if (output.scriptPubKey && output.scriptPubKey.addresses) to = to.concat(output.scriptPubKey.addresses); } } } let wallet = false; for (let w of BlueApp.getWallets()) { for (let t of w.getTransactions()) { if (t.hash === hash) { console.log('tx', hash, 'belongs to', w.getLabel()); wallet = w; } } } this.state = { isLoading: true, tx: foundTx, from, to, wallet, isCPFPpossible: buttonStatus.unknown, isRBFBumpFeePossible: buttonStatus.unknown, isRBFCancelPossible: buttonStatus.unknown, isHandOffUseEnabled: false, }; } async componentDidMount() { console.log('transactions/details - componentDidMount'); const isHandOffUseEnabled = await HandoffSettings.isHandoffUseEnabled(); this.setState({ isLoading: false, isHandOffUseEnabled, }); try { await this.checkPossibilityOfCPFP(); await this.checkPossibilityOfRBFBumpFee(); await this.checkPossibilityOfRBFCancel(); } catch (_) { this.setState({ isCPFPpossible: buttonStatus.notPossible, isRBFBumpFeePossible: buttonStatus.notPossible, isRBFCancelPossible: buttonStatus.notPossible, }); } } async checkPossibilityOfCPFP() { if (!this.state.wallet.allowRBF()) { return this.setState({ isCPFPpossible: buttonStatus.notPossible }); } let tx = new HDSegwitBech32Transaction(null, this.state.tx.hash, this.state.wallet); if ((await tx.isToUsTransaction()) && (await tx.getRemoteConfirmationsNum()) === 0) { return this.setState({ isCPFPpossible: buttonStatus.possible }); } else { return this.setState({ isCPFPpossible: buttonStatus.notPossible }); } } async checkPossibilityOfRBFBumpFee() { if (!this.state.wallet.allowRBF()) { return this.setState({ isRBFBumpFeePossible: buttonStatus.notPossible }); } let tx = new HDSegwitBech32Transaction(null, this.state.tx.hash, this.state.wallet); if ((await tx.isOurTransaction()) && (await tx.getRemoteConfirmationsNum()) === 0 && (await tx.isSequenceReplaceable())) { return this.setState({ isRBFBumpFeePossible: buttonStatus.possible }); } else { return this.setState({ isRBFBumpFeePossible: buttonStatus.notPossible }); } } async checkPossibilityOfRBFCancel() { if (!this.state.wallet.allowRBF()) { return this.setState({ isRBFCancelPossible: buttonStatus.notPossible }); } let tx = new HDSegwitBech32Transaction(null, this.state.tx.hash, this.state.wallet); if ( (await tx.isOurTransaction()) && (await tx.getRemoteConfirmationsNum()) === 0 && (await tx.isSequenceReplaceable()) && (await tx.canCancelTx()) ) { return this.setState({ isRBFCancelPossible: buttonStatus.possible }); } else { return this.setState({ isRBFCancelPossible: buttonStatus.notPossible }); } } render() { if (this.state.isLoading || !this.state.hasOwnProperty('tx')) { return ; } return ( {this.state.isHandOffUseEnabled && ( )} {loc.formatBalanceWithoutSuffix(this.state.tx.value, this.state.wallet.preferredBalanceUnit, true)}{' '} {this.state.wallet.preferredBalanceUnit !== BitcoinUnit.LOCAL_CURRENCY && ( {this.state.wallet.preferredBalanceUnit} )} {(() => { if (BlueApp.tx_metadata[this.state.tx.hash]) { if (BlueApp.tx_metadata[this.state.tx.hash]['memo']) { return ( {BlueApp.tx_metadata[this.state.tx.hash]['memo']} ); } } })()} {(() => { if (!this.state.tx.confirmations) { return ( ); } else if (this.state.tx.value < 0) { return ( ); } else { return ( ); } })()} {this.state.tx.hasOwnProperty('fee') && ( {loc.send.create.fee.toLowerCase()}{' '} {loc.formatBalanceWithoutSuffix(this.state.tx.fee, this.state.wallet.preferredBalanceUnit, true)}{' '} {this.state.wallet.preferredBalanceUnit !== BitcoinUnit.LOCAL_CURRENCY && this.state.wallet.preferredBalanceUnit} )} {this.state.tx.confirmations > 6 ? '6+' : this.state.tx.confirmations} confirmations {(() => { if (this.state.isCPFPpossible === buttonStatus.unknown) { return ( ); } else if (this.state.isCPFPpossible === buttonStatus.possible) { return ( this.props.navigation.navigate('CPFP', { txid: this.state.tx.hash, wallet: this.state.wallet, }) } title="Bump Fee" /> ); } })()} {(() => { if (this.state.isRBFBumpFeePossible === buttonStatus.unknown) { return ( ); } else if (this.state.isRBFBumpFeePossible === buttonStatus.possible) { return ( this.props.navigation.navigate('RBFBumpFee', { txid: this.state.tx.hash, wallet: this.state.wallet, }) } title="Bump Fee" /> ); } })()} {(() => { if (this.state.isRBFCancelPossible === buttonStatus.unknown) { return ( ); } else if (this.state.isRBFCancelPossible === buttonStatus.possible) { return ( this.props.navigation.navigate('RBFCancel', { txid: this.state.tx.hash, wallet: this.state.wallet, }) } style={{ color: '#d0021b', fontSize: 15, fontWeight: '500', textAlign: 'center' }} > {'Cancel Transaction'} ); } })()} this.props.navigation.navigate('TransactionDetails', { hash: this.state.tx.hash })} > {loc.send.create.details.toLowerCase()} ); } } TransactionsStatus.propTypes = { navigation: PropTypes.shape({ goBack: PropTypes.func, navigate: PropTypes.func, state: PropTypes.shape({ params: PropTypes.shape({ hash: PropTypes.string, }), }), }), };