diff --git a/react/src/actions/actionCreators.js b/react/src/actions/actionCreators.js index c9fc0f2..dd305e4 100644 --- a/react/src/actions/actionCreators.js +++ b/react/src/actions/actionCreators.js @@ -40,6 +40,16 @@ export const DASHBOARD_ACTIVE_TXINFO_MODAL = 'DASHBOARD_ACTIVE_TXINFO_MODAL'; export const DASHBOARD_ACTIVE_COIN_NATIVE_BALANCE = 'DASHBOARD_ACTIVE_COIN_NATIVE_BALANCE'; export const DASHBOARD_ACTIVE_COIN_NATIVE_TXHISTORY = 'DASHBOARD_ACTIVE_COIN_NATIVE_TXHISTORY'; export const DASHBOARD_ACTIVE_COIN_NATIVE_OPIDS = 'DASHBOARD_ACTIVE_COIN_NATIVE_OPIDS'; +export const DASHBOARD_ACTIVE_COIN_SENDTO = 'DASHBOARD_ACTIVE_COIN_SENDTO'; +export const DASHBOARD_ACTIVE_COIN_GET_CACHE = 'DASHBOARD_ACTIVE_COIN_GET_CACHE'; +export const DASHBOARD_ACTIVE_COIN_MAIN_BASILISK_ADDR = 'DASHBOARD_ACTIVE_COIN_MAIN_BASILISK_ADDR'; + +export function changeMainBasiliskAddress(address) { + return { + type: DASHBOARD_ACTIVE_COIN_MAIN_BASILISK_ADDR, + address, + } +} export function toggleDashboardActiveSection(name) { return { @@ -661,6 +671,31 @@ export function getFullTransactionsList(coin) { } } +export function getBasiliskTransactionsList(coin, address) { + const payload = { + 'userpass': 'tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'), + 'agent': 'dex', + 'method': 'listtransactions', + 'address': address, + 'count': 100, + 'skip': 0, + 'symbol': coin + }; + + return dispatch => { + return fetch('http://127.0.0.1:' + Config.iguanaCorePort, { + method: 'POST', + body: JSON.stringify(payload), + }) + .catch(function(error) { + console.log(error); + dispatch(triggerToaster(true, 'getBasiliskTransactionsList', 'Error', 'error')); + }) + .then(response => response.json()) + .then(json => dispatch(getNativeTxHistoryState(json))) + } +} + export function getPeersList(coin) { const payload = { 'userpass': 'tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'), @@ -862,9 +897,13 @@ function getKMDAddressesNativeState(json) { } } -export function getKMDAddressesNative(coin) { +export function getKMDAddressesNative(coin, mode, currentAddress) { const type = ['public', 'private']; + if (mode !== 'native') { + type.pop(); + } + return dispatch => { Promise.all(type.map((_type, index) => { return new Promise((resolve, reject) => { @@ -903,6 +942,16 @@ export function getKMDAddressesNative(coin) { }; } + if (mode !== 'native' || mode !== 'basilisk') { + payload = { + 'userpass': 'tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'), + 'coin': coin, + 'agent': 'bitcoinrpc', + 'method': 'getaddressesbyaccount', + 'account': '*' + }; + } + fetch('http://127.0.0.1:' + Config.iguanaCorePort, { method: 'POST', body: JSON.stringify(payload), @@ -940,6 +989,29 @@ export function getKMDAddressesNative(coin) { }; } + if (mode === 'full') { + payload = { + 'userpass': 'tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'), + 'coin': coin, + 'method': 'listunspent', + 'params': [ + 1, + 9999999, + ] + }; + } + + // if api cache option is off + if (mode === 'basilisk') { + payload = { + 'userpass': 'tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'), + 'agent': 'dex', + 'method': 'listunspent', + 'address': currentAddress, + 'symbol': coin + }; + } + fetch('http://127.0.0.1:' + Config.iguanaCorePort, { method: 'POST', body: JSON.stringify(payload), @@ -950,36 +1022,49 @@ export function getKMDAddressesNative(coin) { }) .then(response => response.json()) .then(function(json) { - const allAddrArray = json.map(res => res.address).filter((x, i, a) => a.indexOf(x) == i); - for (let a=0; a < allAddrArray.length; a++) { - const filteredArray = json.filter(res => res.address === allAddrArray[a]).map(res => res.amount); - - let isNewAddr = true; - for (let x=0; x < 2 && isNewAddr; x++) { - for (let y=0; y < result[x].length && isNewAddr; y++) { - if (allAddrArray[a] === result[x][y]) { - isNewAddr = false; + if (mode === 'full' || mode === 'basilisk') { + result[0] = result[0].result; + } + + if (mode !== 'basilisk') { + const allAddrArray = json.map(res => res.address).filter((x, i, a) => a.indexOf(x) == i); + for (let a=0; a < allAddrArray.length; a++) { + const filteredArray = json.filter(res => res.address === allAddrArray[a]).map(res => res.amount); + + let isNewAddr = true; + for (let x=0; x < result.length && isNewAddr; x++) { + for (let y=0; y < result[x].length && isNewAddr; y++) { + if (allAddrArray[a] === result[x][y]) { + isNewAddr = false; + } } } - } - if (isNewAddr) { - if (allAddrArray[a].substring(0, 2) === 'zc' || allAddrArray[a].substring(0, 2) === 'zt') { - result[1][result[1].length] = allAddrArray[a]; - } else { - result[0][result[0].length] = allAddrArray[a]; + if (isNewAddr) { + if (allAddrArray[a].substring(0, 2) === 'zc' || allAddrArray[a].substring(0, 2) === 'zt') { + result[1][result[1].length] = allAddrArray[a]; + } else { + result[0][result[0].length] = allAddrArray[a]; + } + console.log('new addr ' + allAddrArray[a] + ' | ' + allAddrArray[a].substring(0, 2)); } - console.log('new addr ' + allAddrArray[a] + ' | ' + allAddrArray[a].substring(0, 2)); } } let newAddressArray = []; - for (let a=0; a < 2; a++) { + for (let a=0; a < result.length; a++) { newAddressArray[a] = []; for (let b=0; b < result[a].length; b++) { - const filteredArray = json.filter(res => res.address === result[a][b]).map(res => res.amount); + var filteredArray; + + if (mode === 'basilisk') { + filteredArray = json.map(res => res.amount); + } else { + filteredArray = json.filter(res => res.address === result[a][b]).map(res => res.amount); + } + let sum = 0; for (let i=0; i < filteredArray.length; i++) { @@ -988,7 +1073,7 @@ export function getKMDAddressesNative(coin) { newAddressArray[a][b] = { address: result[a][b], - amount: sum, + amount: currentAddress === result[a][b] ? sum : 'N/A', }; } } @@ -1002,6 +1087,30 @@ export function getKMDAddressesNative(coin) { } } +function getShepherdCacheState(json) { + return { + type: DASHBOARD_ACTIVE_COIN_GET_CACHE, + cache: json && json.result && json.result.basilisk ? json.result.basilisk : null, + } +} + +export function getShepherdCache(pubkey) { + return dispatch => { + return fetch('http://127.0.0.1:' + Config.agamaPort + '/shepherd/cache?pubkey=' + pubkey, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .catch(function(error) { + console.log(error); + dispatch(triggerToaster(true, 'getShepherdCache', 'Error', 'error')); + }) + .then(response => response.json()) + .then(json => dispatch(getShepherdCacheState(json))) + } +} + function getDebugLogState(json) { const _data = json.result.replace('\n', '\r\n'); @@ -1281,7 +1390,9 @@ export function getNativeTxHistoryState(json) { json = null; } else if (json && json.result) { json = json.result; - } + } else if (!json.length) { + json = 'no data'; + } return { type: DASHBOARD_ACTIVE_COIN_NATIVE_TXHISTORY, @@ -1292,6 +1403,7 @@ export function getNativeTxHistoryState(json) { function handleGetNewKMDAddresses(pubpriv, coin, dispatch) { dispatch(triggerToaster(true, translate('KMD_NATIVE.NEW_ADDR_GENERATED'), translate('TOASTR.WALLET_NOTIFICATION'), 'success')); dispatch(getKMDAddressesNative(coin)); + return {}; } @@ -1469,6 +1581,88 @@ export function getKMDOPID(opid, coin) { } } +function sendToAddressState(json, dispatch) { + if (json && json.error) { + dispatch(triggerToaster(true, json.error, 'Error', 'error')); + + return { + type: DASHBOARD_ACTIVE_COIN_SENDTO, + lastSendToResponse: json, + } + } else if (json && json.result && json.complete) { + dispatch(triggerToaster(true, translate('TOASTR.TX_SENT_ALT'), translate('TOASTR.WALLET_NOTIFICATION'), 'success')); + + return { + type: DASHBOARD_ACTIVE_COIN_SENDTO, + lastSendToResponse: json, + } + } +} + +export function sendToAddress(coin, _payload) { + const payload = { + 'userpass': 'tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'), + 'coin': coin, + 'method': 'sendtoaddress', + 'params': [ + _payload.sendTo, + _payload.amount, + 'EasyDEX', + 'EasyDEXTransaction' + ] + }; + + return dispatch => { + return fetch('http://127.0.0.1:' + Config.iguanaCorePort, { + method: 'POST', + body: JSON.stringify(payload), + }) + .catch(function(error) { + console.log(error); + dispatch(triggerToaster(true, 'sendToAddress', 'Error', 'error')); + }) + .then(response => response.json()) + .then(json => dispatch(sendToAddressState(json, dispatch))) + } +} + +function checkAddressBasiliskHandle(json) { + if (json && json.error) { + return dispatch => { + dispatch(triggerToaster(true, json.error, translate('TOASTR.WALLET_NOTIFICATION'), 'error')); + } + } + + if (json && json.coin && json.randipbits) { + return dispatch => { + dispatch(triggerToaster(true, 'Address already registered', translate('TOASTR.WALLET_NOTIFICATION'), 'warning')); + } + } +} + +export function checkAddressBasilisk(coin, address) { + const payload = { + 'userpass': 'tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'), + 'agent': 'dex', + 'method': 'checkaddress', + 'address': address, + 'symbol': coin + }; + + return dispatch => { + return fetch('http://127.0.0.1:' + Config.iguanaCorePort, { + method: 'POST', + body: JSON.stringify(payload), + }) + .catch(function(error) { + console.log(error); + dispatch(triggerToaster(true, 'checkAddressBasilisk', 'Error', 'error')); + }) + .then(response => response.json()) + .then(json => dispatch(checkAddressBasiliskHandle(json))) + } +} + /*function Shepherd_SendPendValue() { Shepherd_SysInfo().then(function(result){ var ram_data = formatBytes(result.totalmem_bytes) diff --git a/react/src/components/dashboard/coinTileItem.js b/react/src/components/dashboard/coinTileItem.js index bdc9c06..9e551c7 100644 --- a/react/src/components/dashboard/coinTileItem.js +++ b/react/src/components/dashboard/coinTileItem.js @@ -13,7 +13,9 @@ import { getNativeTxHistory, getKMDAddressesNative, getKMDOPID, - getFullTransactionsList + getFullTransactionsList, + getBasiliskTransactionsList, + getShepherdCache } from '../../actions/actionCreators'; import Store from '../../store'; @@ -24,6 +26,11 @@ class CoinTileItem extends React.Component { }; } + // TODO: 1) cache native/full node data to file + // 2) limit amount of req per update e.g. list of addresses don't change too often + // 3) limit req in basilisk as much as possible incl. activehandle + // 4) add pending requests store + dashboardChangeActiveCoin(coin, mode) { if (coin !== this.props.ActiveCoin.coin) { Store.dispatch(stopInterval('sync', this.props.Interval.interval)); @@ -33,7 +40,7 @@ class CoinTileItem extends React.Component { var _iguanaActiveHandle = setInterval(function() { Store.dispatch(getSyncInfo(coin)); Store.dispatch(iguanaEdexBalance(coin, mode)); - Store.dispatch(getAddressesByAccount(coin)); + Store.dispatch(getKMDAddressesNative(coin, mode)); //getAddressesByAccount(coin)); Store.dispatch(getFullTransactionsList(coin)); }, 3000); Store.dispatch(startInterval('sync', _iguanaActiveHandle)); @@ -50,7 +57,17 @@ class CoinTileItem extends React.Component { Store.dispatch(startInterval('sync', _iguanaActiveHandle)); } if (mode === 'basilisk') { - Store.dispatch(getAddressesByAccount(coin)); + var _iguanaActiveHandle = setInterval(function() { + const useAddress = this.props.ActiveCoin.mainBasiliskAddress ? this.props.ActiveCoin.mainBasiliskAddress : this.props.Dashboard.activeHandle[coin]; + + if (this.props && this.props.Dashboard && this.props.Dashboard.activeHandle && this.props.Dashboard.activeHandle[coin]) { + Store.dispatch(getBasiliskTransactionsList(coin, useAddress)); + Store.dispatch(getKMDAddressesNative(coin, mode, useAddress)); + Store.dispatch(getShepherdCache(this.props.Dashboard.activeHandle.pubkey)); + Store.dispatch(iguanaEdexBalance(coin, mode)); + } + }.bind(this), 3000); + Store.dispatch(startInterval('sync', _iguanaActiveHandle)); // basilisk } } diff --git a/react/src/components/dashboard/dashboard.js b/react/src/components/dashboard/dashboard.js index fbbbe17..2564891 100644 --- a/react/src/components/dashboard/dashboard.js +++ b/react/src/components/dashboard/dashboard.js @@ -17,6 +17,7 @@ import WalletsBasiliskRefresh from './walletsBasiliskRefresh'; import WalletsBasiliskConnection from './walletsBasiliskConnection'; import WalletsNative from './walletsNative'; import WalletsNativeTxInfo from './walletsNativeTxInfo'; +import WalletsTxInfo from './walletsTxInfo'; class Dashboard extends React.Component { constructor(props) { @@ -37,11 +38,12 @@ class Dashboard extends React.Component { - + + diff --git a/react/src/components/dashboard/receiveCoin.js b/react/src/components/dashboard/receiveCoin.js index 6c23bf0..939dcdf 100644 --- a/react/src/components/dashboard/receiveCoin.js +++ b/react/src/components/dashboard/receiveCoin.js @@ -1,19 +1,59 @@ import React from 'react'; import { translate } from '../../translate/translate'; +import { checkAddressBasilisk, importAddressBasilisk } from '../../actions/actionCreators'; +import Store from '../../store'; + +// TODO: implement sorting +// TODO: add import address ui in basilisk +// TODO: fallback to localstorage/stores data in case iguana is taking too long to respond class ReceiveCoin extends React.Component { + constructor(props) { + super(props); + } + + checkAddressBasilisk(address) { + Store.dispatch(checkAddressBasilisk(this.props.coin, address)); + } + + /*importAddressBasilisk(address) { + Store.dispatch(importAddressBasilisk(this.props.coin, address)); + } + this.importAddressBasilisk(address)}> + + */ + + renderAddressActions(address) { + if (this.props.mode === 'basilisk') { + return ( + + + {translate('IAPI.PUBLIC_SM')} + + this.checkAddressBasilisk(address)}> + + + + ); + } else { + return ( + + + {translate('IAPI.PUBLIC_SM')} + + + ); + } + } + renderAddressList() { - if (this.props.addresses && this.props.addresses.length) { - return this.props.addresses.map((address) => - - - - {translate('IAPI.PUBLIC_SM')} - - - {address} - - + if (this.props.addresses && this.props.addresses['public'] && this.props.addresses['public'].length) { + return this.props.addresses['public'].map((address) => + + {this.renderAddressActions(address.address)} + {address.address} + {address.amount} + {address.interest ? address.interest : 'N/A'} ); } else { diff --git a/react/src/components/dashboard/sendCoin.js b/react/src/components/dashboard/sendCoin.js index 06e69ac..2e2202c 100644 --- a/react/src/components/dashboard/sendCoin.js +++ b/react/src/components/dashboard/sendCoin.js @@ -1,7 +1,7 @@ import React from 'react'; import { translate } from '../../translate/translate'; -//import { } from '../../actions/actionCreators'; -//import Store from '../../store'; +import { sendToAddress } from '../../actions/actionCreators'; +import Store from '../../store'; // TODO: implement logic @@ -10,17 +10,111 @@ class SendCoin extends React.Component { super(props); this.state = { currentStep: 0, + sendFrom: this.props.Dashboard && this.props.Dashboard.activeHandle ? this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin] : null, + sendFromAmount: 0, + sendTo: null, + amount: 0, + fee: 0.0001, + sendSig: false, }; + this.updateInput = this.updateInput.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + this.toggleSendSig = this.toggleSendSig.bind(this); } changeSendCoinStep(step) { this.setState(Object.assign({}, this.state, { currentStep: step, })); + + if (step === 2) { + Store.dispatch(sendToAddress(this.props.ActiveCoin.coin, this.state)); + } + } + + toggleSendSig() { + this.setState(Object.assign({}, this.state, { + sendSig: !this.state.sendSig, + })); + } + + updateInput(e) { + this.setState({ + [e.target.name]: e.target.value, + }); + console.log(this.state); + } + + handleSubmit() { + Store.dispatch(sendNativeTx(this.props.ActiveCoin.coin, this.state)); + setTimeout(function() { + Store.dispatch(getKMDOPID(null, this.props.ActiveCoin.coin)); + }, 1000); + } + + renderSignedTx(signedtx) { + const substrBlocks = 10; + const substrLength = this.props.ActiveCoin.lastSendToResponse['signedtx'].length / substrBlocks; + let out = []; + + for (let i = 0; i < substrBlocks; i++) { + out.push( +
{this.props.ActiveCoin.lastSendToResponse['signedtx'].substring(i * substrLength, substrLength * i + substrLength)}
+ ); + } + + return out.length ? out : null; + } + + renderKey(key) { + if (key === 'signedtx') { + return this.renderSignedTx(); + } else if (key === 'complete') { + if (this.props.ActiveCoin.lastSendToResponse[key] === true) { + return ( + true + ); + } else { + return ( + false + ); + } + } else if (key === 'result') { + return ( + {this.props.ActiveCoin.lastSendToResponse[key]} + ); + } else if (key === 'error') { + return ( + {this.props.ActiveCoin.lastSendToResponse[key]} + ); + } else if (key === 'sendrawtransaction') { + if (this.props.ActiveCoin.lastSendToResponse[key] === 'success') { + return ( + true + ); + } else { + return ( + false + ); + } + } + } + + renderSendCoinResponse() { + if (this.props.ActiveCoin.lastSendToResponse) { + return Object.keys(this.props.ActiveCoin.lastSendToResponse).map((key, index) => + + {key} + {this.renderKey(key)} + + ); + } else { + return null; + } } render() { - if (this.props && this.props.send && this.props.mode !== 'native') { + if (this.props.ActiveCoin && this.props.ActiveCoin.send && this.props.ActiveCoin.mode !== 'native') { return (
@@ -51,49 +145,44 @@ class SendCoin extends React.Component {

- {translate('INDEX.SEND')} + {translate('INDEX.SEND')} {this.props.ActiveCoin.coin}

-
-
+
- +
- +
- +
- +
- {translate('INDEX.TOTAL')} ({translate('INDEX.AMOUNT_SM')} - txfee): 0.000 + {translate('INDEX.TOTAL')} ({translate('INDEX.AMOUNT_SM')} - txfee): {Number(this.state.amount) - Number(this.state.fee)} {this.props.ActiveCoin.coin}
- +
- +
@@ -109,13 +198,13 @@ class SendCoin extends React.Component {
{translate('INDEX.TO')}
-
[coin-address-goes-here]
+
{this.state.sendTo}
- 0.00000000 [COIN] + {this.state.amount} {this.props.ActiveCoin.coin}
{translate('INDEX.TX_FEE_REQ')}
- 0.00000000 [COIN] + {this.state.fee} {this.props.ActiveCoin.coin}

@@ -124,9 +213,9 @@ class SendCoin extends React.Component {
{translate('INDEX.FROM')}
-
[coin-address-goes-here]
+
{this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin]}
- -0.00000000 [COIN] + {Number(this.state.amount) - Number(this.state.fee)} {this.props.ActiveCoin.coin}
@@ -151,6 +240,7 @@ class SendCoin extends React.Component { + {this.renderSendCoinResponse()}
diff --git a/react/src/components/dashboard/walletsData.js b/react/src/components/dashboard/walletsData.js index 92a1e0b..6e292f0 100644 --- a/react/src/components/dashboard/walletsData.js +++ b/react/src/components/dashboard/walletsData.js @@ -1,7 +1,14 @@ import React from 'react'; import { translate } from '../../translate/translate'; import { secondsToString } from '../../util/time'; -import { basiliskRefresh, basiliskConnection, getDexNotaries } from '../../actions/actionCreators'; +import { + basiliskRefresh, + basiliskConnection, + getDexNotaries, + toggleDashboardTxInfoModal, + getBasiliskTransactionsList, + changeMainBasiliskAddress +} from '../../actions/actionCreators'; import Store from '../../store'; class WalletsData extends React.Component { @@ -12,12 +19,16 @@ class WalletsData extends React.Component { itemsPerPage: 10, activePage: 1, itemsList: null, + currentAddress: null, + addressSelectorOpen: false, }; this.updateInput = this.updateInput.bind(this); this.toggleBasiliskActionsMenu = this.toggleBasiliskActionsMenu.bind(this); this.basiliskRefreshAction = this.basiliskRefreshAction.bind(this); this.basiliskConnectionAction = this.basiliskConnectionAction.bind(this); this.getDexNotariesAction = this.getDexNotariesAction.bind(this); + this.openDropMenu = this.openDropMenu.bind(this); + this.refreshTxList = this.refreshTxList.bind(this); } toggleBasiliskActionsMenu() { @@ -53,6 +64,10 @@ class WalletsData extends React.Component { }); } + toggleTxInfoModal(display, txIndex) { + Store.dispatch(toggleDashboardTxInfoModal(display, txIndex)); + } + componentWillReceiveProps(props) { if (this.props.ActiveCoin.txhistory && this.props.ActiveCoin.txhistory.length) { if (!this.state.itemsList || (this.state.itemsList && !this.state.itemsList.length) || (props.ActiveCoin.txhistory !== this.props.ActiveCoin.txhistory)) { @@ -64,6 +79,12 @@ class WalletsData extends React.Component { })); } } + + if (this.props.ActiveCoin.txhistory && this.props.ActiveCoin.txhistory === 'no data') { + this.setState(Object.assign({}, this.state, { + itemsList: 'no data', + })); + } } updateCurrentPage(page) { @@ -97,8 +118,8 @@ class WalletsData extends React.Component {