pbca26
7 years ago
8 changed files with 16 additions and 1323 deletions
@ -1,917 +0,0 @@ |
|||
import React from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import Config from '../../../config'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { checkTimestamp } from '../../../util/time'; |
|||
import { |
|||
edexGetTxIDList, |
|||
edexRemoveTXID |
|||
} from '../../../util/cacheFormat'; |
|||
import { |
|||
resolveOpenAliasAddress, |
|||
triggerToaster, |
|||
shepherdGroomPostPromise, |
|||
edexGetTransaction, |
|||
getCacheFile, |
|||
fetchUtxoCache, |
|||
sendToAddress, |
|||
iguanaUTXORawTX, |
|||
clearLastSendToResponseState, |
|||
sendToAddressStateAlt, |
|||
dexSendRawTX |
|||
} from '../../../actions/actionCreators'; |
|||
import Store from '../../../store'; |
|||
import { |
|||
UTXOCacheInfoRender, |
|||
SendCoinResponseRender, |
|||
OASendUIRender, |
|||
SendApiTypeSelectorRender, |
|||
SendCoinRender |
|||
} from './sendCoin.render'; |
|||
|
|||
import io from 'socket.io-client'; |
|||
import { isPositiveNumber } from '../../../util/number'; |
|||
const socket = io.connect(`http://127.0.0.1:${Config.agamaPort}`); |
|||
|
|||
// TODO: prevent any cache updates rather than utxo while on send coin form
|
|||
// fix a bug - total amount is incorrect when switching between steps
|
|||
|
|||
class SendCoin extends React.Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
currentStep: 0, |
|||
sendFrom: this.props.Dashboard.activeHandle ? this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin] : null, |
|||
sendFromAmount: 0, |
|||
sendTo: '', |
|||
sendToOA: null, |
|||
amount: 0, |
|||
fee: 0.0001, |
|||
sendSig: false, |
|||
sendApiType: true, |
|||
addressSelectorOpen: false, |
|||
currentStackLength: 0, |
|||
totalStackLength: 0, |
|||
utxoMethodInProgress: false, |
|||
}; |
|||
this.updateInput = this.updateInput.bind(this); |
|||
this.handleBasiliskSend = this.handleBasiliskSend.bind(this); |
|||
this.openDropMenu = this.openDropMenu.bind(this); |
|||
this.toggleSendSig = this.toggleSendSig.bind(this); |
|||
this.getOAdress = this.getOAdress.bind(this); |
|||
this.toggleSendAPIType = this.toggleSendAPIType.bind(this); |
|||
this._fetchNewUTXOData = this._fetchNewUTXOData.bind(this); |
|||
this.handleClickOutside = this.handleClickOutside.bind(this); |
|||
this.setRecieverFromScan = this.setRecieverFromScan.bind(this); |
|||
socket.on('messages', msg => this.updateSocketsData(msg)); |
|||
} |
|||
|
|||
setRecieverFromScan(receiver) { |
|||
try { |
|||
const recObj = JSON.parse(receiver); |
|||
|
|||
if (recObj && |
|||
typeof recObj === 'object') { |
|||
if (recObj.coin === this.props.ActiveCoin.coin) { |
|||
if (recObj.amount) { |
|||
this.setState({ |
|||
amount: recObj.amount, |
|||
}); |
|||
} |
|||
if (recObj.address) { |
|||
this.setState({ |
|||
sendTo: recObj.address, |
|||
}); |
|||
} |
|||
} else { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('SEND.QR_COIN_MISMATCH_MESSAGE_IMPORT_COIN') + |
|||
recObj.coin + |
|||
translate('SEND.QR_COIN_MISMATCH_MESSAGE_ACTIVE_COIN') + |
|||
this.props.ActiveCoin.coin + |
|||
translate('SEND.QR_COIN_MISMATCH_MESSAGE_END'), |
|||
translate('SEND.QR_COIN_MISMATCH_TITLE'), |
|||
'warning' |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} catch (e) { |
|||
this.setState({ |
|||
sendTo: receiver, |
|||
}); |
|||
} |
|||
|
|||
document.getElementById('edexcoinSendTo').focus(); |
|||
} |
|||
|
|||
componentWillMount() { |
|||
document.addEventListener( |
|||
'click', |
|||
this.handleClickOutside, |
|||
false |
|||
); |
|||
} |
|||
|
|||
componentWillUnmount() { |
|||
document.removeEventListener( |
|||
'click', |
|||
this.handleClickOutside, |
|||
false |
|||
); |
|||
} |
|||
|
|||
handleClickOutside(e) { |
|||
if (e.srcElement.className !== 'btn dropdown-toggle btn-info' && |
|||
(e.srcElement.offsetParent && e.srcElement.offsetParent.className !== 'btn dropdown-toggle btn-info') && |
|||
(e.path && e.path[4] && e.path[4].className.indexOf('showkmdwalletaddrs') === -1)) { |
|||
this.setState({ |
|||
addressSelectorOpen: false, |
|||
}); |
|||
} |
|||
} |
|||
|
|||
componentWillReceiveProps(props) { |
|||
if (this.state && |
|||
!this.state.sendFrom && |
|||
this.props.ActiveCoin.activeAddress) { |
|||
this.setState(Object.assign({}, this.state, { |
|||
sendFrom: this.props.ActiveCoin.activeAddress, |
|||
})); |
|||
} |
|||
} |
|||
|
|||
updateSocketsData(data) { |
|||
if (data && |
|||
data.message && |
|||
data.message.shepherd.iguanaAPI && |
|||
data.message.shepherd.iguanaAPI.totalStackLength) { |
|||
this.setState(Object.assign({}, this.state, { |
|||
totalStackLength: data.message.shepherd.iguanaAPI.totalStackLength, |
|||
})); |
|||
} |
|||
if (data && |
|||
data.message && |
|||
data.message.shepherd.iguanaAPI && |
|||
data.message.shepherd.iguanaAPI.currentStackLength) { |
|||
this.setState(Object.assign({}, this.state, { |
|||
currentStackLength: data.message.shepherd.iguanaAPI.currentStackLength, |
|||
})); |
|||
} |
|||
} |
|||
|
|||
_fetchNewUTXOData() { |
|||
Store.dispatch(fetchUtxoCache({ |
|||
pubkey: this.props.Dashboard.activeHandle.pubkey, |
|||
allcoins: false, |
|||
coin: this.props.ActiveCoin.coin, |
|||
calls: 'refresh', |
|||
address: this.state.sendFrom, |
|||
})); |
|||
} |
|||
|
|||
renderUTXOCacheInfo() { |
|||
if (this.props.ActiveCoin.mode === 'basilisk' && |
|||
this.state.sendFrom && |
|||
!this.state.sendApiType && |
|||
this.props.ActiveCoin.cache && |
|||
this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom]) { |
|||
let refreshCacheData; |
|||
let timestamp; |
|||
let isReadyToUpdate; |
|||
let waitUntilCallIsFinished = this.state.currentStackLength > 1 ? true : false; |
|||
const _cache = this.props.ActiveCoin.cache; |
|||
const _coin = this.props.ActiveCoin.coin; |
|||
const _sendFrom = this.state.sendFrom; |
|||
|
|||
if (_cache[_coin][_sendFrom].refresh || |
|||
_cache[_coin][_sendFrom].listunspent) { |
|||
refreshCacheData = _cache[_coin][_sendFrom].refresh || _cache[_coin][_sendFrom].listunspent; |
|||
timestamp = checkTimestamp(refreshCacheData.timestamp); |
|||
isReadyToUpdate = timestamp > 600 ? true : false; |
|||
} else { |
|||
isReadyToUpdate = true; |
|||
} |
|||
|
|||
if (_cache[_coin][_sendFrom].refresh && |
|||
_cache[_coin][_sendFrom].refresh.data && |
|||
_cache[_coin][_sendFrom].refresh.data.error && |
|||
_cache[_coin][_sendFrom].refresh.data.error === 'request failed') { |
|||
timestamp = null; |
|||
} |
|||
|
|||
return UTXOCacheInfoRender.call( |
|||
this, |
|||
refreshCacheData, |
|||
isReadyToUpdate, |
|||
waitUntilCallIsFinished, |
|||
timestamp |
|||
); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
renderAddressAmount(address) { |
|||
const _addresses = this.props.ActiveCoin.addresses; |
|||
|
|||
if (_addresses && |
|||
_addresses.public && |
|||
_addresses.public.length) { |
|||
for (let i = 0; i < _addresses.public.length; i++) { |
|||
if (_addresses.public[i].address === address) { |
|||
if (_addresses.public[i].amount !== 'N/A') { |
|||
return _addresses.public[i].amount; |
|||
} |
|||
} |
|||
} |
|||
} else { |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
renderAddressByType(type) { |
|||
const _addresses = this.props.ActiveCoin.addresses; |
|||
|
|||
if (_addresses && |
|||
_addresses[type] && |
|||
_addresses[type].length) { |
|||
if (this.state.sendApiType) { |
|||
const mainAddress = this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin]; |
|||
const mainAddressAmount = this.renderAddressAmount(mainAddress); |
|||
|
|||
return( |
|||
<li |
|||
key={ mainAddress } |
|||
className={ mainAddressAmount <= 0 ? 'hide' : '' }> |
|||
<a onClick={ () => this.updateAddressSelection(mainAddress, type, mainAddressAmount) }> |
|||
<i className={ 'icon fa-eye' + (type === 'public' ? '' : '-slash') }></i> |
|||
<span className="text"> |
|||
[ { mainAddressAmount } { this.props.ActiveCoin.coin } ] |
|||
{ mainAddress } |
|||
</span> |
|||
<span className="glyphicon glyphicon-ok check-mark"></span> |
|||
</a> |
|||
</li> |
|||
); |
|||
} else { |
|||
let items = []; |
|||
const _addresses = this.props.ActiveCoin.addresses; |
|||
const _cache = this.props.ActiveCoin.cache; |
|||
const _coin = this.props.ActiveCoin.coin; |
|||
|
|||
for (let i = 0; i < _addresses[type].length; i++) { |
|||
const address = _addresses[type][i].address; |
|||
let _amount = address.amount; |
|||
|
|||
if (this.props.ActiveCoin.mode === 'basilisk' && |
|||
_cache) { |
|||
_amount = _cache[_coin][address] && _cache[_coin][address].getbalance.data && _cache[_coin][address].getbalance.data.balance ? _cache[_coin][address].getbalance.data.balance : 'N/A'; |
|||
} |
|||
|
|||
if (_amount !== 'N/A') { |
|||
items.push( |
|||
<li |
|||
key={ address } |
|||
className={ _amount <= 0 ? 'hide' : '' }> |
|||
<a onClick={ () => this.updateAddressSelection(address, type, _amount) }> |
|||
<i className={ 'icon fa-eye' + (type === 'public' ? '' : '-slash') }></i> |
|||
<span className="text">[ { _amount } { _coin } ] { address }</span> |
|||
<span className="glyphicon glyphicon-ok check-mark"></span> |
|||
</a> |
|||
</li> |
|||
); |
|||
} |
|||
} |
|||
|
|||
return items; |
|||
} |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
renderSelectorCurrentLabel() { |
|||
if (this.state.sendFrom) { |
|||
let _amount; |
|||
const _cache = this.props.ActiveCoin.cache; |
|||
const _coin = this.props.ActiveCoin.coin; |
|||
const _sendFrom = this.state.sendFrom; |
|||
|
|||
if (this.state.sendFromAmount === 0 && |
|||
this.props.ActiveCoin.mode === 'basilisk' && |
|||
_cache) { |
|||
_amount = _cache[_coin][_sendFrom].getbalance.data && _cache[_coin][_sendFrom].getbalance.data.balance ? _cache[_coin][_sendFrom].getbalance.data.balance : 'N/A'; |
|||
} else { |
|||
_amount = this.state.sendFromAmount; |
|||
} |
|||
|
|||
return ( |
|||
<span> |
|||
<i className={ 'icon fa-eye' + (this.state.addressType === 'public' ? '' : '-slash') }></i> |
|||
<span className="text">[ { _amount } { _coin } ] { _sendFrom }</span> |
|||
</span> |
|||
); |
|||
} else if (this.state.sendApiType) { |
|||
const mainAddress = this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin]; |
|||
const mainAddressAmount = this.renderAddressAmount(mainAddress); |
|||
|
|||
return ( |
|||
<span> |
|||
<i className={ 'icon fa-eye' + (this.state.addressType === 'public' ? '' : '-slash') }></i> |
|||
<span className="text">[ { mainAddressAmount } { this.props.ActiveCoin.coin } ] { mainAddress }</span> |
|||
</span> |
|||
); |
|||
} else { |
|||
return ( |
|||
<span>{ translate('SEND.SELECT_T_OR_Z_ADDR') }</span> |
|||
); |
|||
} |
|||
} |
|||
|
|||
renderAddressList() { |
|||
return ( |
|||
<div className={ `btn-group bootstrap-select form-control form-material showkmdwalletaddrs show-tick ${(this.state.addressSelectorOpen ? 'open' : '')}` }> |
|||
<button |
|||
type="button" |
|||
className="btn dropdown-toggle btn-info" |
|||
title={ `${translate('SEND.SELECT_T_OR_Z_ADDR')}` } |
|||
onClick={ this.openDropMenu }> |
|||
<span className="filter-option pull-left"> |
|||
{ this.renderSelectorCurrentLabel() } |
|||
</span> |
|||
<span className="bs-caret"> |
|||
<span className="caret"></span> |
|||
</span> |
|||
</button> |
|||
<div className="dropdown-menu open"> |
|||
<ul className="dropdown-menu inner"> |
|||
<li className="selected"> |
|||
<a> |
|||
<span className="text">{ translate('SEND.SELECT_T_OR_Z_ADDR') }</span> |
|||
<span className="glyphicon glyphicon-ok check-mark"></span> |
|||
</a> |
|||
</li> |
|||
{ this.renderAddressByType('public') } |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
openDropMenu() { |
|||
this.setState(Object.assign({}, this.state, { |
|||
addressSelectorOpen: !this.state.addressSelectorOpen, |
|||
})); |
|||
} |
|||
|
|||
updateAddressSelection(address, type, amount) { |
|||
let _sendFromAmount = amount ? amount : this.props.ActiveCoin.addresses[type][address].amount; |
|||
const _cache = this.props.ActiveCoin.cache; |
|||
const _coin = this.props.ActiveCoin.coin; |
|||
|
|||
if (this.props.ActiveCoin.mode === 'basilisk' && |
|||
this.props.ActiveCoin.cache) { |
|||
_sendFromAmount = _cache[_coin][address].getbalance.data && _cache[_coin][address].getbalance.data.balance ? _cache[_coin][address].getbalance.data.balance : 'N/A'; |
|||
} |
|||
|
|||
this.setState(Object.assign({}, this.state, { |
|||
sendFrom: address, |
|||
addressType: type, |
|||
sendFromAmount: _sendFromAmount, |
|||
addressSelectorOpen: !this.state.addressSelectorOpen, |
|||
})); |
|||
} |
|||
|
|||
changeSendCoinStep(step) { |
|||
if (step === 0) { |
|||
Store.dispatch(clearLastSendToResponseState()); |
|||
|
|||
this.setState({ |
|||
currentStep: 0, |
|||
sendFrom: this.props.Dashboard && this.props.Dashboard.activeHandle ? this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin] : null, |
|||
sendFromAmount: 0, |
|||
sendTo: '', |
|||
sendToOA: null, |
|||
amount: 0, |
|||
fee: 0.0001, |
|||
sendSig: false, |
|||
sendApiType: true, |
|||
addressSelectorOpen: false, |
|||
currentStackLength: 0, |
|||
totalStackLength: 0, |
|||
utxoMethodInProgress: false, |
|||
}); |
|||
} |
|||
|
|||
if (step === 1) { |
|||
if (!this.validateSendFormData()) { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
if (step === 1 || |
|||
step === 2) { |
|||
this.setState(Object.assign({}, this.state, { |
|||
currentStep: step, |
|||
utxoMethodInProgress: !this.state.sendApiType && this.props.ActiveCoin.mode === 'basilisk' ? true : false, |
|||
})); |
|||
} |
|||
|
|||
if (step === 2) { |
|||
if (!this.state.sendApiType && |
|||
this.props.ActiveCoin.mode === 'basilisk') { |
|||
this.handleBasiliskSend(); |
|||
} else { |
|||
Store.dispatch( |
|||
sendToAddress( |
|||
this.props.ActiveCoin.coin, |
|||
this.state |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
toggleSendSig() { |
|||
this.setState(Object.assign({}, this.state, { |
|||
sendSig: !this.state.sendSig, |
|||
})); |
|||
} |
|||
|
|||
toggleSendAPIType() { |
|||
this.setState(Object.assign({}, this.state, { |
|||
sendApiType: !this.state.sendApiType, |
|||
fee: !this.state.sendApiType ? 0 : 0.0001, |
|||
sendFrom: this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin], |
|||
})); |
|||
} |
|||
|
|||
updateInput(e) { |
|||
this.setState({ |
|||
[e.target.name]: e.target.value, |
|||
}); |
|||
} |
|||
|
|||
// TODO: move to action creators
|
|||
handleBasiliskSend() { |
|||
const refreshData = this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh; |
|||
const listunspentData = this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].listunspent; |
|||
const utxoSet = (refreshData && refreshData.data) || (listunspentData && listunspentData.data); |
|||
const _pubkey = this.props.Dashboard.activeHandle.pubkey; |
|||
const forceUpdateCache = this._fetchNewUTXOData; |
|||
const _sendFrom = this.state.sendFrom; |
|||
const sendData = { |
|||
coin: this.props.ActiveCoin.coin, |
|||
sendfrom: this.state.sendFrom, |
|||
sendtoaddr: this.state.sendTo, |
|||
amount: this.state.amount, |
|||
txfee: this.state.fee, |
|||
sendsig: this.state.sendSig === true ? 0 : 1, |
|||
utxos: utxoSet, |
|||
}; |
|||
|
|||
// TODO: es arrows
|
|||
iguanaUTXORawTX(sendData, Store.dispatch) |
|||
.then(function(json) { |
|||
if (json.result === 'success' && |
|||
json.completed === true) { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('TOASTR.SIGNED_TX_GENERATED'), |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'success' |
|||
) |
|||
); |
|||
|
|||
if (sendData.sendsig === 1) { |
|||
const dexrawtxData = { |
|||
signedtx: json.signedtx, |
|||
coin: sendData.coin, |
|||
}; |
|||
dexSendRawTX( |
|||
dexrawtxData, |
|||
Store.dispatch |
|||
).then(function(dexRawTxJSON) { |
|||
if (dexRawTxJSON.indexOf('"error":{"code"') > -1) { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('TOASTR.TRANSACTION_FAILED'), |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'error' |
|||
) |
|||
); |
|||
Store.dispatch(sendToAddressStateAlt(JSON.parse(dexRawTxJSON))); |
|||
|
|||
this.setState(Object.assign({}, this.state, { |
|||
utxoMethodInProgress: false, |
|||
})); |
|||
} else { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('TOASTR.SIGNED_TX_SENT'), |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'success' |
|||
) |
|||
); |
|||
Store.dispatch(sendToAddressStateAlt(json)); |
|||
|
|||
let getTxidData = function() { |
|||
return new Promise(function(resolve, reject) { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('TOASTR.GETTING_TXID_INFO'), |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'info' |
|||
) |
|||
); |
|||
|
|||
edexGetTransaction({ |
|||
coin: sendData.coin, |
|||
txid: dexRawTxJSON.txid ? dexRawTxJSON.txid : dexRawTxJSON, |
|||
}, Store.dispatch) |
|||
.then(function(json) { |
|||
resolve(json); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
let processRefreshUTXOs = function(vinData) { |
|||
return new Promise(function(resolve, reject) { |
|||
let edexGetTxIDListRes = edexGetTxIDList(vinData); |
|||
resolve(edexGetTxIDListRes); |
|||
}); |
|||
} |
|||
|
|||
let getDataCacheContents = function(txidListToRemove) { |
|||
return new Promise(function(resolve, reject) { |
|||
getCacheFile(_pubkey) |
|||
.then(function(result) { |
|||
let saveThisData = edexRemoveTXID(result.result, _sendFrom, txidListToRemove); |
|||
resolve(saveThisData); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
let saveNewCacheData = function(saveThisData) { |
|||
return new Promise(function(resolve, reject) { |
|||
shepherdGroomPostPromise( |
|||
_pubkey, |
|||
saveThisData |
|||
).then(function(result) { |
|||
resolve(result); |
|||
forceUpdateCache(); |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('TOASTR.LOCAL_UTXO_UPDATED'), |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'info' |
|||
) |
|||
); |
|||
|
|||
this.setState(Object.assign({}, this.state, { |
|||
utxoMethodInProgress: false, |
|||
})); |
|||
}.bind(this)); |
|||
}.bind(this)); |
|||
}.bind(this); |
|||
|
|||
Store.dispatch( |
|||
triggerToaster( |
|||
`${translate('TOASTR.AWAITING_TX_RESP')}...`, |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'info' |
|||
) |
|||
); |
|||
|
|||
function waterfallUTXOProcess() { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
`${translate('TOASTR.PROCESSING_UTXO')}...`, |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'info' |
|||
) |
|||
); |
|||
|
|||
getTxidData() |
|||
.then(function(gettxdata) { |
|||
return processRefreshUTXOs(gettxdata.vin); |
|||
}) |
|||
.then(function(new_utxos_set) { |
|||
return getDataCacheContents(new_utxos_set); |
|||
}) |
|||
.then(function(save_this_data) { |
|||
return saveNewCacheData(save_this_data); |
|||
}); |
|||
} |
|||
|
|||
let sentTxData = setInterval(function() { |
|||
getTxidData() |
|||
.then(function(gettxdata) { |
|||
if (gettxdata.vin && |
|||
gettxdata.vin.length) { |
|||
clearInterval(sentTxData); |
|||
waterfallUTXOProcess(); |
|||
} |
|||
}) |
|||
}, 1000); |
|||
} |
|||
}.bind(this)); |
|||
} else { |
|||
Store.dispatch(sendToAddressStateAlt(json)); |
|||
|
|||
this.setState(Object.assign({}, this.state, { |
|||
utxoMethodInProgress: false, |
|||
})); |
|||
} |
|||
} else { |
|||
Store.dispatch(sendToAddressStateAlt(json)); |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
`${translate('TOASTR.SIGNED_TX_GENERATED_FAIL')}`, |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'error' |
|||
) |
|||
); |
|||
|
|||
this.setState(Object.assign({}, this.state, { |
|||
utxoMethodInProgress: false, |
|||
})); |
|||
} |
|||
|
|||
// console.log(json);
|
|||
}.bind(this)); |
|||
} |
|||
|
|||
renderSignedTx(isRawTx) { |
|||
let substrBlocks; |
|||
|
|||
if (this.props.ActiveCoin.mode === 'basilisk') { |
|||
substrBlocks = isRawTx ? 3 : 8; |
|||
} else { |
|||
substrBlocks = 10; |
|||
} |
|||
|
|||
const _lastSendToResponse = this.props.ActiveCoin.lastSendToResponse[isRawTx ? 'rawtx' : 'signedtx']; |
|||
const substrLength = _lastSendToResponse.length / substrBlocks; |
|||
let out = []; |
|||
|
|||
for (let i = 0; i < substrBlocks; i++) { |
|||
out.push( |
|||
<div key={ i }>{ _lastSendToResponse.substring(i * substrLength, substrLength * i + substrLength) }</div> |
|||
); |
|||
} |
|||
|
|||
return out.length ? out : null; |
|||
} |
|||
|
|||
renderKey(key) { |
|||
if (key === 'signedtx') { |
|||
return this.renderSignedTx(); |
|||
} else if (key === 'rawtx') { |
|||
return this.renderSignedTx(true); |
|||
} else if (key === 'complete' || key === 'completed' || key === 'result') { |
|||
const _lastSendToResponse = this.props.ActiveCoin.lastSendToResponse; |
|||
|
|||
if (_lastSendToResponse[key] === true || |
|||
_lastSendToResponse[key] === 'success') { |
|||
return ( |
|||
<span className="label label-success">{ _lastSendToResponse[key] === true ? 'true' : 'success' }</span> |
|||
); |
|||
} else { |
|||
if (key === 'result' && |
|||
_lastSendToResponse.result && |
|||
typeof _lastSendToResponse.result !== 'object') { |
|||
return ( |
|||
<span>{ _lastSendToResponse.result }</span> |
|||
); |
|||
} else { |
|||
return ( |
|||
<span className="label label-danger">false</span> |
|||
); |
|||
} |
|||
} |
|||
} else if (key === 'error') { |
|||
const _lastSendToResponse = this.props.ActiveCoin.lastSendToResponse; |
|||
|
|||
if (Object.keys(_lastSendToResponse[key]).length) { |
|||
return ( |
|||
<span>{ JSON.stringify(_lastSendToResponse[key], null, '\t') }</span> |
|||
); |
|||
} else { |
|||
return ( |
|||
<span className="label label-danger">{ _lastSendToResponse[key] }</span> |
|||
); |
|||
} |
|||
} else if (key === 'sendrawtransaction') { |
|||
const _lastSendToResponse = this.props.ActiveCoin.lastSendToResponse; |
|||
|
|||
if (_lastSendToResponse[key] === 'success') { |
|||
return ( |
|||
<span className="label label-success">true</span> |
|||
); |
|||
} else { |
|||
return ( |
|||
<span className="label label-danger">false</span> |
|||
); |
|||
} |
|||
} else if (key === 'txid' || key === 'sent') { |
|||
const _lastSendToResponse = this.props.ActiveCoin.lastSendToResponse; |
|||
|
|||
return ( |
|||
<span>{ _lastSendToResponse[key] }</span> |
|||
); |
|||
} else if (key === 'tag') { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
renderSendCoinResponse() { |
|||
return SendCoinResponseRender.call(this); |
|||
} |
|||
|
|||
// experimental, ask @kolo for details if required
|
|||
getOAdress() { |
|||
resolveOpenAliasAddress(this.state.sendToOA) |
|||
.then(function(json) { |
|||
const reply = json.Answer; |
|||
|
|||
if (reply && |
|||
reply.length) { |
|||
for (let i = 0; i < reply.length; i++) { |
|||
const _address = reply[i].data.split(' '); |
|||
const coin = _address[0].replace('"oa1:', ''); |
|||
const coinAddress = _address[1].replace('recipient_address=', '').replace(';', ''); |
|||
|
|||
if (coin.toUpperCase() === this.props.ActiveCoin.coin) { |
|||
this.setState(Object.assign({}, this.state, { |
|||
sendTo: coinAddress, |
|||
})); |
|||
} |
|||
} |
|||
|
|||
if (this.state.sendTo === '') { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
'Couldn\'t find any ' + this.props.ActiveCoin.coin + ' addresses', |
|||
'OpenAlias', |
|||
'error' |
|||
) |
|||
); |
|||
} |
|||
} else { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
'Couldn\'t find any addresses', |
|||
'OpenAlias', |
|||
'error' |
|||
) |
|||
); |
|||
} |
|||
}.bind(this)); |
|||
} |
|||
|
|||
renderOASendUI() { |
|||
if (Config.openAlias) { |
|||
return OASendUIRender.call(this); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
renderSendApiTypeSelector() { |
|||
if (this.props.ActiveCoin.mode === 'basilisk') { |
|||
return SendApiTypeSelectorRender.call(this); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
// TODO same as in walletsNav and receiveCoin, find a way to reuse it?
|
|||
checkBalance() { |
|||
let _balance = '0'; |
|||
const _mode = this.props.ActiveCoin.mode; |
|||
|
|||
if (_mode === 'full') { |
|||
_balance = this.props.ActiveCoin.balance || 0; |
|||
} else if (_mode === 'basilisk') { |
|||
if (this.props.ActiveCoin.cache) { |
|||
const _cache = this.props.ActiveCoin.cache; |
|||
const _coin = this.props.ActiveCoin.coin; |
|||
const _address = this.props.ActiveCoin.activeAddress; |
|||
|
|||
if (_address && |
|||
_cache[_coin] && |
|||
_cache[_coin][_address] && |
|||
_cache[_coin][_address].getbalance && |
|||
_cache[_coin][_address].getbalance.data && |
|||
(_cache[_coin][_address].getbalance.data.balance || |
|||
_cache[_coin][_address].getbalance.data.interest)) { |
|||
const _regBalance = _cache[_coin][_address].getbalance.data.balance ? _cache[_coin][_address].getbalance.data.balance : 0; |
|||
|
|||
_balance = _regBalance; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return _balance; |
|||
} |
|||
|
|||
// TODO: reduce to a single toast
|
|||
validateSendFormData() { |
|||
let valid = true; |
|||
if (!this.state.sendTo || this.state.sendTo.length < 34) { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('SEND.SEND_TO_ADDRESS_MIN_LENGTH'), |
|||
'', |
|||
'error' |
|||
) |
|||
); |
|||
valid = false; |
|||
} |
|||
|
|||
if (!isPositiveNumber(this.state.amount)) { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('SEND.AMOUNT_POSITIVE_NUMBER'), |
|||
'', |
|||
'error' |
|||
) |
|||
); |
|||
valid = false; |
|||
} |
|||
|
|||
if (!isPositiveNumber(this.state.fee)) { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('SEND.FEE_POSITIVE_NUMBER'), |
|||
'', |
|||
'error' |
|||
) |
|||
); |
|||
valid = false; |
|||
} |
|||
|
|||
if (!isPositiveNumber(this.getTotalAmount())) { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('SEND.TOTAL_AMOUNT_POSITIVE_NUMBER'), |
|||
'', |
|||
'error' |
|||
) |
|||
); |
|||
valid = false; |
|||
} |
|||
|
|||
/*if ((this.props.ActiveCoin.mode === 'basilisk' && Number(this.state.amount) > Number(this.state.sendFromAmount)) || |
|||
(this.props.ActiveCoin.mode === 'full' && Number(this.state.amount) > Number(this.checkBalance()))) { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('SEND.INSUFFICIENT_FUNDS'), |
|||
'', |
|||
'error' |
|||
) |
|||
); |
|||
valid = false; |
|||
}*/ |
|||
|
|||
return valid; |
|||
} |
|||
|
|||
getTotalAmount() { |
|||
return Number(this.state.amount) - Number(this.state.fee); |
|||
} |
|||
|
|||
render() { |
|||
if (this.props.ActiveCoin && |
|||
this.props.ActiveCoin.send && |
|||
this.props.ActiveCoin.mode !== 'native') { |
|||
return SendCoinRender.call(this); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
ActiveCoin: { |
|||
coin: state.ActiveCoin.coin, |
|||
mode: state.ActiveCoin.mode, |
|||
send: state.ActiveCoin.send, |
|||
receive: state.ActiveCoin.receive, |
|||
balance: state.ActiveCoin.balance, |
|||
cache: state.ActiveCoin.cache, |
|||
activeAddress: state.ActiveCoin.activeAddress, |
|||
lastSendToResponse: state.ActiveCoin.lastSendToResponse, |
|||
addresses: state.ActiveCoin.addresses, |
|||
}, |
|||
Dashboard: { |
|||
activeHandle: state.Dashboard.activeHandle, |
|||
}, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(SendCoin); |
@ -1,402 +0,0 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { |
|||
secondsElapsedToString, |
|||
secondsToString |
|||
} from '../../../util/time'; |
|||
|
|||
import QRModal from '../qrModal/qrModal'; |
|||
|
|||
export const UTXOCacheInfoRender = function(refreshCacheData, isReadyToUpdate, waitUntilCallIsFinished, timestamp) { |
|||
const _progress = 100 - this.state.currentStackLength * 100 / this.state.totalStackLength; |
|||
|
|||
return ( |
|||
<div className="col-lg-12"> |
|||
<hr /> |
|||
{ translate('SEND.TOTAL_UTXO_AVAILABLE') }: |
|||
{ refreshCacheData ? refreshCacheData.data && refreshCacheData.data.length : translate('SEND.PRESS_UPDATE_BTN') }<br /> |
|||
<div className={ !timestamp ? 'hide' : '' }> |
|||
{ translate('SEND.LAST_UPDATED') } @ |
|||
{ secondsToString(refreshCacheData ? refreshCacheData.timestamp : 0, true) } | |
|||
{ secondsElapsedToString(timestamp || 0) }&nbps; |
|||
{ translate('SEND.AGO') }<br /> |
|||
</div> |
|||
<div className={ isReadyToUpdate ? 'hide' : '' }> |
|||
{ translate('SEND.NEXT_UPDATE_IN') } { secondsElapsedToString(600 - timestamp) }s |
|||
</div> |
|||
<div className={ 'full-width margin-bottom-10 margin-top-10 ' + (this.state.currentStackLength === 1 || (this.state.currentStackLength === 0 && this.state.totalStackLength === 0) ? 'hide' : 'progress progress-sm') }> |
|||
<div |
|||
className="progress-bar progress-bar-striped active progress-bar-indicating progress-bar-success font-size-80-percent" |
|||
style={{ width: `${_progress}%` }}> |
|||
{ translate('SEND.PROCESSING_REQ') }: { this.state.currentStackLength } / { this.state.totalStackLength } |
|||
</div> |
|||
</div> |
|||
<button |
|||
type="button" |
|||
className={ 'margin-top-10 ' + (isReadyToUpdate ? 'btn btn-primary waves-effect waves-light' : 'hide') } |
|||
onClick={ this._fetchNewUTXOData } |
|||
disabled={ waitUntilCallIsFinished }> |
|||
{ waitUntilCallIsFinished ? `${translate('SEND.LOCKED_PLEASE_WAIT')}...` : translate('SEND.UPDATE') } |
|||
</button> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export const SendCoinResponseRender = function() { |
|||
if (this.props.ActiveCoin.lastSendToResponse) { |
|||
let items = []; |
|||
const _response = this.props.ActiveCoin.lastSendToResponse; |
|||
|
|||
for (let key in _response) { |
|||
if (key !== 'tag') { |
|||
items.push( |
|||
<tr key={ key }> |
|||
<td>{ key }</td> |
|||
<td>{ this.renderKey(key) }</td> |
|||
</tr> |
|||
); |
|||
} |
|||
} |
|||
|
|||
return items; |
|||
} else { |
|||
return ( |
|||
<tr className="hover--none"> |
|||
<td colSpan="2"> |
|||
<div className="padding-20 text-align-center"> |
|||
<div className="vertical-padding-10 horizontal-padding-0"> |
|||
{ translate('SEND.PROCESSING_TRANSACTION') }...<br /> |
|||
{ translate('SEND.NOTE_IT_WILL_TAKE') }. |
|||
</div> |
|||
<div className="loader-wrapper active"> |
|||
<div className="loader-layer loader-blue"> |
|||
<div className="loader-circle-left"> |
|||
<div className="circle"></div> |
|||
</div> |
|||
<div className="loader-circle-gap"></div> |
|||
<div className="loader-circle-right"> |
|||
<div className="circle"></div> |
|||
</div> |
|||
</div> |
|||
<div className="loader-layer loader-red"> |
|||
<div className="loader-circle-left"> |
|||
<div className="circle"></div> |
|||
</div> |
|||
<div className="loader-circle-gap"></div> |
|||
<div className="loader-circle-right"> |
|||
<div className="circle"></div> |
|||
</div> |
|||
</div> |
|||
<div className="loader-layer loader-green"> |
|||
<div className="loader-circle-left"> |
|||
<div className="circle"></div> |
|||
</div> |
|||
<div className="loader-circle-gap"></div> |
|||
<div className="loader-circle-right"> |
|||
<div className="circle"></div> |
|||
</div> |
|||
</div> |
|||
<div className="loader-layer loader-yellow"> |
|||
<div className="loader-circle-left"> |
|||
<div className="circle"></div> |
|||
</div> |
|||
<div className="loader-circle-gap"></div> |
|||
<div className="loader-circle-right"> |
|||
<div className="circle"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export const OASendUIRender = function() { |
|||
return ( |
|||
<div className="row"> |
|||
<div className="col-lg-6 form-group form-material"> |
|||
<label |
|||
className="control-label" |
|||
htmlFor="kmdWalletSendTo">Fetch OpenAlias recipient address</label> |
|||
<input |
|||
type="text" |
|||
className="form-control" |
|||
name="sendToOA" |
|||
onChange={ this.updateInput } |
|||
id="kmdWalletSendTo" |
|||
placeholder="Enter an alias as address@site.com" |
|||
autoComplete="off" |
|||
required /> |
|||
</div> |
|||
<div className="col-lg-6 form-group form-material"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this.getOAdress }> |
|||
Get address |
|||
</button> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export const SendApiTypeSelectorRender = function() { |
|||
return ( |
|||
<div className="row"> |
|||
<div className="col-lg-8 margin-bottom-10"> |
|||
<span className="pointer"> |
|||
<label className="switch"> |
|||
<input |
|||
type="checkbox" |
|||
checked={ this.state.sendApiType } |
|||
readOnly /> |
|||
<div |
|||
className="slider" |
|||
onClick={ this.toggleSendAPIType }></div> |
|||
</label> |
|||
<div |
|||
className="toggle-label" |
|||
onClick={ this.toggleSendAPIType }> |
|||
{ translate('SEND.SEND_VIA') } (sendtoaddress API) |
|||
</div> |
|||
</span> |
|||
</div> |
|||
<div className="col-lg-4 text-right"> |
|||
<QRModal |
|||
mode="scan" |
|||
setRecieverFromScan={ this.setRecieverFromScan } /> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export const SendCoinRender = function() { |
|||
return ( |
|||
<div className="col-sm-12 padding-top-10"> |
|||
<div className="col-xlg-12 col-md-12 col-sm-12 col-xs-12"> |
|||
<div className="steps row margin-top-10"> |
|||
<div className={ 'step col-md-4' + (this.state.currentStep === 0 ? ' current' : '') }> |
|||
<span className="step-number">1</span> |
|||
<div className="step-desc"> |
|||
<span className="step-title">{ translate('INDEX.FILL_SEND_FORM') }</span> |
|||
<p>{ translate('INDEX.FILL_SEND_DETAILS') }</p> |
|||
</div> |
|||
</div> |
|||
<div className={ 'step col-md-4' + (this.state.currentStep === 1 ? ' current' : '') }> |
|||
<span className="step-number">2</span> |
|||
<div className="step-desc"> |
|||
<span className="step-title">{ translate('INDEX.CONFIRMING') }</span> |
|||
<p>{ translate('INDEX.CONFIRM_DETAILS') }</p> |
|||
</div> |
|||
</div> |
|||
<div className={ 'step col-md-4' + (this.state.currentStep === 2 ? ' current' : '') }> |
|||
<span className="step-number">3</span> |
|||
<div className="step-desc"> |
|||
<span className="step-title">{ translate('INDEX.PROCESSING_TX') }</span> |
|||
<p>{ translate('INDEX.PROCESSING_DETAILS') }</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className={ 'panel' + (this.state.currentStep === 0 ? '' : ' hide') }> |
|||
<div className="panel-heading"> |
|||
<h3 className="panel-title"> |
|||
{ translate('INDEX.SEND') } { this.props.ActiveCoin.coin } |
|||
</h3> |
|||
</div> |
|||
<div className="panel-body container-fluid"> |
|||
<form |
|||
className="edexcoin-send-form" |
|||
method="post" |
|||
autoComplete="off"> |
|||
{ this.renderSendApiTypeSelector() } |
|||
<div className="row"> |
|||
<div className={ this.props.ActiveCoin.mode === 'basilisk' ? 'col-xlg-12 form-group form-material' : 'hide' }> |
|||
<label |
|||
className="control-label" |
|||
htmlFor="edexcoinSendFrom"> |
|||
{ translate('INDEX.SEND_FROM') } |
|||
</label> |
|||
{ this.renderAddressList() } |
|||
</div> |
|||
</div> |
|||
{ this.renderOASendUI() } |
|||
<div className="row"> |
|||
<div className="col-xlg-12 form-group form-material"> |
|||
<label |
|||
className="control-label" |
|||
htmlFor="edexcoinSendTo"> |
|||
{ translate('INDEX.SEND_TO') } |
|||
</label> |
|||
<input |
|||
type="text" |
|||
className="form-control" |
|||
id="edexcoinSendTo" |
|||
name="sendTo" |
|||
placeholder={ translate('SEND.ENTER_AN_ADDRESS') } |
|||
autoComplete="off" |
|||
value={ this.state.sendTo } |
|||
onChange={ this.updateInput } |
|||
required /> |
|||
</div> |
|||
<div className="col-lg-6 form-group form-material"> |
|||
<label |
|||
className="control-label" |
|||
htmlFor="edexcoinAmount"> |
|||
{ this.props.ActiveCoin.coin } |
|||
</label> |
|||
<input |
|||
type="number" |
|||
min="0" |
|||
className="form-control" |
|||
id="edexcoinAmount" |
|||
name="amount" |
|||
placeholder="0.001" |
|||
autoComplete="off" |
|||
value={ this.state.amount } |
|||
onChange={ this.updateInput } /> |
|||
</div> |
|||
<div className="col-lg-6 form-group form-material"> |
|||
<label |
|||
className="control-label" |
|||
htmlFor="edexcoinFee"> |
|||
{ translate('INDEX.FEE') } |
|||
</label> |
|||
<input |
|||
type="number" |
|||
min="0" |
|||
className="form-control" |
|||
id="edexcoinFee" |
|||
name="fee" |
|||
value={ this.state.fee } |
|||
placeholder="0.001" |
|||
autoComplete="off" |
|||
onChange={ this.updateInput } /> |
|||
</div> |
|||
<div className="col-lg-12"> |
|||
<strong> |
|||
{ translate('INDEX.TOTAL') } |
|||
({ translate('INDEX.AMOUNT_SM') } - { translate('INDEX.FEE') }): |
|||
</strong> |
|||
{ this.getTotalAmount() } { this.props.ActiveCoin.coin } |
|||
</div> |
|||
<div className={ this.state.sendApiType ? 'hide' : 'col-lg-10 margin-top-30' }> |
|||
<span className="pointer"> |
|||
<label className="switch"> |
|||
<input |
|||
type="checkbox" |
|||
checked={ this.state.sendSig } |
|||
readOnly /> |
|||
<div |
|||
className="slider" |
|||
onClick={ this.toggleSendSig }></div> |
|||
</label> |
|||
<div |
|||
className="toggle-label" |
|||
onClick={ this.toggleSendSig }> |
|||
{ translate('INDEX.DONT_SEND') } |
|||
</div> |
|||
</span> |
|||
</div> |
|||
{ this.renderUTXOCacheInfo()} |
|||
<div className="col-lg-12"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light pull-right" |
|||
onClick={ () => this.changeSendCoinStep(1) } |
|||
disabled={ !this.state.sendFrom || !this.state.sendTo || !this.state.amount }> |
|||
{ translate('INDEX.SEND') } |
|||
{ Number(this.state.amount) - Number(this.state.fee) } { this.props.ActiveCoin.coin } |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className={ 'col-xlg-12 col-md-12 col-sm-12 col-xs-12' + (this.state.currentStep === 1 ? '' : ' hide') }> |
|||
<div className="panel"> |
|||
<div className="panel-body"> |
|||
<div className="row"> |
|||
<div className="col-xs-12"> |
|||
<strong>{ translate('INDEX.TO') }</strong> |
|||
</div> |
|||
<div className="col-lg-6 col-sm-6 col-xs-12">{ this.state.sendTo }</div> |
|||
<div className="col-lg-6 col-sm-6 col-xs-6"> |
|||
{ this.state.amount } { this.props.ActiveCoin.coin } |
|||
</div> |
|||
<div className="col-lg-6 col-sm-6 col-xs-12">{ translate('INDEX.TX_FEE_REQ') }</div> |
|||
<div className="col-lg-6 col-sm-6 col-xs-6"> |
|||
{ this.state.fee } { this.props.ActiveCoin.coin } |
|||
</div> |
|||
</div> |
|||
<br /> |
|||
|
|||
<div className="row"> |
|||
<div className="col-xs-12"> |
|||
<strong>{ translate('INDEX.FROM') }</strong> |
|||
</div> |
|||
<div className="col-lg-6 col-sm-6 col-xs-12"> |
|||
{ this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin] } |
|||
</div> |
|||
<div className="col-lg-6 col-sm-6 col-xs-6 confirm-currency-send-container"> |
|||
{ Number(this.state.amount) - Number(this.state.fee) } { this.props.ActiveCoin.coin } |
|||
</div> |
|||
</div> |
|||
<div className="widget-body-footer"> |
|||
<a |
|||
className="btn btn-default waves-effect waves-light" |
|||
onClick={ () => this.changeSendCoinStep(0) }>{ translate('INDEX.BACK') }</a> |
|||
<div className="widget-actions pull-right"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary" |
|||
onClick={ () => this.changeSendCoinStep(2) }> |
|||
{ translate('INDEX.CONFIRM') } |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className={ 'col-xlg-12 col-md-12 col-sm-12 col-xs-12' + (this.state.currentStep === 2 ? '' : ' hide') }> |
|||
<div className="panel"> |
|||
<div className="panel-heading"> |
|||
<h4 className="panel-title"> |
|||
{ translate('INDEX.TRANSACTION_RESULT') } |
|||
</h4> |
|||
<div className={ !this.state.sendSig ? 'hide' : 'center' }> |
|||
{ translate('SEND.YOU_PICKED_OPT') } |
|||
</div> |
|||
<table className="table table-hover table-striped"> |
|||
<thead> |
|||
<tr> |
|||
<th>{ translate('INDEX.KEY') }</th> |
|||
<th>{ translate('INDEX.INFO') }</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{ this.renderSendCoinResponse() } |
|||
</tbody> |
|||
</table> |
|||
<div className="widget-body-footer"> |
|||
<div className="widget-actions margin-bottom-15 margin-right-15"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary" |
|||
onClick={ () => this.changeSendCoinStep(0) } |
|||
disabled={ this.state.utxoMethodInProgress }> |
|||
{ !this.state.utxoMethodInProgress ? translate('INDEX.MAKE_ANOTHER_TX') : `${translate('SEND.PLEASE_WAIT')}...` } |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
Loading…
Reference in new issue