diff --git a/react/src/actions/actionCreators.js b/react/src/actions/actionCreators.js index 367d8ce..af58301 100644 --- a/react/src/actions/actionCreators.js +++ b/react/src/actions/actionCreators.js @@ -52,9 +52,17 @@ export const SYNC_ONLY_DATA = 'SYNC_ONLY_DATA'; export const LOAD_APP_CONFIG = 'LOAD_APP_CONFIG'; export const SAVE_APP_CONFIG = 'SAVE_APP_CONFIG'; export const SERVICE_ERROR = 'SERVICE_ERROR'; +export const DASHBOARD_ACTIVE_ADDRESS = 'DASHBOARD_ACTIVE_ADDRESS'; var iguanaForks = {}; // forks in mem array +export function changeActiveAddress(address) { + return { + type: DASHBOARD_ACTIVE_ADDRESS, + address, + } +} + function updateErrosStack(method) { return { apiMethod: method, @@ -795,7 +803,7 @@ export function getBasiliskTransactionsList(coin, address) { }) .then(response => response.json()) .then(function(json) { - if (json.result && json.result.indexOf('no file with handle') > -1) { + if (json.result && !json.result.basilisk && json.result.indexOf('no file with handle') > -1) { console.log('new cache'); } @@ -1091,6 +1099,7 @@ export function getKMDAddressesNative(coin, mode, currentAddress) { .then(response => response.json()) .then(function(json) { json = json.result.basilisk; + if (json[coin].addresses) { resolve({ 'result': json[coin].addresses }); } @@ -1254,8 +1263,8 @@ export function getKMDAddressesNative(coin, mode, currentAddress) { json = json.result.basilisk; // if listunspent is not in cache file retrieve new copy // otherwise read from cache data - if (json[coin][currentAddress].listunspent) { - calcBalance(result, json[coin][currentAddress].listunspent.data, dispatch, mode); + if (json[coin][currentAddress].refresh) { + calcBalance(result, json[coin][currentAddress].refresh.data, dispatch, mode); } else { fetch('http://127.0.0.1:' + (Config.useBasiliskInstance && mode === 'basilisk' ? Config.basiliskPort : Config.iguanaCorePort), { method: 'POST', @@ -1267,7 +1276,7 @@ export function getKMDAddressesNative(coin, mode, currentAddress) { }) .then(response => response.json()) .then(function(json) { - updatedCache.basilisk[coin][currentAddress].listunspent = { + updatedCache.basilisk[coin][currentAddress].refresh = { 'data': json, 'status': 'done', 'timestamp': Date.now(), @@ -1296,6 +1305,9 @@ export function getKMDAddressesNative(coin, mode, currentAddress) { } export function shepherdGroomPost(_filename, _payload) { + console.log('shepherdGroomPost to file ', _filename); + console.log('shepherdGroomPost payload ', _payload); + return dispatch => { return fetch('http://127.0.0.1:' + Config.agamaPort + '/shepherd/groom/', { method: 'POST', @@ -1316,13 +1328,37 @@ export function shepherdGroomPost(_filename, _payload) { } } +export function shepherdGroomPostPromise(_filename, _payload) { + console.log('shepherdGroomPostPromise to file ', _filename); + console.log('shepherdGroomPostPromise payload ', _payload); + + return new Promise((resolve, reject) => { + fetch('http://127.0.0.1:' + Config.agamaPort + '/shepherd/groom/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + 'filename': _filename, + 'payload': JSON.stringify(_payload), + }), + }) + .catch(function(error) { + console.log(error); + dispatch(triggerToaster(true, 'shepherdGroomPostPromise', 'Error', 'error')); + }) + .then(response => response.json()) + .then(json => resolve(json)) + }) +} + export function fetchUtxoCache(_payload) { const _userpass = '?userpass=tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'), _pubkey = '&pubkey=' + _payload.pubkey, _route = _payload.allcoins ? 'cache-all' : 'cache-one', _coin = '&coin=' + _payload.coin, _calls = '&calls=' + _payload.calls, - _address = '&address=' + _payload.address, + _address = _payload.address ? ('&address=' + _payload.address) : '', _iguanaInstancePort = Config.useBasiliskInstance ? '&port=' + Config.basiliskPort : ''; return dispatch => { @@ -1343,7 +1379,7 @@ export function fetchUtxoCache(_payload) { function getShepherdCacheState(json, pubkey, coin) { if (json.result && json.error && json.result.indexOf('no file with handle') > -1) { - console.log('new cache'); + console.log('request new cache', true); return dispatch => { dispatch(fetchNewCacheData({ 'pubkey': pubkey, @@ -1585,7 +1621,6 @@ export function getKMDBalanceTotal(coin) { 'asset': coin, 'function': 'z_gettotalbalance', 'hex': '3000', - 'immediate': 60000, 'timeout': 60000 }; } else { @@ -1595,7 +1630,6 @@ export function getKMDBalanceTotal(coin) { 'method': 'passthru', 'function': 'z_gettotalbalance', 'hex': '3000', - 'immediate': 60000, 'timeout': 60000 }; } @@ -1802,7 +1836,13 @@ export function sendNativeTx(coin, _payload) { dispatch(triggerToaster(true, 'sendNativeTx', 'Error', 'error')); }) .then(response => response.json()) - .then(json => dispatch(triggerToaster(true, translate('TOASTR.TX_SENT_ALT'), translate('TOASTR.WALLET_NOTIFICATION'), 'success'))) + .then(function(json) { + if (json.error && json.error.toString().indexOf('code:') > -1) { + dispatch(triggerToaster(true, 'Send failed', translate('TOASTR.WALLET_NOTIFICATION'), 'error')); + } else { + dispatch(triggerToaster(true, translate('TOASTR.TX_SENT_ALT'), translate('TOASTR.WALLET_NOTIFICATION'), 'success')); + } + }) .catch(function(ex) { dispatch(triggerToaster(true, translate('TOASTR.TX_SENT_ALT'), translate('TOASTR.WALLET_NOTIFICATION'), 'success')); console.log('parsing failed', ex); @@ -2138,11 +2178,11 @@ export function deleteCacheFile(_payload) { } } -export function getCacheFile() { - const _pubkey = JSON.parse(sessionStorage.getItem('IguanaActiveAccount')).pubkey; +export function getCacheFile(pubkey) { + const _pubkey = pubkey || JSON.parse(sessionStorage.getItem('IguanaActiveAccount')).pubkey; return new Promise((resolve, reject) => { - fetch('http://127.0.0.1:' + Config.agamaPort + '/shepherd/groom?pubkey=' + _pubkey, { + fetch('http://127.0.0.1:' + Config.agamaPort + '/shepherd/groom?filename=' + _pubkey, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -2164,10 +2204,11 @@ export function fetchNewCacheData(_payload) { _route = _payload.allcoins ? 'cache-all' : 'cache-one', _coin = '&coin=' + _payload.coin, _calls = '&calls=' + _payload.calls, + _address = _payload.address ? ('&address=' + _payload.address) : '', _iguanaInstancePort = Config.useBasiliskInstance ? '&port=' + Config.basiliskPort : ''; return dispatch => { - return fetch('http://127.0.0.1:' + Config.agamaPort + '/shepherd/' + _route + _userpass + _pubkey + _coin + _calls + _iguanaInstancePort, { + return fetch('http://127.0.0.1:' + Config.agamaPort + '/shepherd/' + _route + _userpass + _pubkey + _coin + _calls + _address + _iguanaInstancePort, { method: 'GET', headers: { 'Content-Type': 'application/json', diff --git a/react/src/components/dashboard/appSettings.js b/react/src/components/dashboard/appSettings.js new file mode 100644 index 0000000..2efba73 --- /dev/null +++ b/react/src/components/dashboard/appSettings.js @@ -0,0 +1,39 @@ +import React from 'react'; +import { translate } from '../../translate/translate'; +import { + +} from '../../actions/actionCreators'; +import Store from '../../store'; + +class AppSettings extends React.Component { + constructor(props) { + super(props); + //this.closeSyncOnlyModal = this.closeSyncOnlyModal.bind(this); + } + + render() { + /*if (this.props && this.props.SyncOnly.display) { + return ( +
+
+
+
+
+ {this.renderForksList()} +
+
+ +
+
+
+
+
+
+ ); + } else { + return null; + }*/ + } +} + +export default AppSettings; \ No newline at end of file diff --git a/react/src/components/dashboard/coinTileItem.js b/react/src/components/dashboard/coinTileItem.js index 6166468..4945df5 100644 --- a/react/src/components/dashboard/coinTileItem.js +++ b/react/src/components/dashboard/coinTileItem.js @@ -16,7 +16,8 @@ import { getFullTransactionsList, getBasiliskTransactionsList, getShepherdCache, - fetchNewCacheData + fetchNewCacheData, + changeActiveAddress } from '../../actions/actionCreators'; import Store from '../../store'; @@ -32,6 +33,9 @@ class CoinTileItem extends React.Component { // 3) limit req in basilisk as much as possible incl. activehandle // 4) add pending requests store + // TODO: update all addresses once in 20 min, current address every 10 min + // always fetch main addr data and current selected address + dispatchCoinActions(coin, mode) { if (mode === 'native') { Store.dispatch(iguanaActiveHandle(true)); @@ -51,9 +55,9 @@ class CoinTileItem extends React.Component { if (mode === 'basilisk') { const useAddress = this.props.ActiveCoin.mainBasiliskAddress ? this.props.ActiveCoin.mainBasiliskAddress : this.props.Dashboard.activeHandle[coin]; Store.dispatch(iguanaActiveHandle(true)); + Store.dispatch(getShepherdCache(JSON.parse(sessionStorage.getItem('IguanaActiveAccount')).pubkey, coin)); if (this.props && this.props.Dashboard && this.props.Dashboard.activeHandle && this.props.Dashboard.activeHandle[coin]) { - Store.dispatch(getShepherdCache(this.props.Dashboard.activeHandle.pubkey, coin)); Store.dispatch(getBasiliskTransactionsList(coin, useAddress)); Store.dispatch(getKMDAddressesNative(coin, mode, useAddress)); //Store.dispatch(iguanaEdexBalance(coin, mode)); @@ -83,21 +87,35 @@ class CoinTileItem extends React.Component { Store.dispatch(startInterval('sync', _iguanaActiveHandle)); } if (mode === 'basilisk') { - var _iguanaActiveHandle = setInterval(function() { - this.dispatchCoinActions(coin, mode); - }.bind(this), 3000); + const _basiliskMainAddress = this.props.Dashboard.activeHandle[coin] || JSON.parse(sessionStorage.getItem('IguanaActiveAccount'))[coin]; + Store.dispatch(changeActiveAddress(_basiliskMainAddress)); - var _basiliskCache = setInterval(function() { + if (_basiliskMainAddress) { Store.dispatch(fetchNewCacheData({ 'pubkey': this.props.Dashboard.activeHandle.pubkey, 'allcoins': false, - 'coin': this.props.ActiveCoin.coin, + 'coin': coin, 'calls': 'listtransactions:getbalance', + 'address': _basiliskMainAddress, })); - }.bind(this), 60000); - Store.dispatch(startInterval('sync', _iguanaActiveHandle)); - Store.dispatch(startInterval('basilisk', _basiliskCache)); - // basilisk + + var _iguanaActiveHandle = setInterval(function() { + this.dispatchCoinActions(coin, mode); + }.bind(this), 3000); + + var _basiliskCache = setInterval(function() { + Store.dispatch(fetchNewCacheData({ + 'pubkey': this.props.Dashboard.activeHandle.pubkey, + 'allcoins': false, + 'coin': this.props.ActiveCoin.coin, + 'calls': 'listtransactions:getbalance', + 'address': _basiliskMainAddress, + })); + }.bind(this), 240000); + Store.dispatch(startInterval('sync', _iguanaActiveHandle)); + Store.dispatch(startInterval('basilisk', _basiliskCache)); + // basilisk + } } } } diff --git a/react/src/components/dashboard/sendCoin.js b/react/src/components/dashboard/sendCoin.js index fbe2bac..7cbbbc0 100644 --- a/react/src/components/dashboard/sendCoin.js +++ b/react/src/components/dashboard/sendCoin.js @@ -21,7 +21,8 @@ import { fetchUtxoCache, basiliskRefresh, edexGetTransaction, - getCacheFile + getCacheFile, + shepherdGroomPostPromise } from '../../actions/actionCreators'; import Store from '../../store'; @@ -32,7 +33,6 @@ const socket = io.connect('http://127.0.0.1:' + Config.agamaPort); class SendCoin extends React.Component { constructor(props) { super(props); - this.state = { currentStep: 0, sendFrom: this.props.Dashboard && this.props.Dashboard.activeHandle ? this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin] : null, @@ -46,26 +46,7 @@ class SendCoin extends React.Component { addressSelectorOpen: false, currentStackLength: 0, totalStackLength: 0, - vin: [ - { - "txid": "8f5d3bc7ff1d8abdedefa4ed71c2a085a5fec62b8491c08e4a6ed53925df0235", - "vout": 0, - "scriptSig": { - "asm": "3045022100ea6442b209ab48b7109f13399fa12f85915ee2204a21698b85454987d9303fe1022025e4630c684af9b982b243420d69e094d0fc708aaccf11b83fd9320fe4dd45f701 02bee71575d87c7285eda952358175af10879081a0cc6b94623aac6cb2c6a51eae", - "hex": "483045022100ea6442b209ab48b7109f13399fa12f85915ee2204a21698b85454987d9303fe1022025e4630c684af9b982b243420d69e094d0fc708aaccf11b83fd9320fe4dd45f7012102bee71575d87c7285eda952358175af10879081a0cc6b94623aac6cb2c6a51eae" - }, - "sequence": 4294967295 - }, - { - "txid": "4cfb597119d4239e8fa75486d1ba4c62cef615a52568aca1bc9be3b457c12eac", - "vout": 0, - "scriptSig": { - "asm": "3044022001c1481b5fb142a1f38f8387c8d0ba6e4d6c6a96a8c6765ce805ce841cb2a58b02206caec099e209a469b78c22c674be77d92698fff94b4521c0d5e462ac5b19627c01 02bee71575d87c7285eda952358175af10879081a0cc6b94623aac6cb2c6a51eae", - "hex": "473044022001c1481b5fb142a1f38f8387c8d0ba6e4d6c6a96a8c6765ce805ce841cb2a58b02206caec099e209a469b78c22c674be77d92698fff94b4521c0d5e462ac5b19627c012102bee71575d87c7285eda952358175af10879081a0cc6b94623aac6cb2c6a51eae" - }, - "sequence": 4294967295 - } - ] + utxoMethodInProgress: false, }; this.updateInput = this.updateInput.bind(this); this.handleBasiliskSend = this.handleBasiliskSend.bind(this); @@ -73,11 +54,18 @@ class SendCoin extends React.Component { this.toggleSendSig = this.toggleSendSig.bind(this); this.getOAdress = this.getOAdress.bind(this); this.toggleSendAPIType = this.toggleSendAPIType.bind(this); - this.renderUTXOCacheInfo = this.renderUTXOCacheInfo.bind(this); this._fetchNewUTXOData = this._fetchNewUTXOData.bind(this); socket.on('messages', msg => this.updateSocketsData(msg)); } + componentWillReceiveProps(props) { + if (!this.state.sendFrom && this.props.ActiveCoin.activeAddress) { + this.setState(Object.assign({}, this.state, { + sendFrom: this.props.ActiveCoin.activeAddress, + })); + } + } + updateSocketsData(data) { console.log('sockets', data); if (data && data.message && data.message.shepherd.iguanaAPI && @@ -107,40 +95,55 @@ class SendCoin extends React.Component { 'calls': 'refresh', 'address': this.state.sendFrom, })); - console.log('_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.coin][this.state.sendFrom] && - this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh) { - const refreshCacheData = this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh; - const timestamp = checkTimestamp(refreshCacheData.timestamp); - const isReadyToUpdate = timestamp > 600 ? true : false; + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom]) { + let refreshCacheData, + timestamp, + isReadyToUpdate, + waitUntilCallIsFinished = this.state.currentStackLength > 1 ? true : false; + + if (this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh || + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].listunspent) { + refreshCacheData = this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh || this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].listunspent; + timestamp = checkTimestamp(refreshCacheData.timestamp); + isReadyToUpdate = timestamp > 600 ? true : false; + } else { + isReadyToUpdate = true; + } + + if (this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh.data && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh.data.error && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.state.sendFrom].refresh.data.error === 'request failed') { + timestamp = null; + } return (

- Total UTXO available: {refreshCacheData.data.length}
- Last updated @ {secondsToString(refreshCacheData.timestamp, true)} | {secondsElapsedToString(timestamp)} ago
+ Total UTXO available: {refreshCacheData ? refreshCacheData.data && refreshCacheData.data.length : 'N/A. Press update button.'}
+
+ Last updated @ {secondsToString(refreshCacheData ? refreshCacheData.timestamp : 0, true)} | {secondsElapsedToString(timestamp || 0)} ago
+
Next update available in {secondsElapsedToString(600 - timestamp)}s
Processing requests: {this.state.currentStackLength} / {this.state.totalStackLength}
-
+
+ Split funds +
+
); } else { @@ -241,12 +244,6 @@ class SendCoin extends React.Component { sendFromAmount: amount ? amount : this.props.ActiveCoin.addresses[type][address].amount, addressSelectorOpen: !this.state.addressSelectorOpen, })); - - this.renderCachedUTXOInfo(); - } - - renderCachedUTXOInfo() { - } changeSendCoinStep(step) { @@ -254,6 +251,7 @@ class SendCoin extends React.Component { this.setState(Object.assign({}, this.state, { currentStep: step, + utxoMethodInProgress: !this.state.sendApiType && this.props.ActiveCoin.mode === 'basilisk' ? true : false, })); if (step === 2) { @@ -274,6 +272,7 @@ class SendCoin extends React.Component { toggleSendAPIType() { this.setState(Object.assign({}, this.state, { sendApiType: !this.state.sendApiType, + sendFrom: this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin], })); } @@ -286,7 +285,9 @@ class SendCoin extends React.Component { 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 utxoSet = (refreshData && refreshData.data) || (listunspentData && listunspentData.data); + const _pubkey = this.props.Dashboard.activeHandle.pubkey; + const forceUpdateCache = this._fetchNewUTXOData; const sendData = { 'coin': this.props.ActiveCoin.coin, 'sendfrom': this.state.sendFrom, @@ -305,7 +306,6 @@ class SendCoin extends React.Component { Store.dispatch(triggerToaster(true, translate('TOASTR.SIGNED_TX_GENERATED') + '.', translate('TOASTR.WALLET_NOTIFICATION'), 'success')); if (sendData.sendsig === 1) { - // Store.dispatch(triggerToaster(true, translate('TOASTR.SENDING_TX') + '.', translate('TOASTR.WALLET_NOTIFICATION'), 'success')); const dexrawtxData = { 'signedtx': json.signedtx, 'coin': sendData.coin @@ -316,6 +316,10 @@ class SendCoin extends React.Component { if (dexRawTxJSON.indexOf('"error":{"code"') > -1) { Store.dispatch(triggerToaster(true, '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(true, translate('TOASTR.SIGNED_TX_SENT'), translate('TOASTR.WALLET_NOTIFICATION'), 'success')); Store.dispatch(sendToAddressStateAlt(json)); @@ -326,7 +330,7 @@ class SendCoin extends React.Component { edexGetTransaction({ 'coin': sendData.coin, - 'txid': dexRawTxJSON.txid + 'txid': dexRawTxJSON.txid ? dexRawTxJSON.txid : dexRawTxJSON }) .then(function(json) { console.log('gettx', json); @@ -336,6 +340,8 @@ class SendCoin extends React.Component { } let processRefreshUTXOs = function(vinData) { + console.log('vin', vinData); + return new Promise(function(resolve, reject) { let edexGetTxIDListRes = edexGetTxIDList(vinData); resolve(edexGetTxIDListRes); @@ -347,11 +353,11 @@ class SendCoin extends React.Component { console.log(txidListToRemove); console.log(sendData); - getCacheFile() + getCacheFile(_pubkey) .then(function(result) { - console.log(result); - let saveThisData = edexRemoveTXID(result, txidListToRemove); - console.log(saveThisData); + console.log('got cache file', result); + let saveThisData = edexRemoveTXID(result.result, txidListToRemove); + console.log('saveThisData', saveThisData); resolve(saveThisData); }); }); @@ -361,16 +367,26 @@ class SendCoin extends React.Component { return new Promise(function(resolve, reject) { console.log('saveNewCacheData', saveThisData); - shepherdGroomPost(saveThisData) + shepherdGroomPostPromise(_pubkey, saveThisData) .then(function(result) { console.log('saveNewCacheData', saveThisData); console.log(result); resolve(result); - }); - }); - } + forceUpdateCache(); + Store.dispatch(triggerToaster(true, 'Local UTXO data is updated. Ready to send new transaction.', translate('TOASTR.WALLET_NOTIFICATION'), 'info')); + + this.setState(Object.assign({}, this.state, { + utxoMethodInProgress: false, + })); + }.bind(this)); + }.bind(this)); + }.bind(this); + + Store.dispatch(triggerToaster(true, 'Awaiting transaction data response...', translate('TOASTR.WALLET_NOTIFICATION'), 'info')); + + function waterfallUTXOProcess() { + Store.dispatch(triggerToaster(true, 'Processing UTXO...', translate('TOASTR.WALLET_NOTIFICATION'), 'info')); - setTimeout(function() { getTxidData() .then(function(gettxdata) { return processRefreshUTXOs(gettxdata.vin); @@ -381,20 +397,39 @@ class SendCoin extends React.Component { .then(function(save_this_data) { return saveNewCacheData(save_this_data); }); - }, 2000); + } + + let sentTxData = setInterval(function() { + getTxidData() + .then(function(gettxdata) { + if (gettxdata.vin && gettxdata.vin.length) { + clearInterval(sentTxData); + waterfallUTXOProcess(); + } + }) + }, 1000); console.log('utxo remove', true); } - }); + }.bind(this)); } else { Store.dispatch(sendToAddressStateAlt(json)); + + this.setState(Object.assign({}, this.state, { + utxoMethodInProgress: false, + })); } } else { Store.dispatch(sendToAddressStateAlt(json)); Store.dispatch(triggerToaster(true, 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) { @@ -480,7 +515,10 @@ class SendCoin extends React.Component { } else { return (
-
Processing transaction...
+
+ Processing transaction...
+ Note: it may take a few minutes to complete the transaction. +
@@ -712,6 +750,9 @@ class SendCoin extends React.Component {

{translate('INDEX.TRANSACTION_RESULT')}

+
+ You picked option "{translate('INDEX.DONT_SEND')}" +
@@ -725,7 +766,7 @@ class SendCoin extends React.Component {
- +
diff --git a/react/src/components/dashboard/settings.js b/react/src/components/dashboard/settings.js index 0b71cc3..5ff81a1 100644 --- a/react/src/components/dashboard/settings.js +++ b/react/src/components/dashboard/settings.js @@ -7,7 +7,8 @@ import { importPrivKey, getDebugLog, getPeersList, - addPeerNode + addPeerNode, + getAppConfig } from '../../actions/actionCreators'; import Store from '../../store'; import AddCoinOptionsCrypto from '../addcoin/addcoinOptionsCrypto'; @@ -42,6 +43,7 @@ class Settings extends React.Component { componentDidMount() { Store.dispatch(iguanaActiveHandle()); + Store.dispatch(getAppConfig()); } openTab(elemId, tab) { @@ -388,6 +390,43 @@ class Settings extends React.Component {
+ +
+
this.openTab('AppSettings', 7)}> + + App Settings (config.json) + +
+
+
+

Manage app settings

+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
{this.props.Settings.debugLog}
+
+
+
+
+
+
+
+
diff --git a/react/src/components/dashboard/walletsBalance.js b/react/src/components/dashboard/walletsBalance.js index 0a84b48..bdb28df 100644 --- a/react/src/components/dashboard/walletsBalance.js +++ b/react/src/components/dashboard/walletsBalance.js @@ -18,6 +18,49 @@ class WalletsBalance extends React.Component { } } + renderBalance(type) { + let _balance = '0'; + + if (this.props.ActiveCoin.mode === 'full') { + _balance = this.props.ActiveCoin.balance || 0; + } else { + if (this.props.ActiveCoin.cache) { + if (type === 'main' && + this.props.ActiveCoin.mode === 'basilisk' && + this.props.ActiveCoin.activeAddress && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.balance) { + _balance = this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.balance; + } + + if (type === 'interest' && + this.props.ActiveCoin.mode === 'basilisk' && + this.props.ActiveCoin.activeAddress && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.interest) { + _balance = this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.interest; + } + + if (type === 'total' && + this.props.ActiveCoin.mode === 'basilisk' && + this.props.ActiveCoin.activeAddress && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance && + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data && + (this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.balance || + this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.interest)) { + const _regBalance = this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.balance ? this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.balance : 0; + const _regInterest = this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.interest ? this.props.ActiveCoin.cache[this.props.ActiveCoin.coin][this.props.ActiveCoin.activeAddress].getbalance.data.interest : 0; + + _balance = _regBalance + _regInterest; + } + } + } + + return _balance; + } + render() { if (this.props && this.props.ActiveCoin && @@ -58,7 +101,7 @@ class WalletsBalance extends React.Component { {translate('INDEX.BALANCE')} - {this.props.ActiveCoin && this.props.ActiveCoin.balance ? this.props.ActiveCoin.balance : 0} + {this.renderBalance('main')} {this.props.ActiveCoin.coin} @@ -66,7 +109,7 @@ class WalletsBalance extends React.Component { -
+
@@ -75,7 +118,7 @@ class WalletsBalance extends React.Component { {translate('INDEX.INTEREST_EARNED')}
- + {this.renderBalance('interest')} {this.props.ActiveCoin.coin}
@@ -83,7 +126,7 @@ class WalletsBalance extends React.Component {
-
+
@@ -92,7 +135,7 @@ class WalletsBalance extends React.Component { {translate('INDEX.TOTAL_BALANCE')}
- + {this.renderBalance('total')} {this.props.ActiveCoin.coin}
diff --git a/react/src/components/dashboard/walletsData.js b/react/src/components/dashboard/walletsData.js index d86fb51..6d51695 100644 --- a/react/src/components/dashboard/walletsData.js +++ b/react/src/components/dashboard/walletsData.js @@ -16,7 +16,8 @@ import { toggleViewCacheModal, fetchNewCacheData, fetchUtxoCache, - restartBasiliskInstance + restartBasiliskInstance, + changeActiveAddress } from '../../actions/actionCreators'; import Store from '../../store'; @@ -50,7 +51,6 @@ class WalletsData extends React.Component { this.basiliskConnectionAction = this.basiliskConnectionAction.bind(this); this.getDexNotariesAction = this.getDexNotariesAction.bind(this); this.openDropMenu = this.openDropMenu.bind(this); - this.refreshTxList = this.refreshTxList.bind(this); this.removeAndFetchNewCache = this.removeAndFetchNewCache.bind(this); this._toggleViewCacheModal = this._toggleViewCacheModal.bind(this); this.toggleCacheApi = this.toggleCacheApi.bind(this); @@ -133,15 +133,22 @@ class WalletsData extends React.Component { })); } + basiliskRefreshActionOne() { + Store.dispatch(fetchNewCacheData({ + 'pubkey': this.props.Dashboard.activeHandle.pubkey, + 'allcoins': false, + 'coin': this.props.ActiveCoin.coin, + 'calls': 'listtransactions:getbalance', + })); + } + basiliskRefreshAction() { - /*if (this.props.Dashboard) { - Store.dispatch(basiliskRefresh(!this.props.Dashboard.basiliskRefresh)); - }*/ Store.dispatch(fetchNewCacheData({ 'pubkey': this.props.Dashboard.activeHandle.pubkey, 'allcoins': false, 'coin': this.props.ActiveCoin.coin, 'calls': 'listtransactions:getbalance', + 'address': this.props.ActiveCoin.activeAddress, })); } @@ -173,6 +180,12 @@ class WalletsData extends React.Component { } componentWillReceiveProps(props) { + if (!this.state.currentAddress && this.props.ActiveCoin.activeAddress) { + this.setState(Object.assign({}, this.state, { + currentAddress: this.props.ActiveCoin.activeAddress, + })); + } + if (this.props.ActiveCoin.txhistory && this.props.ActiveCoin.txhistory !== 'loading' && this.props.ActiveCoin.txhistory !== 'no data' && this.props.ActiveCoin.txhistory.length) { if (!this.state.itemsList || (this.state.itemsList && !this.state.itemsList.length) || (props.ActiveCoin.txhistory !== this.props.ActiveCoin.txhistory)) { let historyToSplit = sortByDate(this.props.ActiveCoin.txhistory); @@ -329,6 +342,8 @@ class WalletsData extends React.Component { } updateAddressSelection(address, type, amount) { + Store.dispatch(changeActiveAddress(address)); + this.setState(Object.assign({}, this.state, { currentAddress: address, addressSelectorOpen: false, @@ -341,16 +356,35 @@ class WalletsData extends React.Component { }.bind(this), 100); } - refreshTxList() { - Store.dispatch(getBasiliskTransactionsList(this.props.ActiveCoin.coin, this.props.ActiveCoin.mainBasiliskAddress)); - } - openDropMenu() { this.setState(Object.assign({}, this.state, { addressSelectorOpen: !this.state.addressSelectorOpen, })); } + /*filterTable() { + function myFunction() { + // Declare variables + var input, filter, table, tr, td, i; + input = document.getElementById("myInput"); + filter = input.value.toUpperCase(); + table = document.getElementById("myTable"); + tr = table.getElementsByTagName("tr"); + + // Loop through all table rows, and hide those who don't match the search query + for (i = 0; i < tr.length; i++) { + td = tr[i].getElementsByTagName("td")[0]; + if (td) { + if (td.innerHTML.toUpperCase().indexOf(filter) > -1) { + tr[i].style.display = ""; + } else { + tr[i].style.display = "none"; + } + } + } + } + }*/ + renderUseCacheToggle() { if (this.props.ActiveCoin.mode === 'basilisk') { return ( @@ -379,7 +413,7 @@ class WalletsData extends React.Component { } renderAddressAmount() { - if (this.props.ActiveCoin.addresses['public'] && this.props.ActiveCoin.addresses['public'].length) { + if (this.props.ActiveCoin.addresses && this.props.ActiveCoin.addresses['public'] && this.props.ActiveCoin.addresses['public'].length) { for (let i = 0; i < this.props.ActiveCoin.addresses['public'].length; i++) { if (this.props.ActiveCoin.addresses['public'][i].address === this.state.currentAddress) { return this.props.ActiveCoin.addresses['public'][i].amount; @@ -430,7 +464,12 @@ class WalletsData extends React.Component { } render() { - if (this.props && this.props.ActiveCoin && this.props.ActiveCoin.coin && this.props.ActiveCoin.mode !== 'native' && !this.props.ActiveCoin.send && !this.props.ActiveCoin.receive) { + if (this.props && + this.props.ActiveCoin && + this.props.ActiveCoin.coin && + this.props.ActiveCoin.mode !== 'native' && + !this.props.ActiveCoin.send && + !this.props.ActiveCoin.receive) { return ( @@ -450,9 +489,6 @@ class WalletsData extends React.Component { Processing requests: {this.state.currentStackLength} / {this.state.totalStackLength}
-
  • - {translate('INDEX.FETCH_WALLET_DATA')} + {translate('INDEX.FETCH_WALLET_DATA')} (active address) + +
  • +
  • + + Fetch (all addresses)
  • @@ -480,19 +521,14 @@ class WalletsData extends React.Component { {translate('INDEX.REFETCH_WALLET_DATA')}
  • -
  • +
  • Update UTXO
  • -
  • - - Fetch all - -
  • - Restart Basilisk Instance + Restart Basilisk Instance (!)
  • @@ -507,7 +543,7 @@ class WalletsData extends React.Component {
    -
    +
    {this.renderAddressList()}
    {this.renderUseCacheToggle} diff --git a/react/src/components/dashboard/walletsNativeSend.js b/react/src/components/dashboard/walletsNativeSend.js index 3cc33ef..ba30dd2 100644 --- a/react/src/components/dashboard/walletsNativeSend.js +++ b/react/src/components/dashboard/walletsNativeSend.js @@ -187,7 +187,7 @@ class WalletsNativeSend extends React.Component { Store.dispatch(sendNativeTx(this.props.ActiveCoin.coin, this.state)); setTimeout(function() { Store.dispatch(getKMDOPID(null, this.props.ActiveCoin.coin)); - }, 1000); + }.bind(this), 1000); } getOAdress() { diff --git a/react/src/components/login/login.js b/react/src/components/login/login.js index e72ffb8..a747eaa 100644 --- a/react/src/components/login/login.js +++ b/react/src/components/login/login.js @@ -68,9 +68,8 @@ class Login extends React.Component { this.setState({ display: false, }); - - // Store.dispatch(stopInterval('activeCoins', this.props.Interval.interval)); } + if (props && props.Main && !props.Main.isLoggedIn) { this.setState({ display: true, @@ -85,6 +84,7 @@ class Login extends React.Component { document.body.className = 'page-login layout-full page-dark'; } + if (this.state.activeLoginSection !== 'signup') { if (props && props.Main && props.Main.activeCoins) { this.setState({ @@ -124,6 +124,7 @@ class Login extends React.Component { this.setState({ isSeedConfirmError: false, }); + Store.dispatch(createNewWallet(this.state.randomSeedConfirm, this.props.Dashboard.activeHandle)); } else { this.setState({ @@ -132,6 +133,12 @@ class Login extends React.Component { } } + handleKeydown(e) { + if (e.key === 'Enter') { + this.loginSeed(); + } + } + render() { if ((this.state && this.state.display) || !this.props.Main) { return ( @@ -178,7 +185,7 @@ class Login extends React.Component {

    {translate('INDEX.WELCOME_LOGIN')}

    - + this.handleKeydown(event)} />
    diff --git a/react/src/reducers/activeCoin.js b/react/src/reducers/activeCoin.js index 0c9c2bf..58200d9 100644 --- a/react/src/reducers/activeCoin.js +++ b/react/src/reducers/activeCoin.js @@ -14,7 +14,8 @@ import { DASHBOARD_ACTIVE_COIN_GET_CACHE, DASHBOARD_ACTIVE_COIN_MAIN_BASILISK_ADDR, DASHBOARD_GET_NOTARIES_LIST, - DASHBOARD_DISPLAY_NOTARIES_MODAL + DASHBOARD_DISPLAY_NOTARIES_MODAL, + DASHBOARD_ACTIVE_ADDRESS, } from '../actions/actionCreators'; // TODO: refactor @@ -36,6 +37,7 @@ export function ActiveCoin(state = { mainBasiliskAddress: null, notaries: null, displayNotariesModal: false, + activeAddress: null, }, action) { switch (action.type) { case DASHBOARD_ACTIVE_COIN_CHANGE: @@ -55,6 +57,7 @@ export function ActiveCoin(state = { lastSendToResponse: state.lastSendToResponse, mainBasiliskAddress: state.mainBasiliskAddress, opids: state.opids, + activeBasiliskAddress: state.activeBasiliskAddress, }; let _coins = state.coins; _coins[state.coin] = _coinDataToStore; @@ -74,6 +77,7 @@ export function ActiveCoin(state = { lastSendToResponse: _coinData.lastSendToResponse, mainBasiliskAddress: _coinData.mainBasiliskAddress, opids: _coinData.opids, + activeBasiliskAddress: _coinData.activeBasiliskAddress, }); } else { if (state.coin) { @@ -91,6 +95,7 @@ export function ActiveCoin(state = { lastSendToResponse: state.lastSendToResponse, mainBasiliskAddress: state.mainBasiliskAddress, opids: state.opids, + activeBasiliskAddress: state.activeBasiliskAddress, }; let _coins = state.coins; _coins[state.coin] = _coinData; @@ -185,6 +190,10 @@ export function ActiveCoin(state = { return Object.assign({}, state, { displayNotariesModal: action.display, }); + case DASHBOARD_ACTIVE_ADDRESS: + return Object.assign({}, state, { + activeAddress: action.address, + }); default: return state; } diff --git a/react/src/styles/index.scss b/react/src/styles/index.scss index fc7e6f4..608d56f 100644 --- a/react/src/styles/index.scss +++ b/react/src/styles/index.scss @@ -272,6 +272,10 @@ body { } } +.center { + text-align: center; +} + /*.toaster .single-toast:nth-child(0) { bottom: 12px; } diff --git a/react/src/util/cacheFormat.js b/react/src/util/cacheFormat.js index 90f4cb0..0ea9bfb 100644 --- a/react/src/util/cacheFormat.js +++ b/react/src/util/cacheFormat.js @@ -11,7 +11,7 @@ export function edexGetTxIDList(getTxData) { } export function edexRemoveTXID(_obj, txidArray) { - let txidToStr = txidArray.join(':'); + let txidToStr = ':' + txidArray.join(':') + ':'; console.log(txidToStr); @@ -26,8 +26,21 @@ export function edexRemoveTXID(_obj, txidArray) { _obj.basilisk[key][coinAddr].refresh.data && _obj.basilisk[key][coinAddr].refresh.data.length > 0) { for (let i = 0; i < _obj.basilisk[key][coinAddr].refresh.data.length; i++) { - if (txidToStr.indexOf(_obj.basilisk[key][coinAddr].refresh.data[i].txid) > -1) { + if (txidToStr.indexOf(_obj.basilisk[key][coinAddr].refresh.data[i].txid.toString()) > -1) { + console.log('cacheformat remove node', _obj.basilisk[key][coinAddr].refresh.data[i].txid); _obj.basilisk[key][coinAddr].refresh.data.splice(i, 1); + _obj.basilisk[key][coinAddr].refresh.timestamp = Date.now(); + } + } + } + if (_obj.basilisk[key][coinAddr].listunspent && + _obj.basilisk[key][coinAddr].listunspent.data && + _obj.basilisk[key][coinAddr].listunspent.data.length > 0) { + for (let i = 0; i < _obj.basilisk[key][coinAddr].listunspent.data.length; i++) { + if (txidToStr.indexOf(_obj.basilisk[key][coinAddr].listunspent.data[i].txid.toString()) > -1) { + console.log('cacheformat remove node', _obj.basilisk[key][coinAddr].listunspent.data[i].txid); + _obj.basilisk[key][coinAddr].listunspent.data.splice(i, 1); + _obj.basilisk[key][coinAddr].listunspent.timestamp = Date.now(); } } } @@ -38,4 +51,6 @@ export function edexRemoveTXID(_obj, txidArray) { } else { console.log('basilisk node is missing'); } + + return _obj; } \ No newline at end of file