Browse Source

AGP-230 merge send coin

v0.25
pbca26 7 years ago
parent
commit
24b339e76d
  1. 129
      react/src/actions/actions/nativeSend.js
  2. 917
      react/src/components/dashboard/_sendCoin/sendCoin.js
  3. 402
      react/src/components/dashboard/_sendCoin/sendCoin.render.js
  4. 4
      react/src/components/dashboard/jumblr/jumblr.render.js
  5. 1016
      react/src/components/dashboard/sendCoin/sendCoin.js
  6. 627
      react/src/components/dashboard/sendCoin/sendCoin.render.js
  7. 4
      react/src/components/dashboard/walletsNative/walletsNative.render.js
  8. 460
      react/src/components/dashboard/walletsNativeSend/walletsNativeSend.js
  9. 213
      react/src/components/dashboard/walletsNativeSend/walletsNativeSend.render.js
  10. 17
      react/src/components/overrides.scss

129
react/src/actions/actions/nativeSend.js

@ -1,4 +1,7 @@
import { DASHBOARD_ACTIVE_COIN_NATIVE_OPIDS } from '../storeType';
import {
DASHBOARD_ACTIVE_COIN_NATIVE_OPIDS,
DASHBOARD_ACTIVE_COIN_SENDTO
} from '../storeType';
import { translate } from '../../translate/translate';
import { triggerToaster } from '../actionCreators';
import Config from '../../config';
@ -106,6 +109,7 @@ export function sendNativeTx(coin, _payload) {
);
}
} else {
dispatch(sendToAddressState(JSON.parse(json).result));
dispatch(
triggerToaster(
translate('TOASTR.TX_SENT_ALT'),
@ -127,88 +131,41 @@ export function getKMDOPIDState(json) {
// remove
export function getKMDOPID(opid, coin) {
let tmpopidOutput = '';
let ajaxDataToHex;
if (opid === undefined) {
ajaxDataToHex = null;
} else {
ajaxDataToHex = `["${opid}"]`;
}
return dispatch => {
return iguanaHashHex(ajaxDataToHex, dispatch).then((hashHexJson) => {
if (hashHexJson === '5b226e756c6c225d00') {
hashHexJson = '';
}
let payload;
let passthruAgent = getPassthruAgent(coin);
let tmpIguanaRPCAuth = `tmpIgRPCUser@${sessionStorage.getItem('IguanaRPCAuth')}`;
if (passthruAgent === 'iguana') {
payload = {
userpass: tmpIguanaRPCAuth,
agent: passthruAgent,
method: 'passthru',
asset: coin,
function: 'z_getoperationstatus',
hex: hashHexJson,
};
} else {
payload = {
userpass: tmpIguanaRPCAuth,
agent: passthruAgent,
method: 'passthru',
function: 'z_getoperationstatus',
hex: hashHexJson,
};
}
let _fetchConfig = {
method: 'POST',
body: JSON.stringify(payload),
};
if (Config.cli.default) {
payload = {
mode: null,
chain: coin,
cmd: 'z_getoperationstatus',
};
const payload = {
mode: null,
chain: coin,
cmd: 'z_getoperationstatus',
};
_fetchConfig = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ payload: payload }),
};
}
const _fetchConfig = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ payload: payload }),
};
fetch(
Config.cli.default ? `http://127.0.0.1:${Config.agamaPort}/shepherd/cli` : `http://127.0.0.1:${Config.iguanaCorePort}`,
_fetchConfig
)
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'getKMDOPID',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
if (Config.cli.default) {
json = json.result;
}
dispatch(getKMDOPIDState(json));
})
fetch(
`http://127.0.0.1:${Config.agamaPort}/shepherd/cli`,
_fetchConfig
)
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'getKMDOPID',
'Error',
'error'
)
);
})
}
.then(response => response.json())
.then(json => {
json = json.result;
dispatch(getKMDOPIDState(json));
})
};
}
export function sendToAddressPromise(coin, address, amount) {
@ -253,4 +210,18 @@ export function sendToAddressPromise(coin, address, amount) {
resolve(json);
});
});
}
export function sendToAddressState(json) {
return {
type: DASHBOARD_ACTIVE_COIN_SENDTO,
lastSendToResponse: json,
}
}
export function clearLastSendToResponseState() {
return {
type: DASHBOARD_ACTIVE_COIN_SENDTO,
lastSendToResponse: null,
}
}

917
react/src/components/dashboard/_sendCoin/sendCoin.js

@ -0,0 +1,917 @@
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>&nbsp;&nbsp;
<span className="text">
[ { mainAddressAmount } { this.props.ActiveCoin.coin } ]&nbsp;&nbsp;
{ 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>&nbsp;&nbsp;
<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>&nbsp;&nbsp;
<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>&nbsp;&nbsp;
<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() }&nbsp;&nbsp;
</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);

402
react/src/components/dashboard/_sendCoin/sendCoin.render.js

@ -0,0 +1,402 @@
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) }&nbsp;|&nbsp;
{ 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') }&nbsp;
({ translate('INDEX.AMOUNT_SM') } - { translate('INDEX.FEE') }):
</strong>&nbsp;
{ 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') }&nbsp;
{ 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>
);
};

4
react/src/components/dashboard/jumblr/jumblr.render.js

@ -2,7 +2,7 @@ import React from 'react';
import { translate } from '../../../translate/translate';
import WalletsHeader from '../walletsHeader/walletsHeader';
import WalletsNativeSend from '../walletsNativeSend/walletsNativeSend';
import SendCoin from '../sendCoin/sendCoin';
import ReceiveCoin from '../receiveCoin/receiveCoin';
export const JumblrRenderSecretAddressList = function(type) {
@ -338,7 +338,7 @@ export const JumblrRender = function() {
<div className={ 'tab-pane' + (this.state.activeTab === 3 ? ' active' : '') }>
<p>{ translate('JUMBLR.DEPOSIT_FORM_P1') }</p>
<p className="padding-bottom-20">{ translate('JUMBLR.DEPOSIT_FORM_P2') }</p>
<WalletsNativeSend
<SendCoin
renderFormOnly="true"
activeSection="send" />
</div>

1016
react/src/components/dashboard/sendCoin/sendCoin.js

File diff suppressed because it is too large

627
react/src/components/dashboard/sendCoin/sendCoin.render.js

@ -1,402 +1,327 @@
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;
export const AddressListRender = function() {
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) }&nbsp;|&nbsp;
{ 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>
<div className={ `btn-group bootstrap-select form-control form-material showkmdwalletaddrs show-tick ${(this.state.addressSelectorOpen ? 'open' : '')}` }>
<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') }
className="btn dropdown-toggle btn-info"
title="Select private address"
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"
onClick={ () => this.updateAddressSelection(null, 'public', null) }>
<a>
<span className="text">{ translate('INDEX.T_FUNDS') }</span>
<span
className="glyphicon glyphicon-ok check-mark pull-right"
style={{ display: this.state.sendFrom === null ? 'inline-block' : 'none' }}></span>
</a>
</li>
{ this.renderAddressByType('public') }
{ this.renderAddressByType('private') }
</ul>
</div>
</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>
);
export const _SendFormRender = function() {
return (
<form
className="extcoin-send-form"
method="post"
autoComplete="off">
{ this.state.renderAddressDropdown &&
<div className="row">
<div className="col-xlg-12 form-group form-material">
<label className="control-label padding-bottom-10">{ translate('INDEX.SEND_FROM') }</label>
{ this.renderAddressList() }
</div>
</div>
}
}
<div className="row">
<div className="col-xlg-12 form-group form-material">
<label
className="control-label"
htmlFor="kmdWalletSendTo">{ translate('INDEX.SEND_TO') }</label>
<input
type="text"
className="form-control"
name="sendTo"
onChange={ this.updateInput }
value={ this.state.sendTo }
id="kmdWalletSendTo"
placeholder={ translate('SEND.ENTER_T_OR_Z_ADDR') }
autoComplete="off"
required />
</div>
<div className="col-lg-12 form-group form-material">
<label
className="control-label"
htmlFor="kmdWalletAmount">
{ translate('INDEX.AMOUNT') }
</label>
<input
type="text"
className="form-control"
name="amount"
value={ this.state.amount !== 0 ? this.state.amount : '' }
onChange={ this.updateInput }
id="kmdWalletAmount"
placeholder="0.000"
autoComplete="off" />
</div>
<div className={ 'col-lg-6 form-group form-material' + (this.isTransparentTx() ? '' : ' hide') }>
<span className="pointer">
<label className="switch">
<input
type="checkbox"
checked={ this.state.substractFee } />
<div
className="slider"
onClick={ () => this.toggleSubstractFee() }></div>
</label>
<div
className="toggle-label"
onClick={ () => this.toggleSubstractFee() }>
{ translate('DASHBOARD.SUBSTRACT_FEE') }
</div>
</span>
</div>
<div className="col-lg-6 form-group form-material hide">
<label
className="control-label"
htmlFor="kmdWalletFee">
{ translate('INDEX.FEE') }
</label>
<input
type="text"
className="form-control"
name="fee"
onChange={ this.updateInput }
id="kmdWalletFee"
placeholder="0.000"
value={ this.state.fee !== 0 ? this.state.fee : '' }
autoComplete="off" />
</div>
<div className="col-lg-12 hide">
<span>
<strong>{ translate('INDEX.TOTAL') }:</strong>&nbsp;
{ this.state.amount } - { this.state.fee }/kb = { Number(this.state.amount) - Number(this.state.fee) }&nbsp;
{ this.props.ActiveCoin.coin }
</span>
</div>
<div className="col-lg-12">
<button
type="button"
className="btn btn-primary waves-effect waves-light pull-right"
onClick={ this.props.renderFormOnly ? this.handleSubmit : () => this.changeSendCoinStep(1) }
disabled={ !this.state.sendTo || !this.state.amount }>
{ translate('INDEX.SEND') } { this.state.amount } { this.props.ActiveCoin.coin }
</button>
</div>
</div>
</form>
);
}
return items;
export const SendRender = function() {
if (this.props.renderFormOnly) {
return (
<div>{ this.SendFormRender() }</div>
);
} 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 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 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={ '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 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 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>
</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>
);
};
</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 className={ 'col-xlg-12 col-md-12 col-sm-12 col-xs-12' + (this.state.currentStep === 0 ? '' : ' hide') }>
<div className="panel">
<div className="panel-heading">
<h3 className="panel-title">
{ translate('INDEX.SEND') } { this.props.ActiveCoin.coin }
</h3>
</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 className="qr-modal-send-block">
<QRModal
mode="scan"
setRecieverFromScan={ this.setRecieverFromScan } />
</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 className="panel-body container-fluid">
{ this.SendFormRender() }
</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={ '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-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 className="col-xs-12">
<strong>{ translate('INDEX.TO') }</strong>
</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
className="col-lg-6 col-sm-6 col-xs-12"
style={{ overflow: 'hidden' }}>{ 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-12">
<strong>
{ translate('INDEX.TOTAL') }&nbsp;
({ translate('INDEX.AMOUNT_SM') } - { translate('INDEX.FEE') }):
</strong>&nbsp;
{ 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 className={ this.state.substractFee ? 'col-lg-6 col-sm-6 col-xs-12 padding-top-10 bold' : 'hide' }>{ translate('DASHBOARD.SUBSTRACT_FEE') }</div>
</div>
{ this.state.sendFrom &&
<div className="row padding-top-20">
<div className="col-xs-12">
<strong>{ translate('INDEX.FROM') }</strong>
</div>
<div
className="col-lg-6 col-sm-6 col-xs-12"
style={{ overflow: 'hidden' }}>{ this.state.sendFrom }</div>
<div className="col-lg-6 col-sm-6 col-xs-6 confirm-currency-send-container">
{ Number(this.state.amount) } { this.props.ActiveCoin.coin }
</div>
</div>
{ this.renderUTXOCacheInfo()}
<div className="col-lg-12">
}
<div className="widget-body-footer">
<a
className="btn btn-default waves-effect waves-light"
onClick={ () => this.changeSendCoinStep(0, true) }>{ translate('INDEX.BACK') }</a>
<div className="widget-actions pull-right">
<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') }&nbsp;
{ Number(this.state.amount) - Number(this.state.fee) } { this.props.ActiveCoin.coin }
className="btn btn-primary"
onClick={ () => this.changeSendCoinStep(2) }>
{ translate('INDEX.CONFIRM') }
</button>
</div>
</div>
</form>
</div>
</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 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>
<table className="table table-hover table-striped">
<thead>
<tr>
<th>{ translate('INDEX.KEY') }</th>
<th>{ translate('INDEX.INFO') }</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Result
</td>
<td>
<span className="label label-success">success</span>
</td>
</tr>
<tr>
<td>Transaction ID</td>
<td>{ this.state.lastSendToResponse }</td>
</tr>
</tbody>
</table>
</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 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) }>
{ translate('INDEX.MAKE_ANOTHER_TX') }
</button>
</div>
</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>
{ this.renderOPIDListCheck() &&
<div className="col-xs-12">
<div className="row">
<div className="panel nav-tabs-horizontal">
<div>
<div className="col-xlg-12 col-lg-12 col-sm-12 col-xs-12">
<div className="panel">
<header className="panel-heading">
<h3 className="panel-title">
{ translate('INDEX.OPERATIONS_STATUSES') }
</h3>
</header>
<div className="panel-body">
<table
className="table table-hover dataTable table-striped"
width="100%">
<thead>
<tr>
<th>{ translate('INDEX.STATUS') }</th>
<th>ID</th>
<th>{ translate('INDEX.TIME') }</th>
<th>{ translate('INDEX.RESULT') }</th>
</tr>
</thead>
<tbody>
{ this.renderOPIDList() }
</tbody>
<tfoot>
<tr>
<th>{ translate('INDEX.STATUS') }</th>
<th>ID</th>
<th>{ translate('INDEX.TIME') }</th>
<th>{ translate('INDEX.RESULT') }</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
</div>
</div>
);
);
}
};

4
react/src/components/dashboard/walletsNative/walletsNative.render.js

@ -1,7 +1,7 @@
import React from 'react';
import WalletsBalance from '../walletsBalance/walletsBalance';
import WalletsInfo from '../walletsInfo/walletsInfo';
import WalletsNativeSend from '../walletsNativeSend/walletsNativeSend';
import SendCoin from '../sendCoin/sendCoin';
import WalletsProgress from '../walletsProgress/walletsProgress';
import WalletsData from '../walletsData/walletsData';
import ReceiveCoin from '../receiveCoin/receiveCoin';
@ -33,7 +33,7 @@ const WalletsNativeRender = function() {
<WalletsBalance />
<ReceiveCoin />
<WalletsData />
<WalletsNativeSend />
<SendCoin />
<WalletsInfo />
</div>
</div>

460
react/src/components/dashboard/walletsNativeSend/walletsNativeSend.js

@ -1,460 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import Config from '../../../config';
import { translate } from '../../../translate/translate';
import { secondsToString } from '../../../util/time';
import {
triggerToaster,
sendNativeTx,
getKMDOPID
} from '../../../actions/actionCreators';
import Store from '../../../store';
import {
AddressListRender,
WalletsNativeSendRender,
WalletsNativeSendFormRender,
_WalletsNativeSendFormRender
} from './walletsNativeSend.render';
import { isPositiveNumber } from '../../../util/number';
class WalletsNativeSend extends React.Component {
constructor(props) {
super(props);
this.state = {
addressType: null,
sendFrom: null,
sendFromAmount: 0,
sendTo: '',
amount: 0,
fee: 0,
addressSelectorOpen: false,
renderAddressDropdown: true,
substractFee: false,
};
this.updateInput = this.updateInput.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.openDropMenu = this.openDropMenu.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
this.checkZAddressCount = this.checkZAddressCount.bind(this);
this.setRecieverFromScan = this.setRecieverFromScan.bind(this);
this.renderOPIDListCheck = this.renderOPIDListCheck.bind(this);
this.WalletsNativeSendFormRender = _WalletsNativeSendFormRender.bind(this);
this.isTransparentTx = this.isTransparentTx.bind(this);
this.toggleSubstractFee = this.toggleSubstractFee.bind(this);
}
// TODO: 1) t -> z amount validation
// 2) z -> z amount validation
WalletsNativeSendFormRender() {
return _WalletsNativeSendFormRender.call(this);
}
toggleSubstractFee() {
this.setState({
substractFee: !this.state.substractFee,
});
}
componentWillMount() {
document.addEventListener(
'click',
this.handleClickOutside,
false
);
}
componentWillUnmount() {
document.removeEventListener(
'click',
this.handleClickOutside,
false
);
}
componentWillReceiveProps() {
this.checkZAddressCount();
}
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('kmdWalletSendTo').focus();
}
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,
});
}
}
checkZAddressCount() {
const _addresses = this.props.ActiveCoin.addresses;
if (_addresses &&
(!_addresses.private ||
_addresses.private.length === 0)) {
this.setState({
renderAddressDropdown: false,
});
} else {
this.setState({
renderAddressDropdown: true,
});
}
}
renderAddressByType(type) {
let _items = [];
if (this.props.ActiveCoin.addresses &&
this.props.ActiveCoin.addresses[type] &&
this.props.ActiveCoin.addresses[type].length) {
this.props.ActiveCoin.addresses[type].map((address) => {
if (address.amount > 0) {
_items.push(
<li
className="selected"
key={ address.address }>
<a onClick={ () => this.updateAddressSelection(address.address, type, address.amount) }>
<i className={ 'icon fa-eye' + (type === 'public' ? '' : '-slash') }></i>&nbsp;&nbsp;
<span className="text">
[ { address.amount } { this.props.ActiveCoin.coin } ]&nbsp;&nbsp;
{ type === 'public' ? address.address : address.address.substring(0, 34) + '...' }
</span>
<span
className="glyphicon glyphicon-ok check-mark pull-right"
style={{ display: this.state.sendFrom === address.address ? 'inline-block' : 'none' }}></span>
</a>
</li>
);
}
});
return _items;
} else {
return null;
}
}
renderOPIDListCheck() {
if (this.state.renderAddressDropdown &&
this.props.ActiveCoin.opids &&
this.props.ActiveCoin.opids.length) {
return true;
}
}
renderSelectorCurrentLabel() {
if (this.state.sendFrom) {
return (
<span>
<i className={ 'icon fa-eye' + this.state.addressType === 'public' ? '' : '-slash' }></i>
<span className="text">
[ { this.state.sendFromAmount } { this.props.ActiveCoin.coin } ]
{ this.state.addressType === 'public' ? this.state.sendFrom : this.state.sendFrom.substring(0, 34) + '...' }
</span>
</span>
);
} else {
return (
<span>{ translate('INDEX.T_FUNDS') }</span>
);
}
}
renderAddressList() {
return AddressListRender.call(this);
}
renderOPIDLabel(opid) {
const _satatusDef = {
queued: {
icon: 'warning',
label: 'QUEUED',
},
executing: {
icon: 'info',
label: 'EXECUTING',
},
failed: {
icon: 'danger',
label: 'FAILED',
},
success: {
icon: 'success',
label: 'SUCCESS',
},
};
return (
<span className={ `label label-${_satatusDef[opid.status].icon}` }>
<i className="icon fa-eye"></i>&nbsp;
<span>{ translate(`KMD_NATIVE.${_satatusDef[opid.status].label}`) }</span>
</span>
);
}
renderOPIDResult(opid) {
let isWaitingStatus = true;
if (opid.status === 'queued') {
isWaitingStatus = false;
return (
<i>{ translate('SEND.AWAITING') }...</i>
);
}
if (opid.status === 'executing') {
isWaitingStatus = false;
return (
<i>{ translate('SEND.PROCESSING') }...</i>
);
}
if (opid.status === 'failed') {
isWaitingStatus = false;
return (
<span>
<strong>{ translate('SEND.ERROR_CODE') }:</strong> <span>{ opid.error.code }</span>
<br />
<strong>{ translate('KMD_NATIVE.MESSAGE') }:</strong> <span>{ opid.error.message }</span>
</span>
);
}
if (opid.status === 'success') {
isWaitingStatus = false;
return (
<span>
<strong>{ translate('KMD_NATIVE.TXID') }:</strong> <span>{ opid.result.txid }</span>
<br />
<strong>{ translate('KMD_NATIVE.EXECUTION_SECONDS') }:</strong> <span>{ opid.execution_secs }</span>
</span>
);
}
if (isWaitingStatus) {
return (
<span>{ translate('SEND.WAITING') }...</span>
);
}
}
renderOPIDList() {
if (this.props.ActiveCoin.opids &&
this.props.ActiveCoin.opids.length) {
return this.props.ActiveCoin.opids.map((opid) =>
<tr key={ opid.id }>
<td>{ this.renderOPIDLabel(opid) }</td>
<td>{ opid.id }</td>
<td>{ secondsToString(opid.creation_time) }</td>
<td>{ this.renderOPIDResult(opid) }</td>
</tr>
);
} else {
return null;
}
}
openDropMenu() {
this.setState(Object.assign({}, this.state, {
addressSelectorOpen: !this.state.addressSelectorOpen,
}));
}
updateAddressSelection(address, type, amount) {
this.setState(Object.assign({}, this.state, {
sendFrom: address,
addressType: type,
sendFromAmount: amount,
addressSelectorOpen: !this.state.addressSelectorOpen,
}));
}
updateInput(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleSubmit() {
if (!this.validateSendFormData()) {
return;
}
Store.dispatch(
sendNativeTx(
this.props.ActiveCoin.coin,
this.state
)
);
if (this.state.addressType === 'private') {
setTimeout(() => {
Store.dispatch(
getKMDOPID(
null,
this.props.ActiveCoin.coin
)
);
}, 1000);
}
this.setState({
addressType: null,
sendFrom: null,
sendFromAmount: 0,
sendTo: '',
sendToOA: null,
amount: 0,
fee: 0,
addressSelectorOpen: false,
renderAddressDropdown: true,
substractFee: false,
});
}
// 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'),
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
}
if (!isPositiveNumber(this.state.amount)) {
Store.dispatch(
triggerToaster(
translate('SEND.AMOUNT_POSITIVE_NUMBER'),
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
}
if (((!this.state.sendFrom || this.state.addressType === 'public') &&
this.state.sendTo &&
this.state.sendTo.length === 34 &&
this.props.ActiveCoin.balance &&
this.props.ActiveCoin.balance.transparent &&
Number(this.state.amount) > Number(this.props.ActiveCoin.balance.transparent)) ||
(this.state.addressType === 'public' &&
this.state.sendTo &&
this.state.sendTo.length > 34 &&
Number(this.state.amount) > Number(this.state.sendFromAmount)) ||
(this.state.addressType === 'private' &&
this.state.sendTo &&
this.state.sendTo.length >= 34 &&
Number(this.state.amount) > Number(this.state.sendFromAmount))) {
Store.dispatch(
triggerToaster(
translate('SEND.INSUFFICIENT_FUNDS'),
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
}
if (this.state.sendTo.length > 34 &&
(!this.state.sendFrom || this.state.sendFrom.length < 34)) {
Store.dispatch(
triggerToaster(
translate('SEND.SELECT_SOURCE_ADDRESS'),
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
}
return valid;
}
isTransparentTx() {
if (((this.state.sendFrom && this.state.sendFrom.length === 34) || !this.state.sendFrom) &&
(this.state.sendTo && this.state.sendTo.length === 34)) {
return true;
}
return false;
}
render() {
if (this.props &&
this.props.ActiveCoin &&
(this.props.ActiveCoin.activeSection === 'send' || this.props.activeSection === 'send')) {
return WalletsNativeSendRender.call(this);
}
return null;
}
}
const mapStateToProps = (state, props) => {
let _mappedProps = {
ActiveCoin: {
addresses: state.ActiveCoin.addresses,
coin: state.ActiveCoin.coin,
mode: state.ActiveCoin.mode,
opids: state.ActiveCoin.opids,
balance: state.ActiveCoin.balance,
activeSection: state.ActiveCoin.activeSection,
},
};
if (props &&
props.activeSection &&
props.renderFormOnly) {
_mappedProps.ActiveCoin.activeSection = props.activeSection;
_mappedProps.renderFormOnly = props.renderFormOnly;
}
return _mappedProps;
};
export default connect(mapStateToProps)(WalletsNativeSend);

213
react/src/components/dashboard/walletsNativeSend/walletsNativeSend.render.js

@ -1,213 +0,0 @@
import React from 'react';
import { translate } from '../../../translate/translate';
import QRModal from '../qrModal/qrModal';
export const AddressListRender = function() {
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="Select private address"
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"
onClick={ () => this.updateAddressSelection(null, 'public', null) }>
<a>
<span className="text">{ translate('INDEX.T_FUNDS') }</span>
<span
className="glyphicon glyphicon-ok check-mark pull-right"
style={{ display: this.state.sendFrom === null ? 'inline-block' : 'none' }}></span>
</a>
</li>
{ this.renderAddressByType('public') }
{ this.renderAddressByType('private') }
</ul>
</div>
</div>
);
};
export const _WalletsNativeSendFormRender = function() {
return (
<form
className="extcoin-send-form"
method="post"
autoComplete="off">
{ this.state.renderAddressDropdown &&
<div className="row">
<div className="col-xlg-12 form-group form-material">
<label className="control-label padding-bottom-10">{ translate('INDEX.SEND_FROM') }</label>
{ this.renderAddressList() }
</div>
</div>
}
<div className="row">
<div className="col-xlg-12 form-group form-material">
<label
className="control-label"
htmlFor="kmdWalletSendTo">{ translate('INDEX.SEND_TO') }</label>
<input
type="text"
className="form-control"
name="sendTo"
onChange={ this.updateInput }
value={ this.state.sendTo }
id="kmdWalletSendTo"
placeholder={ translate('SEND.ENTER_T_OR_Z_ADDR') }
autoComplete="off"
required />
</div>
<div className="col-lg-12 form-group form-material">
<label
className="control-label"
htmlFor="kmdWalletAmount">
{ translate('INDEX.AMOUNT') }
</label>
<input
type="text"
className="form-control"
name="amount"
value={ this.state.amount !== 0 ? this.state.amount : '' }
onChange={ this.updateInput }
id="kmdWalletAmount"
placeholder="0.000"
autoComplete="off" />
</div>
<div className={ 'col-lg-6 form-group form-material' + (this.isTransparentTx() ? '' : ' hide') }>
<span className="pointer">
<label className="switch">
<input
type="checkbox"
checked={ this.state.substractFee } />
<div
className="slider"
onClick={ () => this.toggleSubstractFee() }></div>
</label>
<div
className="toggle-label"
onClick={ () => this.toggleSubstractFee() }>
{ translate('DASHBOARD.SUBSTRACT_FEE') }
</div>
</span>
</div>
<div className="col-lg-6 form-group form-material hide">
<label
className="control-label"
htmlFor="kmdWalletFee">
{ translate('INDEX.FEE') }
</label>
<input
type="text"
className="form-control"
name="fee"
onChange={ this.updateInput }
id="kmdWalletFee"
placeholder="0.000"
value={ this.state.fee !== 0 ? this.state.fee : '' }
autoComplete="off" />
</div>
<div className="col-lg-12 hide">
<span>
<strong>{ translate('INDEX.TOTAL') }:</strong>&nbsp;
{ this.state.amount } - { this.state.fee }/kb = { Number(this.state.amount) - Number(this.state.fee) }&nbsp;
{ this.props.ActiveCoin.coin }
</span>
</div>
<div className="col-lg-12">
<button
type="button"
className="btn btn-primary waves-effect waves-light pull-right"
onClick={ this.handleSubmit }
disabled={ !this.state.sendTo || !this.state.amount }>
{ translate('INDEX.SEND') } { this.state.amount } { this.props.ActiveCoin.coin }
</button>
</div>
</div>
</form>
);
}
export const WalletsNativeSendRender = function() {
if (this.props.renderFormOnly) {
return (
<div>{ this.WalletsNativeSendFormRender() }</div>
);
} else {
return (
<div id="kmd_wallet_send">
<div className="col-xlg-12 col-md-12 col-sm-12 col-xs-12">
<div
className="panel"
id="projects">
<div className="panel-heading">
<h3 className="panel-title">
{ translate('INDEX.SEND') } { this.props.ActiveCoin.coin }
</h3>
</div>
<div className="qr-modal-send-block">
<QRModal
mode="scan"
setRecieverFromScan={ this.setRecieverFromScan } />
</div>
<div className="panel-body container-fluid">
{ this.WalletsNativeSendFormRender() }
</div>
</div>
</div>
{ this.renderOPIDListCheck() &&
<div className="col-xs-12">
<div className="row">
<div className="panel nav-tabs-horizontal">
<div>
<div className="col-xlg-12 col-lg-12 col-sm-12 col-xs-12">
<div className="panel">
<header className="panel-heading">
<h3 className="panel-title">
{ translate('INDEX.OPERATIONS_STATUSES') }
</h3>
</header>
<div className="panel-body">
<table
className="table table-hover dataTable table-striped"
width="100%">
<thead>
<tr>
<th>{ translate('INDEX.STATUS') }</th>
<th>ID</th>
<th>{ translate('INDEX.TIME') }</th>
<th>{ translate('INDEX.RESULT') }</th>
</tr>
</thead>
<tbody>
{ this.renderOPIDList() }
</tbody>
<tfoot>
<tr>
<th>{ translate('INDEX.STATUS') }</th>
<th>ID</th>
<th>{ translate('INDEX.TIME') }</th>
<th>{ translate('INDEX.RESULT') }</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
</div>
);
}
};

17
react/src/components/overrides.scss

@ -402,4 +402,21 @@ select{
.bold {
font-weight: bold;
}
.modal {
.modal-content {
.modal-header {
.close {
font-size: 28px;
position: absolute;
z-index: 100;
right: 20px;
opacity: 0.5;
}
.close:hover {
opacity: 1;
}
}
}
}
Loading…
Cancel
Save