diff --git a/react/src/actions/actions/nativeSyncInfo.js b/react/src/actions/actions/nativeSyncInfo.js index 867605310..94af944df 100644 --- a/react/src/actions/actions/nativeSyncInfo.js +++ b/react/src/actions/actions/nativeSyncInfo.js @@ -5,7 +5,6 @@ import { import { triggerToaster, getDebugLog, - toggleCoindDownModal } from '../actionCreators'; import Config from '../../config'; import { translate } from '../../translate/translate'; @@ -138,7 +137,7 @@ export function getSyncInfoNative(coin, skipDebug, skipRemote, suppressErrors) { { result: 'daemon is busy', error: null, - id: null + id: null, }, coin, true, @@ -146,16 +145,15 @@ export function getSyncInfoNative(coin, skipDebug, skipRemote, suppressErrors) { ) ); } else { - if (!json) { + if (!json || + json.indexOf('"code":-777') > -1) { let _kmdMainPassiveMode; try { _kmdMainPassiveMode = window.require('electron').remote.getCurrentWindow().kmdMainPassiveMode; } catch (e) {} - if (!_kmdMainPassiveMode) { - dispatch(nativeGetinfoFailureState()); - } else { + if (_kmdMainPassiveMode) { dispatch( triggerToaster( translate('API.KMD_PASSIVE_ERROR'), @@ -171,7 +169,6 @@ export function getSyncInfoNative(coin, skipDebug, skipRemote, suppressErrors) { } else { dispatch(getDebugLog('komodo', 50, coin)); } - dispatch(toggleCoindDownModal(true)); } else { json = JSON.parse(json); } diff --git a/react/src/actions/actions/settings.js b/react/src/actions/actions/settings.js index dfe0a429e..5db5ebf8d 100644 --- a/react/src/actions/actions/settings.js +++ b/react/src/actions/actions/settings.js @@ -386,4 +386,96 @@ export function resetAppConfig() { ); }) } +} + +export function coindGetStdout(chain) { + const _chain = chain === 'KMD' ? 'komodod' : chain; + + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/coind/stdout?chain=${chain}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .catch((error) => { + console.log(error); + dispatch( + triggerToaster( + 'coindGetStdout', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(json); + }); + }); +} + +export function getWalletDatKeys(chain, keyMatchPattern) { + const _chain = chain === 'KMD' ? null : chain; + + return new Promise((resolve, reject) => { + fetch(keyMatchPattern ? `http://127.0.0.1:${Config.agamaPort}/shepherd/coindwalletkeys?chain=${_chain}&search=${keyMatchPattern}` : `http://127.0.0.1:${Config.agamaPort}/shepherd/coindwalletkeys?chain=${_chain}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .catch((error) => { + console.log(error); + dispatch( + triggerToaster( + 'getWalletDatKeys', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(json); + }); + }); +} + +export function dumpPrivKey(coin, address) { + return new Promise((resolve, reject) => { + const payload = { + mode: null, + chain: coin, + cmd: 'dumpprivkey', + params: [ address ] + }; + + const _fetchConfig = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ 'payload': payload }), + }; + + fetch( + `http://127.0.0.1:${Config.agamaPort}/shepherd/cli`, + _fetchConfig + ) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'dumpPrivKey', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(json.result ? json.result : json); + }) + }); } \ No newline at end of file diff --git a/react/src/components/addcoin/addcoin.js b/react/src/components/addcoin/addcoin.js index 43237f64c..1d42963f2 100644 --- a/react/src/components/addcoin/addcoin.js +++ b/react/src/components/addcoin/addcoin.js @@ -7,9 +7,11 @@ import { toggleAddcoinModal, triggerToaster, shepherdGetCoinList, - shepherdPostCoinList + shepherdPostCoinList, + toggleZcparamsFetchModal, } from '../../actions/actionCreators'; import Store from '../../store'; +import { zcashParamsCheckErrors } from '../../util/zcashParams'; import CoinSelectorsRender from './coin-selectors.render'; import AddCoinRender from './addcoin.render'; @@ -46,6 +48,43 @@ class AddCoin extends React.Component { this.toggleActionsMenu = this.toggleActionsMenu.bind(this); this.saveCoinSelection = this.saveCoinSelection.bind(this); this.loadCoinSelection = this.loadCoinSelection.bind(this); + this.verifyZcashParamsExist = this.verifyZcashParamsExist.bind(this); + } + + verifyZcashParamsExist(mode) { + return new Promise((resolve, reject) => { + if (Number(mode) === -1) { + const _res = window.require('electron').remote.getCurrentWindow().zcashParamsExist; + const __errors = zcashParamsCheckErrors(_res); + + if (__errors) { + window.require('electron').remote.getCurrentWindow().zcashParamsExistPromise() + .then((res) => { + const _errors = zcashParamsCheckErrors(res); + window.require('electron').remote.getCurrentWindow().zcashParamsExist = res; + + if (_errors) { + Store.dispatch( + triggerToaster( + _errors, + 'Komodod', + 'error', + false + ) + ); + Store.dispatch(toggleZcparamsFetchModal(true)); + resolve(false); + } else { + resolve(true); + } + }); + } else { + resolve(true); + } + } else { + resolve(true); + } + }); } saveCoinSelection() { @@ -206,23 +245,28 @@ class AddCoin extends React.Component { return; } - if (!_coin.daemonParam) { - Store.dispatch(addCoin( - coin, - _coin.mode, - )); - } else { - Store.dispatch(addCoin( - coin, - _coin.mode, - { type: _coin.daemonParam } - )); - } + this.verifyZcashParamsExist(_coin.mode) + .then((res) => { + if (res) { + if (!_coin.daemonParam) { + Store.dispatch(addCoin( + coin, + _coin.mode, + )); + } else { + Store.dispatch(addCoin( + coin, + _coin.mode, + { type: _coin.daemonParam } + )); + } - this.removeCoin(); - this.addNewItem(); + this.removeCoin(); + this.addNewItem(); - Store.dispatch(toggleAddcoinModal(false, false)); + Store.dispatch(toggleAddcoinModal(false, false)); + } + }); } dismiss() { diff --git a/react/src/components/dashboard/coinTile/coinTileItem.js b/react/src/components/dashboard/coinTile/coinTileItem.js index cbd3c6419..12b824143 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.js @@ -24,13 +24,14 @@ import { activeHandle, triggerToaster, shepherdRemoveCoin, + toggleCoindDownModal, } from '../../../actions/actionCreators'; import Store from '../../../store'; import Config from '../../../config'; import CoinTileItemRender from './coinTileItem.render'; -const SPV_DASHBOARD_UPDATE_TIMEOUT = 10000; +const SPV_DASHBOARD_UPDATE_TIMEOUT = 60000; const ACTIVE_HANDLE_TIMEOUT_COIND_NATIVE = 15000; const COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD = window.require('electron').remote.getCurrentWindow().appConfig.failedRPCAttemptsThreshold || 10; @@ -39,10 +40,31 @@ class CoinTileItem extends React.Component { super(); this.state = { appConfig: {}, + activeCoin: null, + activeCoinMode: null, + propsUpdatedCounter: 0, }; this.autoSetActiveCoin = this.autoSetActiveCoin.bind(this); } + openCoindDownModal() { + Store.dispatch(toggleCoindDownModal(true)); + } + + renderCoinConError(item) { + if (this.props.ActiveCoin.getinfoFetchFailures >= COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD && + (this.props.ActiveCoin.mode === 'native' && + this.props.ActiveCoin.coin === this.state.activeCoin && + this.props.ActiveCoin.coin === item.coin && + this.state.activeCoin === item.coin && + this.state.activeCoinMode === 'native' && + this.props.ActiveCoin.mode === this.state.activeCoinMode && + this.state.propsUpdatedCounter > 1) || + (this.props.ActiveCoin.coins && this.props.ActiveCoin.coins[item.coin]) && this.props.ActiveCoin.coins[item.coin].getinfoFetchFailures >= COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD) { + return true; + } + } + renderStopCoinButton() { if (this.props.Main && this.props.Main.coins && @@ -87,11 +109,11 @@ class CoinTileItem extends React.Component { _coinMode[coin] = mode; }); - if (_coinMode['KMD'] && - _coinMode['KMD'] === 'native') { + if (_coinMode.KMD && + _coinMode.KMD === 'native') { _coin = 'KMD'; _mode = 'native'; - } else if (_coinMode['KMD'] && _coinMode['KMD'] === 'spv') { + } else if (_coinMode.KMD && _coinMode.KMD === 'spv') { _coin = 'KMD'; _mode = 'spv'; } @@ -271,6 +293,13 @@ class CoinTileItem extends React.Component { Store.dispatch(electrumServerChanged(false)); }, 100); } + + this.setState({ + activeCoin: props.ActiveCoin.coin, + activeCoinMode: props.ActiveCoin.mode, + // prevent native con error icon flashing on coin switch + propsUpdatedCounter: this.state.activeCoin === props.ActiveCoin.coin && this.state.activeCoinMode === props.ActiveCoin.mode ? this.state.propsUpdatedCounter + 1 : 0, + }); } render() { @@ -282,6 +311,7 @@ const mapStateToProps = (state) => { return { ActiveCoin: { coin: state.ActiveCoin.coin, + coins: state.ActiveCoin.coins, mode: state.ActiveCoin.mode, addresses: state.ActiveCoin.addresses, mainBasiliskAddress: state.ActiveCoin.mainBasiliskAddress, diff --git a/react/src/components/dashboard/coinTile/coinTileItem.render.js b/react/src/components/dashboard/coinTile/coinTileItem.render.js index 08578f0e6..333e5e107 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.render.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.render.js @@ -34,7 +34,7 @@ const CoinTileItemRender = function() { this.removeCoin(item.coin, item.mode) } title="Remove" - className={ 'icon fa-plus-circle ' + (item.mode === 'spv' ? 'coind-remove-icon coind-remove-icon-spv' : 'coind-remove-icon') }> + className={ 'icon fa-plus-circle coind-remove-icon' + (item.mode === 'spv' ? ' coind-remove-icon-spv' : '') }> } { this.props.Dashboard && this.props.Dashboard.electrumCoins && @@ -45,6 +45,12 @@ const CoinTileItemRender = function() { title={ translate('SETTINGS.SPV_SINGLE_SERVER_NOTICE') } className="icon fa-info-circle icon-spv-connection-warning"> } + { this.renderCoinConError(item) && + + } ); }; diff --git a/react/src/components/dashboard/coindDownModal/coindDownModal.js b/react/src/components/dashboard/coindDownModal/coindDownModal.js index c538ba327..d43de7f8f 100644 --- a/react/src/components/dashboard/coindDownModal/coindDownModal.js +++ b/react/src/components/dashboard/coindDownModal/coindDownModal.js @@ -1,6 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; -import { toggleCoindDownModal } from '../../../actions/actionCreators'; +import { + toggleCoindDownModal, + coindGetStdout, + getDebugLog, +} from '../../../actions/actionCreators'; import Store from '../../../store'; import CoindDownModalRender from './coindDownModal.render'; @@ -12,10 +16,43 @@ class CoindDownModal extends React.Component { super(); this.state = { display: false, - debugLogCrash: null, kmdMainPassiveMode: false, + coindStdOut: 'Loading...', + toggleDebugLog: true, }; this.dismiss = this.dismiss.bind(this); + this.getCoindGetStdout = this.getCoindGetStdout.bind(this); + this.toggleDebugLog = this.toggleDebugLog.bind(this); + this.refreshDebugLog = this.refreshDebugLog.bind(this); + } + + refreshDebugLog() { + const _coin = this.props.ActiveCoin.coin; + + if (!this.state.toggleDebugLog) { + if (_coin === 'KMD') { + Store.dispatch(getDebugLog('komodo', 50)); + } else { + Store.dispatch(getDebugLog('komodo', 50, _coin)); + } + } else { + this.getCoindGetStdout(); + } + } + + toggleDebugLog() { + this.setState({ + toggleDebugLog: !this.state.toggleDebugLog, + }); + } + + getCoindGetStdout() { + coindGetStdout(this.props.ActiveCoin.coin) + .then((res) => { + this.setState({ + coindStdOut: res.msg === 'success' ? res.result : `Error reading ${this.props.ActiveCoin.coin} stdout`, + }); + }); } dismiss() { @@ -39,6 +76,10 @@ class CoindDownModal extends React.Component { this.setState(Object.assign({}, this.state, { display: nextProps.displayCoindDownModal, })); + + if (nextProps.displayCoindDownModal) { + this.getCoindGetStdout(); + } } } diff --git a/react/src/components/dashboard/coindDownModal/coindDownModal.render.js b/react/src/components/dashboard/coindDownModal/coindDownModal.render.js index bbd5eb03b..e5acc9705 100644 --- a/react/src/components/dashboard/coindDownModal/coindDownModal.render.js +++ b/react/src/components/dashboard/coindDownModal/coindDownModal.render.js @@ -27,12 +27,36 @@ const CoindDownModalRender = function() {
- Debug.log ({ translate('INDEX.LAST_50_LINES') }) + +
+ +
+ Show debug.log +
+
+ { !this.state.toggleDebugLog && + Debug.log ({ translate('INDEX.LAST_50_LINES') }) + } + { this.state.toggleDebugLog && + { this.props.ActiveCoin.coin === 'KMD' ? 'Komodod' : `Komodod / ${this.props.ActiveCoin.coin} stdout` } + }
+ value={ !this.state.toggleDebugLog ? _debuglog : this.state.coindStdOut }>
- + { this.state.toggledAddressMenu && + this.state.toggledAddressMenu === address && +
+
    +
  • this._copyCoinAddress(address) }> + { translate('INDEX.COPY') + ' pub key' } +
  • + { address[0] !== 'b' && +
  • this.dumpPrivKey(address) }> + { translate('INDEX.COPY') + ' priv key (WIF)' } +
  • + } +
  • + +
  • +
+
+ } ); }; diff --git a/react/src/components/dashboard/settings/settings.daemonStdoutPanel.js b/react/src/components/dashboard/settings/settings.daemonStdoutPanel.js new file mode 100644 index 000000000..3fbad6345 --- /dev/null +++ b/react/src/components/dashboard/settings/settings.daemonStdoutPanel.js @@ -0,0 +1,111 @@ +import React from 'react'; +import { translate } from '../../../translate/translate'; +import { connect } from 'react-redux'; +import { coindGetStdout } from '../../../actions/actionCreators'; +import Store from '../../../store'; + +class DaemonStdoutPanel extends React.Component { + constructor() { + super(); + this.state = { + coindStdOut: 'Loading...', + coin: null, + textareaHeight: '100px', + }; + this.getCoindGetStdout = this.getCoindGetStdout.bind(this); + this.updateInput = this.updateInput.bind(this); + } + + componentWillMount() { + this.getCoindGetStdout(); + } + + getCoindGetStdout() { + const _coin = this.state.coin || this.props.ActiveCoin.coin; + + coindGetStdout(_coin) + .then((res) => { + this.setState({ + coindStdOut: res.msg === 'success' ? res.result : `Error reading ${_coin} stdout`, + }); + + setTimeout(() => { + document.querySelector('#settingsCoindStdoutTextarea').style.height = '1px'; + document.querySelector('#settingsCoindStdoutTextarea').style.height = `${(15 + document.querySelector('#settingsCoindStdoutTextarea').scrollHeight)}px`; + }, 100); + }); + } + + updateInput(e) { + this.setState({ + [e.target.name]: e.target.value, + }); + + this.getCoindGetStdout(); + } + + renderCoinListSelectorOptions(coin) { + let _items = []; + let _nativeCoins = this.props.Main.coins.native; + + for (let i = 0; i < _nativeCoins.length; i++) { + _items.push( + + ); + } + + return _items; + } + + render() { + return ( +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+ ); + }; +} + +const mapStateToProps = (state) => { + return { + ActiveCoin: { + coin: state.ActiveCoin.coin, + mode: state.ActiveCoin.mode, + }, + Main: state.Main, + }; +}; + +export default connect(mapStateToProps)(DaemonStdoutPanel); \ No newline at end of file diff --git a/react/src/components/dashboard/settings/settings.nativeWalletDatKeysPanel.js b/react/src/components/dashboard/settings/settings.nativeWalletDatKeysPanel.js new file mode 100644 index 000000000..61de2f901 --- /dev/null +++ b/react/src/components/dashboard/settings/settings.nativeWalletDatKeysPanel.js @@ -0,0 +1,188 @@ +import React from 'react'; +import { translate } from '../../../translate/translate'; +import { connect } from 'react-redux'; +import { getWalletDatKeys } from '../../../actions/actionCreators'; +import { coindList } from '../../../util/coinHelper'; +import Store from '../../../store'; + +class NativeWalletDatKeysPanel extends React.Component { + constructor() { + super(); + this.state = { + coin: 'none', + keys: null, + keyMatchPattern: '', + loading: false, + }; + this._getWalletDatKeys = this._getWalletDatKeys.bind(this); + this.updateInput = this.updateInput.bind(this); + } + + componentWillReceiveProps(props) { + if (props.Dashboard && + props.Dashboard.activeSection !== 'settings') { + this.setState(Object.assign({}, this.state, { + keys: null, + keyMatchPattern: null, + })); + } + } + + _getWalletDatKeys() { + const _coin = this.state.coin; + + this.setState({ + loading: true, + keys: null, + }); + + setTimeout(() => { + getWalletDatKeys(_coin, this.state.keyMatchPattern.length ? this.state.keyMatchPattern : null) + .then((res) => { + this.setState({ + keys: res, + loading: false, + }); + + if (res.msg === 'success' && + res.result.length > 0) { + setTimeout(() => { + document.querySelector('#coind-keys-textarea-left').style.height = '1px'; + document.querySelector('#coind-keys-textarea-left').style.height = `${(15 + document.querySelector('#coind-keys-textarea-left').scrollHeight)}px`; + document.querySelector('#coind-keys-textarea-right').style.height = `${(15 + document.querySelector('#coind-keys-textarea-right').scrollHeight)}px`; + }, 100); + } + }); + }, 100); + } + + updateInput(e) { + this.setState({ + [e.target.name]: e.target.value, + }); + } + + renderCoinListSelectorOptions() { + let _items = []; + let _nativeCoins = coindList(); + + _items.push( + + ); + for (let i = 0; i < _nativeCoins.length; i++) { + _items.push( + + ); + } + + return _items; + } + + renderKeys() { + let _items = []; + + const _keys = this.state.keys; + + if (_keys.msg === 'error') { + return ( +
{ _keys.result }
+ ); + } else { + let _addresses = ''; + let _wifs = ''; + + for (let i = 0; i < _keys.result.length; i++) { + _addresses += _keys.result[i].pub + '\n'; + _wifs += _keys.result[i].priv + '\n'; + } + + return ( +
+
+ Found { _keys.result.length } keys +
+ { _keys.result.length > 0 && +
+
+ Address +
+ +
+ } + { _keys.result.length > 0 && +
+
+ WIF +
+ +
+ } +
+ ); + } + } + + render() { + return ( +
+
+
+
+
+ + + +
+
+
+ { this.state.keys && +
+
+
+ { this.renderKeys() } +
+
+ } +
+
+ ); + }; +} + +const mapStateToProps = (state) => { + return { + Dashboard: state.Dashboard, + }; +}; + +export default connect(mapStateToProps)(NativeWalletDatKeysPanel); \ No newline at end of file diff --git a/react/src/components/dashboard/settings/settings.panelBody.js b/react/src/components/dashboard/settings/settings.panelBody.js index ed5f10209..f6eb3962d 100644 --- a/react/src/components/dashboard/settings/settings.panelBody.js +++ b/react/src/components/dashboard/settings/settings.panelBody.js @@ -13,6 +13,7 @@ class PanelSection extends React.Component { componentDidMount() { const { active } = this.props; + let _pass = false; if (active) { this.setState({ @@ -22,14 +23,15 @@ class PanelSection extends React.Component { this.accordionResizeInterval = setInterval(() => { if (this.props.active) { - this.setState({ - sectionHeight: `${this.accordionContent.scrollHeight}px`, + this.setState({ // auto resize hack + sectionHeight: _pass ? 'auto' : `${this.accordionContent.scrollHeight}px`, }); + _pass = !_pass; } }, 500); } - componentWillUnmount() { + componentWillUnmount() { // revise(?) clearInterval(this.accordionResizeInterval); } diff --git a/react/src/components/dashboard/settings/settings.render.js b/react/src/components/dashboard/settings/settings.render.js index e6f8a06d6..eba640c5b 100644 --- a/react/src/components/dashboard/settings/settings.render.js +++ b/react/src/components/dashboard/settings/settings.render.js @@ -14,6 +14,9 @@ import ExportKeysPanel from './settings.exportKeysPanel'; // import ImportKeysPanel from './settings.importKeysPanel'; import SupportPanel from './settings.supportPanel'; import SPVServersPanel from './settings.spvServersPanel'; +import DaemonStdoutPanel from './settings.daemonStdoutPanel'; +import NativeWalletDatKeysPanel from './settings.nativeWalletDatKeysPanel'; + // import WalletInfoPanel from './settings.walletInfoPanel'; // import WalletBackupPanel from './settings.walletBackupPanel'; @@ -73,9 +76,18 @@ export const SettingsRender = function() { openByDefault={this.props.disableWalletSpecificUI}> + { this.props.Main.coins && + this.props.Main.coins.native && + Object.keys(this.props.Main.coins.native).length > 0 && + + + + } + icon="icon fa-wrench"> } + { this.props.Main.coins && + this.props.Main.coins.native && + Object.keys(this.props.Main.coins.native).length > 0 && + + + + } { this.props.Main.coins && this.props.Main.coins.spv && Object.keys(this.props.Main.coins.spv).length && diff --git a/react/src/components/dashboard/walletsData/walletsData.js b/react/src/components/dashboard/walletsData/walletsData.js index 6b15b6166..02f97a112 100644 --- a/react/src/components/dashboard/walletsData/walletsData.js +++ b/react/src/components/dashboard/walletsData/walletsData.js @@ -177,7 +177,7 @@ class WalletsData extends React.Component { id: 'timestamp', Header: translate('INDEX.TIME'), Footer: translate('INDEX.TIME'), - accessor: (tx) => secondsToString(tx.blocktime || tx.timestamp || tx.time), + accessor: (tx) => secondsToString(tx.timestamp || tx.time || tx.blocktime), }]; if (itemsCount <= BOTTOM_BAR_DISPLAY_THRESHOLD) { diff --git a/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.js b/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.js index 7a28f79a3..9c77e3114 100644 --- a/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.js +++ b/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.js @@ -108,11 +108,17 @@ class ZcparamsFetchModal extends React.Component { if (data.msg.file === 'proving') { _updateLog = []; _updateLog.push(translate('ZCPARAMS_FETCH.BOTH_KEYS_VERIFIED')); - _updateLog.push(translate('ZCPARAMS_FETCH.PLEASE_RESTART')); + // _updateLog.push(translate('ZCPARAMS_FETCH.PLEASE_RESTART')); this.setState(Object.assign({}, this.state, { updateLog: _updateLog, done: true, })); + + window.require('electron').remote.getCurrentWindow().zcashParamsExistPromise() + .then((res) => { + const _errors = zcashParamsCheckErrors(res); + window.require('electron').remote.getCurrentWindow().zcashParamsExist = res; + }); } else { this.setState(Object.assign({}, this.state, { updateLog: _updateLog, diff --git a/react/src/components/login/login.js b/react/src/components/login/login.js index 8b655f8d9..1f991a8a0 100644 --- a/react/src/components/login/login.js +++ b/react/src/components/login/login.js @@ -499,9 +499,7 @@ class Login extends React.Component { const mapStateToProps = (state) => { return { Main: state.Main, - Dashboard: { - activeHandle: state.Dashboard.activeHandle, - }, + Dashboard: state.Dashboard, Interval: { interval: state.Interval.interval, }, diff --git a/react/src/components/login/login.render.js b/react/src/components/login/login.render.js index 3442a9e80..10b2d04ba 100644 --- a/react/src/components/login/login.render.js +++ b/react/src/components/login/login.render.js @@ -1,10 +1,14 @@ import React from 'react'; import { translate } from '../../translate/translate'; import LoginSettingsModal from '../dashboard/loginSettingsModal/loginSettingsModal'; +import ZcparamsFetchModal from '../dashboard/zcparamsFetchModal/zcparamsFetchModal'; const LoginRender = function () { return (
+ { this.props.Dashboard.displayZcparamsModal && + + } { this.renderSwallModal() }
diff --git a/react/src/components/main/main.js b/react/src/components/main/main.js index 4ca80d70d..7126b67fa 100644 --- a/react/src/components/main/main.js +++ b/react/src/components/main/main.js @@ -1,13 +1,10 @@ import React from 'react'; import WalletMain from './walletMain'; import Store from '../../store'; -import { translate } from '../../translate/translate'; import { getDexCoins, activeHandle, - triggerToaster, shepherdElectrumCoins, - toggleZcparamsFetchModal, } from '../../actions/actionCreators'; class Main extends React.Component { @@ -20,51 +17,16 @@ class Main extends React.Component { componentDidMount() { let appVersion; - let zcashParamsExist; let appConfig; try { appVersion = window.require('electron').remote.getCurrentWindow().appBasicInfo; appConfig = window.require('electron').remote.getCurrentWindow().appConfig; - zcashParamsExist = window.require('electron').remote.getCurrentWindow().zcashParamsExist; } catch (e) {} if (appVersion) { document.title = `${appVersion.name} (v${appVersion.version.replace('version=', '')}-beta)`; } - - // TODO: isolate check only when komodod is detected - if (zcashParamsExist.errors) { - let _errors = [translate('KMD_NATIVE.ZCASH_PARAMS_MISSING'), '']; - - if (!zcashParamsExist.rootDir) { - _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_ROOT_DIR')); - } - if (!zcashParamsExist.provingKey) { - _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_PROVING_KEY')); - } - if (!zcashParamsExist.verifyingKey) { - _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_VERIFYING_KEY')); - } - if (!zcashParamsExist.provingKeySize && - zcashParamsExist.provingKey) { - _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_PROVING_KEY_SIZE')); - } - if (!zcashParamsExist.verifyingKeySize && - zcashParamsExist.verifyingKey) { - _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_VERIFYING_KEY_SIZE')); - } - - Store.dispatch( - triggerToaster( - _errors, - 'Komodo', - 'error', - false - ) - ); - Store.dispatch(toggleZcparamsFetchModal(true)); - } } componentWillMount() { diff --git a/react/src/components/overrides.scss b/react/src/components/overrides.scss index 99a3facb8..62ea340d4 100644 --- a/react/src/components/overrides.scss +++ b/react/src/components/overrides.scss @@ -447,9 +447,10 @@ select{ padding-top: 5px; } -.icon-spv-connection-warning { +.icon-spv-connection-warning, +.icon-native-connection-warning { position: absolute; - bottom: 15px; + bottom: 20px; right: 15px; &:before { @@ -458,6 +459,12 @@ select{ } } +.icon-native-connection-warning { + &:before { + color: #e9595b; + } +} + .color-warning { color: #FF6600; } @@ -522,4 +529,66 @@ select{ tbody > tr > td { border: none; } +} + +.manual-debuglog-refresh { + position: absolute; + top: 40px; + right: 40px; + font-size: 20px; +} + +.coind-stdout-refresh-icon { + font-size: 20px; + margin-top: 7px; +} + +.settings-coind-stdout-textarea { + border: none; + background-image: none !important; +} + +.receive-address-context-menu { + box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.4); + background: #fff; + position: absolute; + z-index: 100; + margin-left: 65px; + margin-top: 5px; + + ul { + margin: 0; + list-style: none; + -webkit-margin-before: 0; + -webkit-margin-after: 0; + -webkit-margin-start: 0; + -webkit-margin-end: 0; + -webkit-padding-start: 0; + + li { + padding: 5px 15px; + + &:first-child { + margin-top: 5px; + } + &:last-child { + margin-bottom: 5px; + } + &:hover { + background: #f9f9f9; + cursor: pointer; + } + } + } + &.closed { + z-index: 1700; + position: absolute; + top: -100%; + left: -100%; + } + .receive-address-context-menu-get-qr { + .modal { + cursor: default; + } + } } \ No newline at end of file diff --git a/react/src/reducers/activeCoin.js b/react/src/reducers/activeCoin.js index 6ac7edcae..4b88843f0 100644 --- a/react/src/reducers/activeCoin.js +++ b/react/src/reducers/activeCoin.js @@ -59,6 +59,7 @@ export function ActiveCoin(state = { activeBasiliskAddress: state.activeBasiliskAddress, progress: state.progress, rescanInProgress: state.rescanInProgress, + getinfoFetchFailures: state.getinfoFetchFailures, }; let _coins = state.coins; _coins[state.coin] = _coinDataToStore; @@ -81,6 +82,7 @@ export function ActiveCoin(state = { activeBasiliskAddress: _coinData.activeBasiliskAddress, progress: _coinData.progress, rescanInProgress: _coinData.rescanInProgress, + getinfoFetchFailures: _coinData.getinfoFetchFailures, }; } else { if (state.coin) { @@ -100,6 +102,7 @@ export function ActiveCoin(state = { activeBasiliskAddress: state.activeBasiliskAddress, progress: state.progress, rescanInProgress: state.rescanInProgress, + getinfoFetchFailures: state.getinfoFetchFailures, }; let _coins = state.coins; _coins[state.coin] = _coinData; @@ -214,7 +217,7 @@ export function ActiveCoin(state = { return { ...state, progress: action.progress, - getinfoFetchFailures: !action.progress ? state.getinfoFetchFailures + 1 : 0, + getinfoFetchFailures: typeof action.progress === 'string' && action.progress.indexOf('"code":-777') ? state.getinfoFetchFailures + 1 : 0, }; case DASHBOARD_ACTIVE_COIN_GETINFO_FAILURE: return { diff --git a/react/src/reducers/toaster.js b/react/src/reducers/toaster.js index 0abc6c171..35a05b802 100644 --- a/react/src/reducers/toaster.js +++ b/react/src/reducers/toaster.js @@ -12,9 +12,18 @@ export function toaster(state = { case ADD_TOASTER_MESSAGE: let _isSameToastTwice = false; + function arrayToString(arr) { + if (typeof arr === 'object') { + return arr.join().toString(); + } else { + return arr; + } + } + for (let i = 0; i < state.toasts.length; i++) { if (state.toasts[i].title === action.title && - state.toasts[i].message === action.message && + (arrayToString(action.message) === arrayToString(state.toasts[i].message) || + state.toasts[i].message === action.message) && state.toasts[i]['_type'] === action['_type']) { _isSameToastTwice = true; break; diff --git a/react/src/translate/en.js b/react/src/translate/en.js index 848fdc62e..89591f20b 100644 --- a/react/src/translate/en.js +++ b/react/src/translate/en.js @@ -471,6 +471,8 @@ export const _lang = { 'CURRENT_BLOCK_SM': 'current block', }, 'TOASTR': { + 'PORT_IS_TAKEN': 'Port @template@ is already taken!', + 'ERROR_STARTING_DAEMON': 'Error starting @template@ daemon.', 'KOMODO_DATADIR_INVALID': 'Komodo datadir path is invalid.
It must be an absolute path to an existing folder that doesn\'t contain spaces and/or any special characters.', 'KOMODO_DATADIR_NOT_DIR': 'Komodo datadir path is not a directory.
It must be an absolute path to an existing folder that doesn\'t contain spaces and/or any special characters.', 'INVALID_ADDRESS': 'Invalid @template@ address', diff --git a/react/src/util/coinHelper.js b/react/src/util/coinHelper.js index 06f96c43f..9b0d20679 100644 --- a/react/src/util/coinHelper.js +++ b/react/src/util/coinHelper.js @@ -402,4 +402,31 @@ export function getModeInfo(mode) { tip: modetip, color: modecolor, }; +} + +export function coindList() { + const _coins = [ + 'KMD', + 'CHIPS', + 'BET', + 'BOTS', + 'CEAL', + 'COQUI', + 'CRYPTO', + 'HODL', + 'DEX', + 'JUMBLR', + 'KV', + 'MGW', + 'MVP', + 'MNZ', + 'PANGEA', + 'REVS', + 'SHARK', + 'MESH', + 'SUPERNET', + 'WLC', + ]; + + return _coins; } \ No newline at end of file diff --git a/react/src/util/zcashParams.js b/react/src/util/zcashParams.js new file mode 100644 index 000000000..7f41f867f --- /dev/null +++ b/react/src/util/zcashParams.js @@ -0,0 +1,29 @@ +import { translate } from '../translate/translate'; + +export function zcashParamsCheckErrors(zcashParamsExist) { + let _errors; + + if (zcashParamsExist.errors) { + _errors = [translate('KMD_NATIVE.ZCASH_PARAMS_MISSING'), '']; + + if (!zcashParamsExist.rootDir) { + _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_ROOT_DIR')); + } + if (!zcashParamsExist.provingKey) { + _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_PROVING_KEY')); + } + if (!zcashParamsExist.verifyingKey) { + _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_VERIFYING_KEY')); + } + if (!zcashParamsExist.provingKeySize && + zcashParamsExist.provingKey) { + _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_PROVING_KEY_SIZE')); + } + if (!zcashParamsExist.verifyingKeySize && + zcashParamsExist.verifyingKey) { + _errors.push(translate('KMD_NATIVE.ZCASH_PARAMS_MISSING_VERIFYING_KEY_SIZE')); + } + } + + return _errors; +} \ No newline at end of file