From 3385c38e9f1add97885b3ec32c45632c120c574f Mon Sep 17 00:00:00 2001 From: Miika Turunen Date: Tue, 26 Sep 2017 00:48:38 +0300 Subject: [PATCH 01/47] AGP-299, Link to explorer --- .../dashboard/walletsTxInfo/walletsTxInfo.js | 25 +++++++++++++++++++ .../walletsTxInfo/walletsTxInfo.render.js | 5 ++++ react/src/translate/en.js | 3 ++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js index 06aecf3a0..d5669128b 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js @@ -1,5 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; +import { translate } from '../../../translate/translate'; import { sortByDate } from '../../../util/sort'; import { toggleDashboardTxInfoModal } from '../../../actions/actionCreators'; import Store from '../../../store'; @@ -34,6 +35,29 @@ class WalletsTxInfo extends React.Component { } } + openExplorerWindow(txid) { + const url = 'http://' + this.props.ActiveCoin.coin + '.explorer.supernet.org/tx/' + txid; + const remote = window.require('electron').remote; + const BrowserWindow = remote.BrowserWindow; + + const externalWindow = new BrowserWindow({ + width: 1280, + height: 800, + title: `${translate('INDEX.LOADING')}...`, + icon: remote.getCurrentWindow().iguanaIcon, + webPreferences: { + "nodeIntegration": false + }, + }); + + externalWindow.loadURL(url); + externalWindow.webContents.on('did-finish-load', function() { + setTimeout(function() { + externalWindow.show(); + }, 40); + }); + } + render() { if (this.props && this.props.ActiveCoin.showTransactionInfo && @@ -52,6 +76,7 @@ const mapStateToProps = (state) => { return { ActiveCoin: { mode: state.ActiveCoin.mode, + coin: state.ActiveCoin.coin, txhistory: state.ActiveCoin.txhistory, showTransactionInfo: state.ActiveCoin.showTransactionInfo, activeSection: state.ActiveCoin.activeSection, diff --git a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js index d04459e23..e6280c025 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js @@ -34,6 +34,11 @@ const WalletsTxInfoRender = function(txInfo) { Raw info +
  • + this.openExplorerWindow(txInfo.txid) }> + { translate('INDEX.OPEN_TRANSACTION_IN_EPLORER') } + +
  • diff --git a/react/src/translate/en.js b/react/src/translate/en.js index 5bceaa46d..c384f9ea1 100644 --- a/react/src/translate/en.js +++ b/react/src/translate/en.js @@ -293,7 +293,8 @@ export const _lang = { 'CREATE_INVOICE_QR': 'Create Invoice QR Code', 'QR_CONTENT': 'QR Content', 'CHOOSE_RECEIVING_ADDRESS': 'Choose Address', - 'SAVE_AS_IMAGE': 'Save as Image' + 'SAVE_AS_IMAGE': 'Save as Image', + 'OPEN_TRANSACTION_IN_EPLORER': 'Open transaction in Explorer', }, 'ATOMIC': { 'RAW_OUTPUT': 'Raw Output', From 05e1016b6e1a59aa4eac144a36d7d1c109501c99 Mon Sep 17 00:00:00 2001 From: Miika Turunen Date: Tue, 26 Sep 2017 23:26:10 +0300 Subject: [PATCH 02/47] AGP-299, Add coin name to explorer link --- .../components/dashboard/walletsTxInfo/walletsTxInfo.render.js | 2 +- react/src/translate/en.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js index e6280c025..968a899c2 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js @@ -36,7 +36,7 @@ const WalletsTxInfoRender = function(txInfo) {
  • this.openExplorerWindow(txInfo.txid) }> - { translate('INDEX.OPEN_TRANSACTION_IN_EPLORER') } + { translate('INDEX.OPEN_TRANSACTION_IN_EPLORER', this.props.ActiveCoin.coin) }
  • diff --git a/react/src/translate/en.js b/react/src/translate/en.js index c384f9ea1..949cb38a5 100644 --- a/react/src/translate/en.js +++ b/react/src/translate/en.js @@ -294,7 +294,7 @@ export const _lang = { 'QR_CONTENT': 'QR Content', 'CHOOSE_RECEIVING_ADDRESS': 'Choose Address', 'SAVE_AS_IMAGE': 'Save as Image', - 'OPEN_TRANSACTION_IN_EPLORER': 'Open transaction in Explorer', + 'OPEN_TRANSACTION_IN_EPLORER': 'Open transaction in @template@ Explorer', }, 'ATOMIC': { 'RAW_OUTPUT': 'Raw Output', From 2fdc565c8e894f42da2c3218c5e4558c991c03f6 Mon Sep 17 00:00:00 2001 From: Miika Turunen Date: Thu, 28 Sep 2017 23:27:35 +0300 Subject: [PATCH 03/47] AGP-299, button placement change --- .../dashboard/walletsTxInfo/walletsTxInfo.render.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js index 968a899c2..e2ab0d722 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js @@ -34,11 +34,6 @@ const WalletsTxInfoRender = function(txInfo) { Raw info -
  • - this.openExplorerWindow(txInfo.txid) }> - { translate('INDEX.OPEN_TRANSACTION_IN_EPLORER', this.props.ActiveCoin.coin) } - -
  • @@ -159,6 +154,12 @@ const WalletsTxInfoRender = function(txInfo) {
    +
    -
    +
    -
    - -
    - { translate('INDEX.TOGGLE_ZERO_ADDRESSES') } -
    + { this.props.mode !== 'spv' && +
    + +
    + { translate('INDEX.TOGGLE_ZERO_ADDRESSES') } +
    +
    + }
    } @@ -91,6 +95,7 @@ export const ReceiveCoinRender = function() {
    + { this.props.mode !== 'spv' &&
    @@ -111,6 +116,7 @@ export const ReceiveCoinRender = function() {
    + }

    { translate('INDEX.RECEIVING_ADDRESS') }

    diff --git a/react/src/components/dashboard/sendCoin/sendCoin.js b/react/src/components/dashboard/sendCoin/sendCoin.js index 135454a75..18deae2c7 100644 --- a/react/src/components/dashboard/sendCoin/sendCoin.js +++ b/react/src/components/dashboard/sendCoin/sendCoin.js @@ -7,7 +7,8 @@ import { triggerToaster, sendNativeTx, getKMDOPID, - clearLastSendToResponseState + clearLastSendToResponseState, + shepherdElectrumSend } from '../../../actions/actionCreators'; import Store from '../../../store'; import { @@ -224,7 +225,7 @@ class SendCoin extends React.Component { ); } else { return ( - { translate('INDEX.T_FUNDS') } + { this.props.ActiveCoin.mode === 'spv' ? `[ ${this.props.ActiveCoin.balance.balance} ${this.props.ActiveCoin.coin} ] ${this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub}` : translate('INDEX.T_FUNDS') } ); } } @@ -388,22 +389,38 @@ class SendCoin extends React.Component { return; } - Store.dispatch( - sendNativeTx( - this.props.ActiveCoin.coin, - this.state - ) - ); + if (this.props.ActiveCoin.mode === 'native') { + Store.dispatch( + sendNativeTx( + this.props.ActiveCoin.coin, + this.state + ) + ); - if (this.state.addressType === 'private') { - setTimeout(() => { + if (this.state.addressType === 'private') { + setTimeout(() => { + Store.dispatch( + getKMDOPID( + null, + this.props.ActiveCoin.coin + ) + ); + }, 1000); + } + } else if (this.props.ActiveCoin.mode === 'spv') { + console.warn('spv send'); + console.warn(this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub); + // no op + if (this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub) { Store.dispatch( - getKMDOPID( - null, - this.props.ActiveCoin.coin + shepherdElectrumSend( + this.props.ActiveCoin.coin, + this.state.amount * 100000000, + this.state.sendTo, + this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub ) ); - }, 1000); + } } } @@ -411,6 +428,30 @@ class SendCoin extends React.Component { validateSendFormData() { let valid = true; + if (this.props.ActiveCoin.mode === 'spv') { + const _amount = this.state.amount; + const _amountSats = this.state.amount * 100000000; + const _balanceSats = this.props.ActiveCoin.balance.sats; + const _fee = this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].txfee; + + if (_amountSats > (Number(_balanceSats) + Number(_fee))) { + Store.dispatch( + triggerToaster( + translate('SEND.INSUFFICIENT_FUNDS'), + translate('TOASTR.WALLET_NOTIFICATION'), + 'error' + ) + ); + valid = false; + } + + console.warn('send val ' + this.state.amount); + console.warn('send val sats ' + (this.state.amount * 100000000)); + + console.warn(this.props.ActiveCoin.balance.sats); + console.warn(this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].txfee); + } + if (!this.state.sendTo || this.state.sendTo.length < 34) { Store.dispatch( @@ -504,6 +545,7 @@ const mapStateToProps = (state, props) => { activeSection: state.ActiveCoin.activeSection, lastSendToResponse: state.ActiveCoin.lastSendToResponse, }, + Dashboard: state.Dashboard, }; if (props && diff --git a/react/src/components/dashboard/sendCoin/sendCoin.render.js b/react/src/components/dashboard/sendCoin/sendCoin.render.js index 9f17240e2..c071f2b2e 100644 --- a/react/src/components/dashboard/sendCoin/sendCoin.render.js +++ b/react/src/components/dashboard/sendCoin/sendCoin.render.js @@ -21,7 +21,7 @@ export const AddressListRender = function() { className="selected" onClick={ () => this.updateAddressSelection(null, 'public', null) }> - { translate('INDEX.T_FUNDS') } + { this.props.ActiveCoin.mode === 'spv' ? `[ ${this.props.ActiveCoin.balance.balance} ${this.props.ActiveCoin.coin} ] ${this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub}` : translate('INDEX.T_FUNDS') } @@ -81,7 +81,7 @@ export const _SendFormRender = function() { placeholder="0.000" autoComplete="off" /> -
    +
    - +
    Transaction ID{ this.state.lastSendToResponse }{ this.props.ActiveCoin.mode === 'spv' ? (this.state.lastSendToResponse && this.state.lastSendToResponse.txid ? this.state.lastSendToResponse.txid : 'error') : this.state.lastSendToResponse }
    diff --git a/react/src/components/dashboard/walletsBalance/walletsBalance.js b/react/src/components/dashboard/walletsBalance/walletsBalance.js index e030d9d19..964a9d4ac 100755 --- a/react/src/components/dashboard/walletsBalance/walletsBalance.js +++ b/react/src/components/dashboard/walletsBalance/walletsBalance.js @@ -1,7 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; import { translate } from '../../../translate/translate'; -import { getDashboardUpdate } from '../../../actions/actionCreators'; +import { + getDashboardUpdate, + shepherdElectrumBalance, +} from '../../../actions/actionCreators'; import Store from '../../../store'; import WalletsBalanceRender from './walletsBalance.render'; @@ -39,7 +42,11 @@ class WalletsBalance extends React.Component { } refreshBalance() { - Store.dispatch(getDashboardUpdate(this.props.ActiveCoin.coin)); + if (this.props.ActiveCoin.mode === 'native') { + Store.dispatch(getDashboardUpdate(this.props.ActiveCoin.coin)); + } else if (this.props.ActiveCoin.mode === 'spv') { + Store.dispatch(shepherdElectrumBalance(this.props.ActiveCoin.coin, this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub)); + } } renderBalance(type) { @@ -70,6 +77,8 @@ class WalletsBalance extends React.Component { this.props.ActiveCoin.balance.transparent) { _balance = this.props.ActiveCoin.balance.transparent; } + } else if (_mode === 'spv' && this.props.ActiveCoin.balance.balance) { + _balance = this.props.ActiveCoin.balance.balance; } return _balance; @@ -118,9 +127,7 @@ const mapStateToProps = (state) => { activeAddress: state.ActiveCoin.activeAddress, progress: state.ActiveCoin.progress, }, - Dashboard: { - activeHandle: state.Dashboard.activeHandle, - }, + Dashboard: state.Dashboard, }; }; diff --git a/react/src/components/dashboard/walletsBalance/walletsBalance.render.js b/react/src/components/dashboard/walletsBalance/walletsBalance.render.js index 290f33156..7eb3cf8df 100644 --- a/react/src/components/dashboard/walletsBalance/walletsBalance.render.js +++ b/react/src/components/dashboard/walletsBalance/walletsBalance.render.js @@ -9,7 +9,7 @@ const WalletsBalanceRender = function() { id="wallet-widgets" className="wallet-widgets">
    -
    +
    - - { this.props.ActiveCoin.coin === 'CHIPS' ? translate('INDEX.BALANCE') : translate('INDEX.TRANSPARENT_BALANCE') } + { this.props.ActiveCoin.coin !== 'CHIPS' && this.props.ActiveCoin.mode !== 'spv' && + + } + { this.props.ActiveCoin.coin === 'CHIPS' || this.props.ActiveCoin.mode === 'spv' ? translate('INDEX.BALANCE') : translate('INDEX.TRANSPARENT_BALANCE') }
    -
    +
    @@ -50,7 +52,7 @@ const WalletsBalanceRender = function() {
    -
    +
    @@ -70,7 +72,7 @@ const WalletsBalanceRender = function() {
    -
    +
    diff --git a/react/src/components/dashboard/walletsData/walletsData.js b/react/src/components/dashboard/walletsData/walletsData.js index d3ed1da5f..904a69d6c 100644 --- a/react/src/components/dashboard/walletsData/walletsData.js +++ b/react/src/components/dashboard/walletsData/walletsData.js @@ -8,6 +8,7 @@ import { toggleDashboardTxInfoModal, changeActiveAddress, getDashboardUpdate, + shepherdElectrumTransactions, } from '../../../actions/actionCreators'; import Store from '../../../store'; import { @@ -182,7 +183,11 @@ class WalletsData extends React.Component { } refreshTxHistory() { - Store.dispatch(getDashboardUpdate(this.props.ActiveCoin.coin)); + if (this.props.ActiveCoin.mode === 'native') { + Store.dispatch(getDashboardUpdate(this.props.ActiveCoin.coin)); + } else if (this.props.ActiveCoin.mode === 'spv') { + Store.dispatch(shepherdElectrumTransactions(this.props.ActiveCoin.coin, this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub)); + } } toggleTxInfoModal(display, txIndex) { @@ -208,9 +213,7 @@ class WalletsData extends React.Component { this.props.ActiveCoin.txhistory !== 'loading' && this.props.ActiveCoin.txhistory !== 'no data' && this.props.ActiveCoin.txhistory.length) { - _stateChange = Object.assign({}, _stateChange, { - isExplorerData: this.props.ActiveCoin.txhistory[0].source ? true : false, itemsList: this.props.ActiveCoin.txhistory, filteredItemsList: this.filterTransactions(this.props.ActiveCoin.txhistory, this.state.searchTerm), txhistory: this.props.ActiveCoin.txhistory, @@ -419,7 +422,7 @@ class WalletsData extends React.Component { if (this.props && this.props.ActiveCoin && this.props.ActiveCoin.coin && - this.props.ActiveCoin.mode === 'native' && + //this.props.ActiveCoin.mode === 'native' && this.props.ActiveCoin.activeSection === 'default' ) { return WalletsDataRender.call(this); @@ -449,6 +452,7 @@ const mapStateToProps = (state) => { Main: { coins: state.Main.coins, }, + Dashboard: state.Dashboard, }; }; diff --git a/react/src/components/dashboard/walletsInfo/walletsInfo.js b/react/src/components/dashboard/walletsInfo/walletsInfo.js index 1b43678e3..38d950394 100644 --- a/react/src/components/dashboard/walletsInfo/walletsInfo.js +++ b/react/src/components/dashboard/walletsInfo/walletsInfo.js @@ -17,7 +17,7 @@ class WalletsInfo extends React.Component { render() { if (this.props && this.props.ActiveCoin && - this.props.ActiveCoin.progress && + (this.props.ActiveCoin.progress || this.props.Dashboard.electrumCoins) && this.props.ActiveCoin.activeSection === 'settings') { return WalletsNativeInfoRender.call(this); } @@ -28,11 +28,8 @@ class WalletsInfo extends React.Component { const mapStateToProps = (state) => { return { - ActiveCoin: { - coin: state.ActiveCoin.coin, - activeSection: state.ActiveCoin.activeSection, - progress: state.ActiveCoin.progress, - }, + ActiveCoin: state.ActiveCoin, + Dashboard: state.Dashboard, }; }; diff --git a/react/src/components/dashboard/walletsInfo/walletsInfo.render.js b/react/src/components/dashboard/walletsInfo/walletsInfo.render.js index b4b6d2923..0ed75c4fa 100644 --- a/react/src/components/dashboard/walletsInfo/walletsInfo.render.js +++ b/react/src/components/dashboard/walletsInfo/walletsInfo.render.js @@ -3,159 +3,227 @@ import { translate } from '../../../translate/translate'; import ClaimInterestModal from '../claimInterestModal/claimInterestModal'; const WalletsInfoRender = function() { - const _progress = this.props.ActiveCoin.progress; + if (this.props.ActiveCoin.mode === 'native') { + const _progress = this.props.ActiveCoin.progress; - return ( -
    -
    -
    -
    -

    { translate('INDEX.WALLET_INFO') }

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - -
    { translate('INDEX.WALLET_VERSION') } - { _progress.walletversion } -
    { translate('INDEX.BALANCE') } - { _progress.balance } -
    { translate('INDEX.UNCONFIRMED_BALANCE') }
    { translate('INDEX.IMMATURE_BALANCE') }
    { translate('INDEX.TOTAL_TX_COUNT') }
    + return ( +
    +
    +
    +
    +

    { translate('INDEX.WALLET_INFO') }

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    { translate('INDEX.WALLET_VERSION') } + { _progress.walletversion } +
    { translate('INDEX.BALANCE') } + { _progress.balance } +
    { translate('INDEX.UNCONFIRMED_BALANCE') }
    { translate('INDEX.IMMATURE_BALANCE') }
    { translate('INDEX.TOTAL_TX_COUNT') }
    +
    + { this.props.ActiveCoin.coin === 'KMD' && +
    + + +
    + }
    - { this.props.ActiveCoin.coin === 'KMD' && -
    - - + +
    +
    +
    +

    + Komodo { translate('INDEX.INFO') } +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    { translate('INDEX.VERSION') } + { _progress.KMDversion } +
    { translate('INDEX.PROTOCOL_VERSION') } + { _progress.protocolversion } +
    { translate('INDEX.NOTARIZED') } + { _progress.notarized } +
    + { translate('INDEX.NOTARIZED') } { translate('INDEX.HASH') } + + { _progress.notarizedhash ? + _progress.notarizedhash.substring( + 0, + Math.floor(_progress.notarizedhash.length / 2) + ) + + '\t' + + _progress.notarizedhash.substring( + Math.floor(_progress.notarizedhash.length / 2), + _progress.notarizedhash.length + ) + : '' + } +
    + { translate('INDEX.NOTARIZED') } BTC +
    { translate('INDEX.BLOCKS') } + { _progress.blocks } +
    { translate('INDEX.CONNECTIONS') } + { _progress.connections } +
    { translate('INDEX.DIFFICULTY') } + { _progress.difficulty } +
    Testnet + { _progress.testnet } +
    { translate('INDEX.PAY_TX_FEE') } + { _progress.paytxfee } +
    { translate('INDEX.RELAY_FEE') } + { _progress.relayfee } +
    { translate('INDEX.ERRORS') } + { _progress.errors } +
    +
    - } +
    + ); + } else if (this.props.ActiveCoin.mode === 'spv') { + const _balance = this.props.ActiveCoin.balance; + const _server = this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin]; -
    -
    -
    -

    - Komodo { translate('INDEX.INFO') } -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    { translate('INDEX.VERSION') } - { _progress.KMDversion } -
    { translate('INDEX.PROTOCOL_VERSION') } - { _progress.protocolversion } -
    { translate('INDEX.NOTARIZED') } - { _progress.notarized } -
    - { translate('INDEX.NOTARIZED') } { translate('INDEX.HASH') } - - { _progress.notarizedhash ? - _progress.notarizedhash.substring( - 0, - Math.floor(_progress.notarizedhash.length / 2) - ) + - '\t' + - _progress.notarizedhash.substring( - Math.floor(_progress.notarizedhash.length / 2), - _progress.notarizedhash.length - ) - : '' - } -
    - { translate('INDEX.NOTARIZED') } BTC -
    { translate('INDEX.BLOCKS') } - { _progress.blocks } -
    { translate('INDEX.CONNECTIONS') } - { _progress.connections } -
    { translate('INDEX.DIFFICULTY') } - { _progress.difficulty } -
    Testnet - { _progress.testnet } -
    { translate('INDEX.PAY_TX_FEE') } - { _progress.paytxfee } -
    { translate('INDEX.RELAY_FEE') } - { _progress.relayfee } -
    { translate('INDEX.ERRORS') } - { _progress.errors } -
    + return ( +
    +
    +
    +
    +

    { translate('INDEX.WALLET_INFO') }

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Server IP + { _server.server.ip } +
    Server Port + { _server.server.port } +
    Connection type + TCP +
    { translate('INDEX.PAY_TX_FEE') } + { _server.txfee } +
    { translate('INDEX.BALANCE') } + { _balance.balance } +
    { translate('INDEX.UNCONFIRMED_BALANCE') } + { _balance.uncomfirmed } +
    +
    + { this.props.ActiveCoin.coin === 'KMD' && this.props.ActiveCoin.mode !== 'spv' && +
    + + +
    + }
    -
    - ); + ); + } }; export default WalletsInfoRender; \ No newline at end of file diff --git a/react/src/components/dashboard/walletsNative/walletsNative.js b/react/src/components/dashboard/walletsNative/walletsNative.js index 718b7319d..f4894e88a 100644 --- a/react/src/components/dashboard/walletsNative/walletsNative.js +++ b/react/src/components/dashboard/walletsNative/walletsNative.js @@ -66,11 +66,15 @@ class WalletsNative extends React.Component { } render() { - if (this.isActiveCoinModeNative()) { + //if (this.isActiveCoinModeNative()) { + if (this.props.ActiveCoin && this.props.ActiveCoin.mode) { return WalletsNativeRender.call(this); } else { return null; } + /*} else { + return null; + }*/ } } diff --git a/react/src/components/dashboard/walletsNative/walletsNative.render.js b/react/src/components/dashboard/walletsNative/walletsNative.render.js index 5aa987f2c..b3383ab72 100644 --- a/react/src/components/dashboard/walletsNative/walletsNative.render.js +++ b/react/src/components/dashboard/walletsNative/walletsNative.render.js @@ -28,7 +28,9 @@ const WalletsNativeRender = function() {
    - + { this.props.ActiveCoin.mode !== 'spv' && + + }
    diff --git a/react/src/components/dashboard/walletsNav/walletsNav.js b/react/src/components/dashboard/walletsNav/walletsNav.js index 08bc756e8..dd8340e80 100644 --- a/react/src/components/dashboard/walletsNav/walletsNav.js +++ b/react/src/components/dashboard/walletsNav/walletsNav.js @@ -32,8 +32,11 @@ class WalletsNav extends React.Component { const _mode = this.props.ActiveCoin.mode; if (this.props.ActiveCoin.balance && - this.props.ActiveCoin.balance.total) { + this.props.ActiveCoin.balance.total && + _mode === 'native') { _balance = this.props.ActiveCoin.balance.total; + } else if (_mode === 'spv' && this.props.ActiveCoin.balance.balance) { + _balance = this.props.ActiveCoin.balance.balance; } return _balance; @@ -49,33 +52,33 @@ class WalletsNav extends React.Component { // TODO: merge toggle func into one toggleSendReceiveCoinForms() { - if (this.props.ActiveCoin.mode === 'native') { + //if (this.props.ActiveCoin.mode === 'native') { Store.dispatch( toggleDashboardActiveSection( this.props.ActiveCoin.activeSection === 'settings' ? 'default' : 'settings' ) ); - } + //} } toggleSendCoinForm(display) { - if (this.props.ActiveCoin.mode === 'native') { + //if (this.props.ActiveCoin.mode === 'native') { Store.dispatch( toggleDashboardActiveSection( this.props.ActiveCoin.activeSection === 'send' ? 'default' : 'send' ) ); - } + //} } toggleReceiveCoinForm(display) { - if (this.props.ActiveCoin.mode === 'native') { + //if (this.props.ActiveCoin.mode === 'native') { Store.dispatch( toggleDashboardActiveSection( this.props.ActiveCoin.activeSection === 'receive' ? 'default' : 'receive' ) ); - } + //} } render() { diff --git a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js index d04459e23..3b992f151 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js @@ -90,19 +90,19 @@ const WalletsTxInfoRender = function(txInfo) { walletconflicts - { txInfo.walletconflicts.length } + { txInfo.walletconflicts ? txInfo.walletconflicts.length : '' } time - { secondsToString(txInfo.time) } + { txInfo.time ? secondsToString(txInfo.time) : '' } timereceived - { secondsToString(txInfo.timereceived) } + { txInfo.timereceived ? secondsToString(txInfo.timereceived) : '' } diff --git a/react/src/components/login/login.js b/react/src/components/login/login.js index 1b888aa71..6b8b23eea 100644 --- a/react/src/components/login/login.js +++ b/react/src/components/login/login.js @@ -2,16 +2,32 @@ import React from 'react'; import { connect } from 'react-redux'; import { toggleAddcoinModal, + iguanaWalletPassphrase, + shepherdElectrumAuth, + shepherdElectrumCoins, + //iguanaActiveHandle, startInterval, getDexCoins, + createNewWallet, triggerToaster, toggleLoginSettingsModal } from '../../actions/actionCreators'; import Config from '../../config'; import Store from '../../store'; import { PassPhraseGenerator } from '../../util/crypto/passphrasegenerator'; +import SwallModalRender from './swall-modal.render'; import LoginRender from './login.render'; import { translate } from '../../translate/translate'; +import { + encryptPassphrase, + loadPinList, + loginWithPin +} from '../../actions/actions/pin'; + +const IGUNA_ACTIVE_HANDLE_TIMEOUT = 3000; +const IGUNA_ACTIVE_COINS_TIMEOUT = 10000; + +// TODO: remove duplicate activehandle and activecoins calls class Login extends React.Component { constructor() { @@ -19,11 +35,44 @@ class Login extends React.Component { this.state = { display: false, activeLoginSection: 'activateCoin', + loginPassphrase: '', + seedInputVisibility: false, + loginPassPhraseSeedType: null, + bitsOption: 256, + randomSeed: PassPhraseGenerator.generatePassPhrase(256), + randomSeedConfirm: '', + isSeedConfirmError: false, + isSeedBlank: false, + displaySeedBackupModal: false, + customWalletSeed: false, + isCustomSeedWeak: false, + nativeOnly: Config.iguanaLessMode, + trimPassphraseTimer: null, displayLoginSettingsDropdown: false, displayLoginSettingsDropdownSection: null, + shouldEncryptSeed: false, + encryptKey: '', + pubKey: '', + decryptKey: '', + selectedPin: '', + isExperimentalOn: false, }; this.toggleActivateCoinForm = this.toggleActivateCoinForm.bind(this); + this.updateRegisterConfirmPassPhraseInput = this.updateRegisterConfirmPassPhraseInput.bind(this); + this.updateLoginPassPhraseInput = this.updateLoginPassPhraseInput.bind(this); + this.loginSeed = this.loginSeed.bind(this); + this.toggleSeedInputVisibility = this.toggleSeedInputVisibility.bind(this); + this.handleRegisterWallet = this.handleRegisterWallet.bind(this); + this.openSyncOnlyModal = this.openSyncOnlyModal.bind(this); + this.toggleSeedBackupModal = this.toggleSeedBackupModal.bind(this); + this.copyPassPhraseToClipboard = this.copyPassPhraseToClipboard.bind(this); + this.execWalletCreate = this.execWalletCreate.bind(this); + this.resizeLoginTextarea = this.resizeLoginTextarea.bind(this); this.toggleLoginSettingsDropdown = this.toggleLoginSettingsDropdown.bind(this); + this.updateEncryptKey = this.updateEncryptKey.bind(this); + this.updatePubKey = this.updatePubKey.bind(this); + this.updateDecryptKey = this.updateDecryptKey.bind(this); + this.loadPinList = this.loadPinList.bind(this); } // the setInterval handler for 'activeCoins' @@ -38,38 +87,391 @@ class Login extends React.Component { }); } + isCustomWalletSeed() { + return this.state.customWalletSeed; + } + + toggleCustomWalletSeed() { + this.setState({ + customWalletSeed: !this.state.customWalletSeed + }, () => { + // if customWalletSeed is set to false, regenerate the seed + if (!this.state.customWalletSeed) { + this.setState({ + randomSeed: PassPhraseGenerator.generatePassPhrase(this.state.bitsOption), + isSeedConfirmError: false, + isSeedBlank: false, + }); + } else { + // if customWalletSeed is set to true, reset to seed to an empty string + this.setState({ + randomSeed: '', + randomSeedConfirm: '', + }); + } + }); + } + + shouldEncryptSeed() { + return this.state.shouldEncryptSeed; + } + + toggleShouldEncryptSeed() { + this.setState({ + shouldEncryptSeed: !this.state.shouldEncryptSeed + }); + } + + updateEncryptKey(e) { + this.setState({ + encryptKey: e.target.value + }); + } + + updatePubKey(e) { + this.setState({ + pubKey: e.target.value + }); + } + + updateDecryptKey(e) { + this.setState({ + decryptKey: e.target.value + }); + } + + openSyncOnlyModal() { + Store.dispatch(getSyncOnlyForks()); + + const _iguanaActiveHandle = setInterval(() => { + Store.dispatch(getSyncOnlyForks()); + }, IGUNA_ACTIVE_HANDLE_TIMEOUT); + Store.dispatch( + startInterval( + 'syncOnly', + _iguanaActiveHandle + ) + ); + + Store.dispatch(toggleSyncOnlyModal(true)); + this.setState({ + displayLoginSettingsDropdown: false, + }); + } + + componentDidMount() { + // Store.dispatch(iguanaActiveHandle(true)); + // this.loadPinList(); + + let appConfig; + + try { + appConfig = window.require('electron').remote.getCurrentWindow().appConfig; + } catch (e) {} + + this.setState({ + isExperimentalOn: appConfig.experimentalFeatures, + }); + } + + toggleSeedInputVisibility() { + this.setState({ + seedInputVisibility: !this.state.seedInputVisibility, + }); + + this.resizeLoginTextarea(); + } + + generateNewSeed(bits) { + this.setState(Object.assign({}, this.state, { + randomSeed: PassPhraseGenerator.generatePassPhrase(bits), + bitsOption: bits, + isSeedBlank: false, + })); + } + + toggleLoginSettingsDropdown() { + this.setState(Object.assign({}, this.state, { + displayLoginSettingsDropdown: !this.state.displayLoginSettingsDropdown, + })); + } + componentWillReceiveProps(props) { + if (props.Login.pinList === 'no pins') { + props.Login.pinList = []; + } if (props && props.Main && - props.Main.coins && - props.Main.coins.native && - props.Main.coins.native.length) { - this.setState({ - display: false, - }); - } else { + props.Main.isLoggedIn) { + if (props.Main.total === 0) { + this.setState({ + activeLoginSection: 'activateCoin', + display: true, + }); + } else { + this.setState({ + display: false, + }); + } + } + + if (props && + props.Main && + !props.Main.isLoggedIn) { + console.warn('display login'); this.setState({ display: true, + activeLoginSection: 'login', }); + + /*if (!this.props.Interval.interval.activeCoins) { + // only start a new 'activeCoins' interval if a previous one doesn't exist + if (!this._iguanaActiveCoins) { + this._iguanaActiveCoins = setInterval(() => { + Store.dispatch(getDexCoins()); + }, IGUNA_ACTIVE_COINS_TIMEOUT); + Store.dispatch(startInterval('activeCoins', this._iguanaActiveCoins)); + } + }*/ + + document.body.className = 'page-login layout-full page-dark'; } - } - toggleLoginSettingsDropdown() { - this.setState(Object.assign({}, this.state, { - displayLoginSettingsDropdown: !this.state.displayLoginSettingsDropdown, - })); + if (this.state.activeLoginSection !== 'signup' && + props && + props.Main && + props.Main.isLoggedIn) { + /*if (props && + props.Main && + props.Main.activeCoins) { + this.setState({ + activeLoginSection: 'login', + }); + } else {*/ + this.setState({ + activeLoginSection: 'activateCoin', + }); + //} + } + + console.warn(this.state); } toggleActivateCoinForm() { Store.dispatch(toggleAddcoinModal(true, false)); } + resizeLoginTextarea() { + // auto-size textarea + setTimeout(() => { + if (this.state.seedInputVisibility) { + document.querySelector('#loginPassphrase').style.height = '1px'; + document.querySelector('#loginPassphrase').style.height = `${(15 + document.querySelector('#loginPassphrase').scrollHeight)}px`; + } + }, 100); + } + + updateLoginPassPhraseInput(e) { + // remove any empty chars from the start/end of the string + const newValue = e.target.value; + + clearTimeout(this.state.trimPassphraseTimer); + + const _trimPassphraseTimer = setTimeout(() => { + this.setState({ + loginPassphrase: newValue ? newValue.trim() : '', // hardcoded field name + loginPassPhraseSeedType: this.getLoginPassPhraseSeedType(newValue), + }); + }, 2000); + + this.resizeLoginTextarea(); + + this.setState({ + trimPassphraseTimer: _trimPassphraseTimer, + [e.target.name]: newValue, + loginPassPhraseSeedType: this.getLoginPassPhraseSeedType(newValue), + }); + } + + updateRegisterConfirmPassPhraseInput(e) { + this.setState({ + [e.target.name]: e.target.value, + isSeedConfirmError: false, + isSeedBlank: this.isBlank(e.target.value), + }); + } + + updateWalletSeed(e) { + this.setState({ + randomSeed: e.target.value, + isSeedConfirmError: false, + isSeedBlank: this.isBlank(e.target.value), + }); + } + + loginSeed() { + // reset the login pass phrase values so that when the user logs out, the values are clear + this.setState({ + loginPassphrase: null, + loginPassPhraseSeedType: null, + }); + + if (this.state.shouldEncryptSeed) { + Store.dispatch(encryptPassphrase(this.state.loginPassphrase, this.state.encryptKey, this.state.pubKey)); + } + + if (this.state.selectedPin) { + Store.dispatch(loginWithPin(this.state.decryptKey, this.state.selectedPin)); + } else { + /*Store.dispatch( + iguanaWalletPassphrase(this.state.loginPassphrase) + );*/ + Store.dispatch( + shepherdElectrumAuth(this.state.loginPassphrase) + ); + Store.dispatch( + shepherdElectrumCoins() + ); + } + } + + loadPinList() { + Store.dispatch(loadPinList()); + } + + updateSelectedPin(e) { + this.setState({ + selectedPin: e.target.value + }); + } + + getLoginPassPhraseSeedType(passPhrase) { + if (!passPhrase) { + return null; + } + + const passPhraseWords = passPhrase.split(' '); + if (!PassPhraseGenerator.arePassPhraseWordsValid(passPhraseWords)) { + return null; + } + + if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 256)) { + return translate('LOGIN.IGUANA_SEED'); + } + + if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 160)) { + return translate('LOGIN.WAVES_SEED'); + } + + if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 128)) { + return translate('LOGIN.NXT_SEED'); + } + + return null; + } + updateActiveLoginSection(name) { + // reset login/create form this.setState({ activeLoginSection: name, + loginPassphrase: null, + loginPassPhraseSeedType: null, + seedInputVisibility: false, + bitsOption: 256, + randomSeed: PassPhraseGenerator.generatePassPhrase(256), + randomSeedConfirm: '', + isSeedConfirmError: false, + isSeedBlank: false, + displaySeedBackupModal: false, + customWalletSeed: false, + isCustomSeedWeak: false, }); } + execWalletCreate() { + Store.dispatch( + createNewWallet( + this.state.randomSeedConfirm, + this.props.Dashboard.activeHandle + ) + ); + + this.setState({ + activeLoginSection: 'activateCoin', + displaySeedBackupModal: false, + isSeedConfirmError: false, + }); + } + + // TODO: disable register btn if seed or seed conf is incorrect + handleRegisterWallet() { + const enteredSeedsMatch = this.state.randomSeed === this.state.randomSeedConfirm; + const isSeedBlank = this.isBlank(this.state.randomSeed); + + // if custom seed check for string strength + // at least 1 letter in upper case + // at least 1 digit + // at least one special char + // min length 10 chars + + const _customSeed = this.state.customWalletSeed ? this.state.randomSeed.match('^(?=.*[A-Z])(?=.*[^<>{}\"/|;:.,~!?@#$%^=&*\\]\\\\()\\[_+]*$)(?=.*[0-9])(?=.*[a-z]).{10,99}$') : false; + + this.setState({ + isCustomSeedWeak: _customSeed === null ? true : false, + isSeedConfirmError: !enteredSeedsMatch ? true : false, + isSeedBlank: isSeedBlank ? true : false, + }); + + if (enteredSeedsMatch && !isSeedBlank && _customSeed !== null) { + this.toggleSeedBackupModal(); + } + } + + isBlank(str) { + return (!str || /^\s*$/.test(str)); + } + + handleKeydown(e) { + this.updateLoginPassPhraseInput(e); + + if (e.key === 'Enter') { + this.loginSeed(); + } + } + + toggleSeedBackupModal() { + this.setState(Object.assign({}, this.state, { + displaySeedBackupModal: !this.state.displaySeedBackupModal, + })); + } + + copyPassPhraseToClipboard() { + const passPhrase = this.state.randomSeed; + const textField = document.createElement('textarea'); + + textField.innerText = passPhrase; + document.body.appendChild(textField); + textField.select(); + document.execCommand('copy'); + textField.remove(); + + Store.dispatch( + triggerToaster( + translate('LOGIN.SEED_SUCCESSFULLY_COPIED'), + translate('LOGIN.SEED_COPIED'), + 'success' + ) + ); + } + + renderSwallModal() { + if (this.state.displaySeedBackupModal) { + return SwallModalRender.call(this); + } + + return null; + } + render() { if ((this.state && this.state.display) || !this.props.Main) { @@ -93,4 +495,4 @@ const mapStateToProps = (state) => { }; }; -export default connect(mapStateToProps)(Login); +export default connect(mapStateToProps)(Login); \ No newline at end of file diff --git a/react/src/components/login/login.render.js b/react/src/components/login/login.render.js index 610d860cd..e6a5af4de 100644 --- a/react/src/components/login/login.render.js +++ b/react/src/components/login/login.render.js @@ -6,6 +6,7 @@ const LoginRender = function () { return (
    + { this.renderSwallModal() }
    +
    +

    + { translate('INDEX.WELCOME_LOGIN') } +

    + { this.props.Login.pinList.length > 0 && + You can login be entering a login seed or by selecting a pin + } +
    + this.handleKeydown(event) } + value={ this.state.loginPassphrase || '' } /> + + + +
    + { this.state.loginPassPhraseSeedType && +
    +
    { this.state.loginPassPhraseSeedType }
    +
    + } + + { this.state.loginPassphrase && +
    +
    + + +
    this.toggleShouldEncryptSeed() }> + { translate('LOGIN.ENCRYPT_SEED') } +
    +
    +
    + +
    +
    + +
    + +
    + +
    +
    +
    + } + + { this.props.Login.pinList.length > 0 && +
    +
    +
    +
    +
    +
    OR
    +
    +
    +
    +
    +
    + } + { this.props.Login.pinList.length > 0 && +
    +
    + +
    +
    + +
    +
    + } + + +
    + + + +
    +
    +

    { translate('INDEX.WELCOME_PLEASE_ADD') } @@ -57,6 +212,131 @@ const LoginRender = function () {

    + +
    +
    +

    + { translate('INDEX.SELECT_SEED_TYPE') }: +

    +
    +
    +
    + + +
    this.toggleCustomWalletSeed() }> + { translate('LOGIN.CUSTOM_WALLET_SEED') } +
    +
    +
    +
    +
    + { !this.isCustomWalletSeed() && +
    +
    +
    this.state.bitsOption !== 256 && this.generateNewSeed(256) }> + + +
    +
    this.state.bitsOption !== 160 && this.generateNewSeed(160) }> + + +
    +
    this.state.bitsOption !== 128 && this.generateNewSeed(128) }> + + +
    +
    +
    + } +
    +
    + +
    + + + + { translate('INDEX.WEAK_SEED') }

    + { translate('INDEX.YOUR_SEED_MUST_CONTAIN') }
    + { translate('INDEX.YOUR_SEED_MUST_CONTAIN1') }
    + { translate('INDEX.YOUR_SEED_MUST_CONTAIN2') }
    + { translate('INDEX.YOUR_SEED_MUST_CONTAIN3') }
    + { translate('INDEX.YOUR_SEED_MUST_CONTAIN4') } +
    + +
    +
    + + + { translate('LOGIN.MUST_ENTER_SEED') }. + + + { translate('LOGIN.ENTER_VALUE_AGAIN') }. + + +
    + +
    + +
    +
    +
    diff --git a/react/src/components/login/login.scss b/react/src/components/login/login.scss index 7d01a0036..3a04ad0a2 100644 --- a/react/src/components/login/login.scss +++ b/react/src/components/login/login.scss @@ -6,6 +6,38 @@ display: block; } +.swal2-modal-container { + display: block; + width: 500px; + padding: 20px; + margin-left: -250px; + background: rgb(255, 255, 255); + margin-top: -230px; +} + +button { + &.copy-floating-label { + position: absolute; + font-weight: 500; + font-size: 14px; + top: -22px; + right: 0; + background-color: #757575; + border: 0; + cursor: pointer; + z-index: 1000; + } + &.swal2-confirm-container { + background-color: rgb(48, 133, 214); + border-left-color: rgb(48, 133, 214); + border-right-color: rgb(48, 133, 214); + } + &.swal2-cancel-container { + display: inline-block; + background-color: rgb(221, 51, 51); + } +} + .vertical-padding-20 { padding-top: 20px; padding-bottom: 20px; @@ -16,6 +48,10 @@ padding-right: 0; } +.browser-usage-container { + color: #424242; +} + .width-540 { width: 540px; } @@ -25,11 +61,73 @@ margin-bottom: 30px; } +textarea { + &.height-100 { + height: 100px; + } +} + +.seed-type-block { + position: absolute; + left: 100%; + min-width: 50px; + + .placeholder-label { + border-radius: 4px; + padding: 3px; + background: #61BD4F; + color: #fff; + } +} + +#loginPassphrase, +#wifkeysPassphraseTextarea, +input[type="password"] { + width: calc(100% - 35px); +} +#wifkeysPassphraseTextarea, +#loginPassphrase { + overflow: hidden; + margin-top: 5px; +} + +.seed-tooltip { + .tooltiptext { + width: 230px; + padding: 10px 20px; + background-color: black; + color: #fff; + text-align: left; + border-radius: 6px; + position: absolute; + z-index: 1; + top: -5px; + left: 105%; + + &::after { + content: ''; + position: absolute; + top: 50%; + right: 100%; + margin-top: -5px; + border-width: 5px; + border-style: solid; + border-color: transparent black transparent transparent; + } + } +} + .auto-side-margin { margin-left: auto; margin-right: auto; } +.register-form { + .floating-label { + font-size: 13px; + } +} + .login-settings-dropdown { font-size: 16px; diff --git a/react/src/components/login/swall-modal.render.js b/react/src/components/login/swall-modal.render.js new file mode 100644 index 000000000..27e47b8ab --- /dev/null +++ b/react/src/components/login/swall-modal.render.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { translate } from '../../translate/translate'; + +const SwallModalRender = function() { + return ( +
    +
    +
    +
    !
    +

    { translate('LOGIN.SAVED_WALLET_SEED') }

    +
    { translate('LOGIN.SEED_MAKE_SURE_BACKUP') }
    +
    + + +
    +
    + ); +}; + +export default SwallModalRender; \ No newline at end of file diff --git a/react/src/components/main/main.js b/react/src/components/main/main.js index 8c94732bf..b0aed64b5 100644 --- a/react/src/components/main/main.js +++ b/react/src/components/main/main.js @@ -4,7 +4,9 @@ import Store from '../../store'; import { translate } from '../../translate/translate'; import { getDexCoins, + activeHandle, triggerToaster, + shepherdElectrumCoins, } from '../../actions/actionCreators'; class Main extends React.Component { @@ -71,6 +73,8 @@ class Main extends React.Component { componentWillMount() { Store.dispatch(getDexCoins()); + Store.dispatch(activeHandle()); + Store.dispatch(shepherdElectrumCoins()); } render() { diff --git a/react/src/reducers/activeCoin.js b/react/src/reducers/activeCoin.js index 28f6d1215..6ac7edcae 100644 --- a/react/src/reducers/activeCoin.js +++ b/react/src/reducers/activeCoin.js @@ -15,6 +15,8 @@ import { DASHBOARD_ACTIVE_COIN_GETINFO_FAILURE, SYNCING_NATIVE_MODE, DASHBOARD_UPDATE, + DASHBOARD_ELECTRUM_BALANCE, + DASHBOARD_ELECTRUM_TRANSACTIONS, } from '../actions/storeType'; // TODO: refactor current coin props copy on change @@ -134,6 +136,16 @@ export function ActiveCoin(state = { }; } } + case DASHBOARD_ELECTRUM_BALANCE: + return { + ...state, + balance: action.balance, + }; + case DASHBOARD_ELECTRUM_TRANSACTIONS: + return { + ...state, + txhistory: action.txhistory, + }; case DASHBOARD_ACTIVE_COIN_BALANCE: return { ...state, diff --git a/react/src/reducers/dashboard.js b/react/src/reducers/dashboard.js index e5cfe22ab..cc3cfdf68 100644 --- a/react/src/reducers/dashboard.js +++ b/react/src/reducers/dashboard.js @@ -5,6 +5,7 @@ import { DISPLAY_CLAIM_INTEREST_MODAL, DASHBOARD_SYNC_ONLY_UPDATE, DISPLAY_IMPORT_KEY_MODAL, + DASHBOARD_ELECTRUM_COINS, } from '../actions/storeType'; export function Dashboard(state = { @@ -14,8 +15,14 @@ export function Dashboard(state = { displayClaimInterestModal: false, skipFullDashboardUpdate: false, displayImportKeyModal: false, + electrumCoins: {}, }, action) { switch (action.type) { + case DASHBOARD_ELECTRUM_COINS: + return { + ...state, + electrumCoins: action.electrumCoins, + }; case DASHBOARD_SECTION_CHANGE: return { ...state, diff --git a/react/src/reducers/index.js b/react/src/reducers/index.js index 1fc31fc51..fe88f5de4 100644 --- a/react/src/reducers/index.js +++ b/react/src/reducers/index.js @@ -8,6 +8,7 @@ import { Dashboard } from './dashboard'; import { ActiveCoin } from './activeCoin'; import { Settings } from './settings'; import { Interval } from './interval'; +import { Login } from './login'; const appReducer = combineReducers({ AddCoin, @@ -17,6 +18,7 @@ const appReducer = combineReducers({ ActiveCoin, Settings, Interval, + Login, routing: routerReducer, }); diff --git a/react/src/reducers/login.js b/react/src/reducers/login.js new file mode 100644 index 000000000..b38b86ef5 --- /dev/null +++ b/react/src/reducers/login.js @@ -0,0 +1,18 @@ +import { GET_PIN_LIST } from '../actions/storeType'; + +export function Login(state = { + pinList: [], +}, action) { + if (state === null) state = { pinList: [] }; + + switch (action.type) { + case GET_PIN_LIST: + return Object.assign({}, state, { + pinList: action.pinList, + }); + default: + return state; + } +} + +export default Login; \ No newline at end of file diff --git a/react/src/reducers/main.js b/react/src/reducers/main.js index 1d207c5a0..6639a079b 100644 --- a/react/src/reducers/main.js +++ b/react/src/reducers/main.js @@ -1,11 +1,15 @@ import { GET_ACTIVE_COINS, + LOGIN, + ACTIVE_HANDLE, DISPLAY_LOGIN_SETTINGS_MODAL } from '../actions/storeType'; export function Main(state = { + isLoggedIn: false, activeCoins: [], displayLoginSettingsModal: false, + total: 0, }, action) { switch (action.type) { case GET_ACTIVE_COINS: @@ -13,6 +17,18 @@ export function Main(state = { ...state, activeCoins: action.activeCoins, coins: action.coins, + total: action.total, + }; + case LOGIN: + return { + ...state, + isLoggedIn: action.isLoggedIn, + }; + case ACTIVE_HANDLE: + return { + ...state, + isLoggedIn: action.isLoggedIn, + activeHandle: action.handle, }; case DISPLAY_LOGIN_SETTINGS_MODAL: return { @@ -24,4 +40,4 @@ export function Main(state = { } } -export default Main; +export default Main; \ No newline at end of file diff --git a/react/src/translate/en.js b/react/src/translate/en.js index 6bd5d2762..e22fe18b8 100644 --- a/react/src/translate/en.js +++ b/react/src/translate/en.js @@ -63,6 +63,7 @@ export const _lang = { 'NO_ACTIVE_COIN': 'No active coin', }, 'INDEX': { + 'SPV_MODE': 'SPV Mode', 'PROGRESS_RESCANNING_BLOCKS': '(rescanning blocks)', 'BLOCKS': 'Blocks', 'PLEASE_WAIT_UNTIL_RESCAN_FINISHED': 'Please wait until rescan process is finished', diff --git a/react/src/util/coinHelper.js b/react/src/util/coinHelper.js index 56ab30064..095ba1899 100644 --- a/react/src/util/coinHelper.js +++ b/react/src/util/coinHelper.js @@ -371,9 +371,9 @@ export function getModeInfo(mode) { modetip = 'Native'; modecolor = 'primary'; break; - case 'basilisk': - modecode = 'Basilisk'; - modetip = 'Basilisk'; + case 'spv': + modecode = 'SPV'; + modetip = 'SPV'; modecolor = 'info'; break; case 'full': diff --git a/react/src/util/crypto/passphrasegenerator.js b/react/src/util/crypto/passphrasegenerator.js index f76e9c836..f309366ba 100755 --- a/react/src/util/crypto/passphrasegenerator.js +++ b/react/src/util/crypto/passphrasegenerator.js @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright © 2016 The Waves Core Developers. * + * Copyright © 2016 The Waves Core Developers. * * * * See the LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -14,10 +14,6 @@ * * ******************************************************************************/ -/** - * @depends {../3rdparty/jquery-2.1.0.js} - */ - import { ClientWordList } from './wordlist.js'; export const PassPhraseGenerator = { From 862f6d80a5b27e478913f31088d474ae5f6ce78b Mon Sep 17 00:00:00 2001 From: Miika Turunen Date: Tue, 3 Oct 2017 22:52:53 +0300 Subject: [PATCH 05/47] AGP-299, Hook to gettransaction & getrawtransaction & print return values in UI --- react/src/actions/actionCreators.js | 1 + react/src/actions/actions/getTxDetails.js | 79 +++++++ .../dashboard/walletsTxInfo/walletsTxInfo.js | 39 +++- .../walletsTxInfo/walletsTxInfo.render.js | 205 +++++++++--------- 4 files changed, 226 insertions(+), 98 deletions(-) create mode 100644 react/src/actions/actions/getTxDetails.js diff --git a/react/src/actions/actionCreators.js b/react/src/actions/actionCreators.js index 5214535bd..ae83cb588 100644 --- a/react/src/actions/actionCreators.js +++ b/react/src/actions/actionCreators.js @@ -54,6 +54,7 @@ export * from './actions/update'; export * from './actions/jumblr'; export * from './actions/interest'; export * from './actions/nativeDashboardUpdate'; +export * from './actions/getTxDetails'; export function changeActiveAddress(address) { return { diff --git a/react/src/actions/actions/getTxDetails.js b/react/src/actions/actions/getTxDetails.js new file mode 100644 index 000000000..cc032a8a4 --- /dev/null +++ b/react/src/actions/actions/getTxDetails.js @@ -0,0 +1,79 @@ +import { + triggerToaster +} from '../actionCreators'; +import Config from '../../config'; + +export function getTxDetails(coin, txid, type) { + return new Promise((resolve, reject) => { + const payload = { + mode: null, + chain: coin, + cmd: 'gettransaction', + params: [ + txid + ], + }; + + fetch( + `http://127.0.0.1:${Config.agamaPort}/shepherd/cli`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ payload: payload }) + }, + ) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'getTransaction', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(json.result ? json.result : json); + }) + }); +} + +export function getRawTxDetails(coin, txid) { + return new Promise((resolve, reject) => { + const payload = { + mode: null, + chain: coin, + cmd: 'getrawtransaction', + params: [ + txid, + 1 + ], + }; + + fetch( + `http://127.0.0.1:${Config.agamaPort}/shepherd/cli`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ payload: payload }) + }, + ) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'getTransaction', + '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/dashboard/walletsTxInfo/walletsTxInfo.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js index d5669128b..ae7889185 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js @@ -2,7 +2,11 @@ import React from 'react'; import { connect } from 'react-redux'; import { translate } from '../../../translate/translate'; import { sortByDate } from '../../../util/sort'; -import { toggleDashboardTxInfoModal } from '../../../actions/actionCreators'; +import { + toggleDashboardTxInfoModal, + getTxDetails, + getRawTxDetails, + } from '../../../actions/actionCreators'; import Store from '../../../store'; import WalletsTxInfoRender from './walletsTxInfo.render'; @@ -11,8 +15,12 @@ class WalletsTxInfo extends React.Component { super(); this.state = { activeTab: 0, + txDetails: null, + rawTxDetails: null, }; this.toggleTxInfoModal = this.toggleTxInfoModal.bind(this); + this.loadTxDetails = this.loadTxDetails.bind(this); + this.loadRawTxDetails = this.loadRawTxDetails.bind(this); } toggleTxInfoModal() { @@ -23,6 +31,35 @@ class WalletsTxInfo extends React.Component { })); } + componentWillReceiveProps(nextProps) { + const texInfo = nextProps.ActiveCoin.txhistory[nextProps.ActiveCoin.showTransactionInfoTxIndex]; + + if (texInfo && + this.props.ActiveCoin.showTransactionInfoTxIndex !== nextProps.ActiveCoin.showTransactionInfoTxIndex) { + this.loadTxDetails(nextProps.ActiveCoin.coin, texInfo.txid); + this.loadRawTxDetails(nextProps.ActiveCoin.coin, texInfo.txid); + } + + } + + loadTxDetails(coin, txid) { + getTxDetails(coin, txid) + .then((json) => { + this.setState(Object.assign({}, this.state, { + txDetails: json, + })); + }); + } + + loadRawTxDetails(coin, txid) { + getRawTxDetails(coin, txid) + .then((json) => { + this.setState(Object.assign({}, this.state, { + rawTxDetails: json, + })); + }); + } + openTab(tab) { this.setState(Object.assign({}, this.state, { activeTab: tab, diff --git a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js index e2ab0d722..dbd1e50ec 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js @@ -36,120 +36,131 @@ const WalletsTxInfoRender = function(txInfo) {
    -
    - { this.state.activeTab === 0 && -
    + { this.state.txDetails && +
    + { this.state.activeTab === 0 && +
    - - - - - - - - - - - - - - - - - - { txInfo.blockhash && + + - + - } - { (txInfo.blocktime || txInfo.timestamp) && - + - } - - - - - - - - + + + + + + + + + { this.state.txDetails.blockindex && + + + + + } + { this.state.txDetails.blockhash && + + + + + } + { (this.state.txDetails.blocktime || this.state.txDetails.timestamp) && + + + + + } + + + + + + + + + + + + + + + + + +
    { translate('TX_INFO.ADDRESS') } - { txInfo.address } -
    { translate('TX_INFO.AMOUNT') } - { txInfo.amount } -
    { translate('TX_INFO.CATEGORY') } - { txInfo.category || txInfo.type } -
    { translate('TX_INFO.CONFIRMATIONS') } - { txInfo.confirmations } -
    blockhash{ translate('TX_INFO.ADDRESS') } - { txInfo.blockhash } + { this.state.txDetails.details[0].address }
    blocktime{ translate('TX_INFO.AMOUNT') } - { secondsToString(txInfo.blocktime || txInfo.timestamp) } + { txInfo.amount }
    txid - { txInfo.txid } -
    walletconflicts - { txInfo.walletconflicts.length } -
    { translate('TX_INFO.CATEGORY') } + { this.state.txDetails.details[0].category || txInfo.type } +
    { translate('TX_INFO.CONFIRMATIONS') } + { this.state.txDetails.confirmations } +
    blockindex + { this.state.txDetails.blockindex } +
    blockhash + { this.state.txDetails.blockhash } +
    blocktime + { secondsToString(this.state.txDetails.blocktime || this.state.txDetails.timestamp) } +
    txid + { this.state.txDetails.txid } +
    walletconflicts + { this.state.txDetails.walletconflicts.length } +
    time + { secondsToString(this.state.txDetails.time) } +
    timereceived + { secondsToString(this.state.txDetails.timereceived) } +
    +
    + } + { this.state.activeTab === 1 && +
    + + - + - + - -
    timevjoinsplit - { secondsToString(txInfo.time) } + { txInfo.vjoinsplit }
    timereceiveddetails - { secondsToString(txInfo.timereceived) } + { txInfo.details }
    -
    - } - { this.state.activeTab === 1 && -
    - - - - - - - - - - - -
    vjoinsplit - { txInfo.vjoinsplit } -
    details - { txInfo.details } -
    -
    - } - { this.state.activeTab === 2 && -
    - -
    - } - { this.state.activeTab === 3 && -
    - -
    - } -
    + + +
    + } + { this.state.activeTab === 2 && +
    + +
    + } + { this.state.activeTab === 3 && +
    + +
    + } +
    + }
    From d8c82d4791255dee902b8d2743126336b6f5bec8 Mon Sep 17 00:00:00 2001 From: Miika Turunen Date: Tue, 3 Oct 2017 23:01:01 +0300 Subject: [PATCH 06/47] AGP-299, Combine functions --- react/src/actions/actions/getTxDetails.js | 52 +++++-------------- .../dashboard/walletsTxInfo/walletsTxInfo.js | 3 +- 2 files changed, 14 insertions(+), 41 deletions(-) diff --git a/react/src/actions/actions/getTxDetails.js b/react/src/actions/actions/getTxDetails.js index cc032a8a4..8ea622258 100644 --- a/react/src/actions/actions/getTxDetails.js +++ b/react/src/actions/actions/getTxDetails.js @@ -5,7 +5,7 @@ import Config from '../../config'; export function getTxDetails(coin, txid, type) { return new Promise((resolve, reject) => { - const payload = { + let payload = { mode: null, chain: coin, cmd: 'gettransaction', @@ -13,44 +13,18 @@ export function getTxDetails(coin, txid, type) { txid ], }; - - fetch( - `http://127.0.0.1:${Config.agamaPort}/shepherd/cli`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ payload: payload }) - }, - ) - .catch(function(error) { - console.log(error); - dispatch( - triggerToaster( - 'getTransaction', - 'Error', - 'error' - ) - ); - }) - .then(response => response.json()) - .then(json => { - resolve(json.result ? json.result : json); - }) - }); -} - -export function getRawTxDetails(coin, txid) { - return new Promise((resolve, reject) => { - const payload = { - mode: null, - chain: coin, - cmd: 'getrawtransaction', - params: [ - txid, - 1 - ], - }; + + if(type==='raw') { + payload = { + mode: null, + chain: coin, + cmd: 'getrawtransaction', + params: [ + txid, + 1 + ], + }; + } fetch( `http://127.0.0.1:${Config.agamaPort}/shepherd/cli`, { diff --git a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js index ae7889185..c1914a9ab 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.js @@ -5,7 +5,6 @@ import { sortByDate } from '../../../util/sort'; import { toggleDashboardTxInfoModal, getTxDetails, - getRawTxDetails, } from '../../../actions/actionCreators'; import Store from '../../../store'; import WalletsTxInfoRender from './walletsTxInfo.render'; @@ -52,7 +51,7 @@ class WalletsTxInfo extends React.Component { } loadRawTxDetails(coin, txid) { - getRawTxDetails(coin, txid) + getTxDetails(coin, txid, 'raw') .then((json) => { this.setState(Object.assign({}, this.state, { rawTxDetails: json, From 51f4a2344d2dde6932cf5e4a8417508ca68dc971 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Sat, 7 Oct 2017 21:28:36 +0300 Subject: [PATCH 07/47] settings spv server selection --- assets/mainWindow/img/fa-flash.png | Bin 0 -> 1676 bytes .../settings/settings.debugLogPanel.js | 10 +- .../settings/settings.exportKeysPanel.js | 136 ++++++++-------- .../components/dashboard/settings/settings.js | 18 ++- .../dashboard/settings/settings.panel.js | 11 +- .../dashboard/settings/settings.render.js | 42 +++-- .../settings/settings.spvServersPanel.js | 150 ++++++++++++++++++ 7 files changed, 267 insertions(+), 100 deletions(-) create mode 100644 assets/mainWindow/img/fa-flash.png create mode 100644 react/src/components/dashboard/settings/settings.spvServersPanel.js diff --git a/assets/mainWindow/img/fa-flash.png b/assets/mainWindow/img/fa-flash.png new file mode 100644 index 0000000000000000000000000000000000000000..3680e31b70572a8880d943aacba3438fe91b0657 GIT binary patch literal 1676 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD~={fr&l9C&U%V{XYsud~T{6fMaViJ-vvU2hYib^V~Y8u*xM#d)Q7FO0aws!Uo z&MvN=Ufw?b0f9kb;n6X%aq)@CDQTHm**Up+`2}U=Rn;}Mbq$S8&8_VnojtvM{Szim znL2Ietl4ws&R?)_(cJanqI^yLRu{yZ^w!Lr0DtKXK;lx$~DUU%7ho*6llY z@7;g!@X_NZPhY-z^X~nJkDoq&`TFhqkDtGO|M~mxk^hy+KzB1HdAqx;2~Yjh&A`B1 z?djqeA|d(qU~hPEsto&w_o3!fBDZKvQ{1ZKd}_%Gov70$-CSGU^xd|eIg^!?wj%P{ zmKaO#>c+4eM!6|G+uqC1?Ad!~_s2JL?tZ>=zt}2$KL0Hs_?Mn%UpMvi={H{wSIh1X z$d%t?en#zI6ytf>1mXIq4|l%jJYujB*V)wB`{B;xoJWgT&%aF;u70rh{fe*{_Vagl zIBX66a8EfWNqWzBwW~$h5BF{knceW6sd80r<9E3wx87?%+l#{k zLvFEhE)LPW!^Js2=&7;ChJOpZSh%+5MDXSw3fXz7Gxdv${hx_@gc|j%SAG(Fwf6S3 zA2YqVzB^1-4!Oy*``&W(WtqawYQ`ZmHU(VV{`m3z2z4&oUn@Roc~#A?s-M-niSfn8 zxmx86d(X@Kn|fNhS*?A=hs!6_r>~f@n{$@W#ivT6!^<)yzmEC1J-ZaiN~KBoC~*#<`;E8YzUL2{<2_%a_hGygwe6NK3mc483k5{?u=u$piPr8n1nSFSvd-U$y`J%_oOH*(K~1 zS9qoLI)!<{m9tgJ0$*8PKWP&9D);(`;%l3^pArZx{L6Kbf20EA%NWChe@ile^N$DGvYu literal 0 HcmV?d00001 diff --git a/react/src/components/dashboard/settings/settings.debugLogPanel.js b/react/src/components/dashboard/settings/settings.debugLogPanel.js index f651bf299..0cac3c845 100644 --- a/react/src/components/dashboard/settings/settings.debugLogPanel.js +++ b/react/src/components/dashboard/settings/settings.debugLogPanel.js @@ -100,11 +100,15 @@ class DebugLogPanel extends React.Component { }); } + // TODO: extend to include asset chains + render() { return (
    -

    { translate('INDEX.DEBUG_LOG_DESC') }

    + { this.props.Main.coins && this.props.Main.coins.native && Object.keys(this.props.Main.coins.native).length > 0 && +

    { translate('INDEX.DEBUG_LOG_DESC') }

    + }
    - { !this.state.toggleAppRuntimeLog && + { !this.state.toggleAppRuntimeLog && this.props.Main.coins && this.props.Main.coins.native && Object.keys(this.props.Main.coins.native).length > 0 &&
    -
    -
    -
    - - { this.renderWifKeys() } -
    -
    - -
    -
    - { this.renderExportWifKeysRaw() } + { this.state.keys && +
    +
    + + + + + + { this.renderWifKeys() } +
    + { translate('SETTINGS.ADDRESS_LIST') } + + { translate('SETTINGS.WIF_KEY_LIST') } +
    -
    + }
    ); }; diff --git a/react/src/components/dashboard/settings/settings.js b/react/src/components/dashboard/settings/settings.js index 72b65c927..31b88e46e 100644 --- a/react/src/components/dashboard/settings/settings.js +++ b/react/src/components/dashboard/settings/settings.js @@ -21,6 +21,19 @@ import { class Settings extends React.Component { constructor(props) { super(props); + this.displaySPVServerListTab = this.displaySPVServerListTab.bind(this); + } + + displaySPVServerListTab() { + if (this.props.Main && + this.props.Main.coins && + this.props.Main.coins.spv) { + for (let i = 0; this.props.Main.coins.spv.length; i++) { + if (this.props.Dashboard.electrumCoins[this.props.Main.coins.spv[i]].serverList) { + return true; + } + } + } } componentDidMount(props) { @@ -37,9 +50,8 @@ class Settings extends React.Component { const mapStateToProps = (state) => { return { - Main: { - coins: state.Main.coins, - }, + Main: state.Main, + Dashboard: state.Dashboard, }; }; diff --git a/react/src/components/dashboard/settings/settings.panel.js b/react/src/components/dashboard/settings/settings.panel.js index 44f83707e..3cf5f69a0 100644 --- a/react/src/components/dashboard/settings/settings.panel.js +++ b/react/src/components/dashboard/settings/settings.panel.js @@ -19,7 +19,8 @@ class Panel extends React.Component { singleOpen, openByDefault, uniqId, - children } = this.props; + children + } = this.props; const settings = { singleOpen, @@ -37,9 +38,8 @@ class Panel extends React.Component { children, } = this.props; - - const kids = React.Children.map(children, (child, i) => { - if(child) { + const _children = React.Children.map(children, (child, i) => { + if (child) { const unqId = `panel-sec-${i}`; return React.cloneElement(child, { toggle: (acId) => this.toggleSection(acId), @@ -49,9 +49,8 @@ class Panel extends React.Component { }); } }); - - return kids; + return _children; } toggleSection(sectionId) { diff --git a/react/src/components/dashboard/settings/settings.render.js b/react/src/components/dashboard/settings/settings.render.js index 75bb7f90b..81c14947d 100644 --- a/react/src/components/dashboard/settings/settings.render.js +++ b/react/src/components/dashboard/settings/settings.render.js @@ -10,9 +10,10 @@ import AppSettingsPanel from './settings.appSettingsPanel'; import CliPanel from './settings.cliPanel'; import DebugLogPanel from './settings.debugLogPanel'; // import FiatCurrencyPanel from './settings.fiatCurrencyPanel'; -// import ExportKeysPanel from './settings.exportKeysPanel'; +import ExportKeysPanel from './settings.exportKeysPanel'; // import ImportKeysPanel from './settings.importKeysPanel'; import SupportPanel from './settings.supportPanel'; +import SPVServersPanel from './settings.spvServersPanel'; // import WalletInfoPanel from './settings.walletInfoPanel'; // import WalletBackupPanel from './settings.walletBackupPanel'; @@ -46,13 +47,6 @@ import SupportPanel from './settings.supportPanel'; } - { !this.props.disableWalletSpecificUI && - - - - } { !this.props.disableWalletSpecificUI && - + title={ translate('SETTINGS.APP_CONFIG') + ' (config.json)' } + icon="icon fa-bug"> + - - - + { this.props.Main.coins && this.props.Main.coins.spv && Object.keys(this.props.Main.coins.spv).length && this.props.Main.isLoggedIn && + + + + } + { this.props.Main.coins && this.props.Main.coins.spv && Object.keys(this.props.Main.coins.spv).length && this.displaySPVServerListTab() && + + + + } + { this.props.Main.coins && this.props.Main.coins.native && Object.keys(this.props.Main.coins.native).length > 0 && + + + + } diff --git a/react/src/components/dashboard/settings/settings.spvServersPanel.js b/react/src/components/dashboard/settings/settings.spvServersPanel.js new file mode 100644 index 000000000..50490a773 --- /dev/null +++ b/react/src/components/dashboard/settings/settings.spvServersPanel.js @@ -0,0 +1,150 @@ +import React from 'react'; +import { translate } from '../../../translate/translate'; +import { connect } from 'react-redux'; +import { + shepherdElectrumCheckServerConnection, + shepherdElectrumSetServer, + shepherdElectrumCoins, + triggerToaster, +} from '../../../actions/actionCreators'; +import Store from '../../../store'; + +class SPVServersPanel extends React.Component { + constructor() { + super(); + this.updateInput = this.updateInput.bind(this); + } + + componentWillReceiveProps(props) { + if (props.Dashboard && + props.Dashboard.activeSection !== 'settings') { + this.setState(Object.assign({}, this.state, { + keys: null, + })); + } + } + + setElectrumServer(coin) { + let _server = [ + this.props.Dashboard.electrumCoins[coin].server.ip, + this.props.Dashboard.electrumCoins[coin].server.port + ]; + + if (this.state && + this.state[coin]) { + _server = this.state[coin].split(':'); + } + + shepherdElectrumCheckServerConnection(_server[0], _server[1]) + .then((res) => { + if (res.result) { + shepherdElectrumSetServer(coin, _server[0], _server[1]) + .then((serverSetRes) => { + Store.dispatch( + triggerToaster( + `${coin} SPV server set to ${_server[0]}:${_server[1]}`, + translate('TOASTR.WALLET_NOTIFICATION'), + 'success' + ) + ); + Store.dispatch(shepherdElectrumCoins()); + }); + } else { + Store.dispatch( + triggerToaster( + `${coin} SPV server ${_server[0]}:${_server[1]} is unreachable!`, + translate('TOASTR.WALLET_NOTIFICATION'), + 'error' + ) + ); + } + }) + } + + updateInput(e) { + this.setState({ + [e.target.name]: e.target.value, + }); + } + + renderServerListSelectorOptions(coin) { + let _items = []; + let _spvServers = this.props.Dashboard.electrumCoins[coin].serverList; + + for (let i = 0; i < _spvServers.length; i++) { + _items.push( + + ); + } + + return _items; + } + + renderServerList() { + let _items = []; + let _spvCoins = this.props.Main.coins.spv; + + for (let i = 0; i < _spvCoins.length; i++) { + if (this.props.Dashboard.electrumCoins[_spvCoins[i]] && + this.props.Dashboard.electrumCoins[_spvCoins[i]].serverList && + this.props.Dashboard.electrumCoins[_spvCoins[i]].serverList !== 'none') { + _items.push( +
    1 ? ' padding-bottom-30' : '') } + key={ `spv-server-list-${ _spvCoins[i] }` }> +
    + { _spvCoins[i] } +
    + +
    +
    + +
    +
    +
    + ); + } + } + + return _items; + } + + render() { + return ( +
    +
    +
    +

    { translate('SETTINGS.SPV_SERVER_LIST_DESC') }

    +
    +
    + { this.renderServerList() } +
    + ); + }; +} + +const mapStateToProps = (state) => { + return { + ActiveCoin: { + coin: state.ActiveCoin.coin, + }, + Dashboard: state.Dashboard, + Main: state.Main, + }; +}; + +export default connect(mapStateToProps)(SPVServersPanel); \ No newline at end of file From ce61fbb15ed66af8c828ea1afc34226b9630d7f3 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Sat, 7 Oct 2017 21:28:58 +0300 Subject: [PATCH 08/47] create wallet fix --- react/src/components/login/login.js | 42 ++++++---------------- react/src/components/login/login.render.js | 39 +++++++++----------- react/src/components/login/login.scss | 8 ++++- react/src/components/main/main.js | 9 ----- react/src/components/overrides.scss | 16 +++++++++ react/src/reducers/main.js | 2 -- react/src/translate/en.js | 11 +++--- 7 files changed, 57 insertions(+), 70 deletions(-) diff --git a/react/src/components/login/login.js b/react/src/components/login/login.js index 6b8b23eea..603c6c4a1 100644 --- a/react/src/components/login/login.js +++ b/react/src/components/login/login.js @@ -2,13 +2,11 @@ import React from 'react'; import { connect } from 'react-redux'; import { toggleAddcoinModal, - iguanaWalletPassphrase, shepherdElectrumAuth, shepherdElectrumCoins, //iguanaActiveHandle, startInterval, getDexCoins, - createNewWallet, triggerToaster, toggleLoginSettingsModal } from '../../actions/actionCreators'; @@ -56,6 +54,7 @@ class Login extends React.Component { decryptKey: '', selectedPin: '', isExperimentalOn: false, + enableEncryptSeed: false, }; this.toggleActivateCoinForm = this.toggleActivateCoinForm.bind(this); this.updateRegisterConfirmPassPhraseInput = this.updateRegisterConfirmPassPhraseInput.bind(this); @@ -101,6 +100,7 @@ class Login extends React.Component { randomSeed: PassPhraseGenerator.generatePassPhrase(this.state.bitsOption), isSeedConfirmError: false, isSeedBlank: false, + isCustomSeedWeak: false, }); } else { // if customWalletSeed is set to true, reset to seed to an empty string @@ -160,9 +160,6 @@ class Login extends React.Component { } componentDidMount() { - // Store.dispatch(iguanaActiveHandle(true)); - // this.loadPinList(); - let appConfig; try { @@ -218,22 +215,11 @@ class Login extends React.Component { if (props && props.Main && !props.Main.isLoggedIn) { - console.warn('display login'); this.setState({ display: true, - activeLoginSection: 'login', + activeLoginSection: this.state.activeLoginSection !== 'signup' ? 'login' : 'signup', }); - /*if (!this.props.Interval.interval.activeCoins) { - // only start a new 'activeCoins' interval if a previous one doesn't exist - if (!this._iguanaActiveCoins) { - this._iguanaActiveCoins = setInterval(() => { - Store.dispatch(getDexCoins()); - }, IGUNA_ACTIVE_COINS_TIMEOUT); - Store.dispatch(startInterval('activeCoins', this._iguanaActiveCoins)); - } - }*/ - document.body.className = 'page-login layout-full page-dark'; } @@ -241,20 +227,10 @@ class Login extends React.Component { props && props.Main && props.Main.isLoggedIn) { - /*if (props && - props.Main && - props.Main.activeCoins) { - this.setState({ - activeLoginSection: 'login', - }); - } else {*/ this.setState({ activeLoginSection: 'activateCoin', }); - //} } - - console.warn(this.state); } toggleActivateCoinForm() { @@ -323,9 +299,6 @@ class Login extends React.Component { if (this.state.selectedPin) { Store.dispatch(loginWithPin(this.state.decryptKey, this.state.selectedPin)); } else { - /*Store.dispatch( - iguanaWalletPassphrase(this.state.loginPassphrase) - );*/ Store.dispatch( shepherdElectrumAuth(this.state.loginPassphrase) ); @@ -389,11 +362,18 @@ class Login extends React.Component { } execWalletCreate() { - Store.dispatch( + /*Store.dispatch( createNewWallet( this.state.randomSeedConfirm, this.props.Dashboard.activeHandle ) + );*/ + + Store.dispatch( + shepherdElectrumAuth(this.state.randomSeedConfirm) + ); + Store.dispatch( + shepherdElectrumCoins() ); this.setState({ diff --git a/react/src/components/login/login.render.js b/react/src/components/login/login.render.js index e6a5af4de..23a3805d2 100644 --- a/react/src/components/login/login.render.js +++ b/react/src/components/login/login.render.js @@ -34,11 +34,6 @@ const LoginRender = function () { { translate('INDEX.SETTINGS') }
    -
  • - - { translate('ADD_COIN.SYNC_ONLY') } - -
  • this.toggleLoginSettingsDropdownSection('about') }> { translate('ABOUT.ABOUT_AGAMA') } @@ -48,7 +43,7 @@ const LoginRender = function () {
  • -
    +

    { translate('INDEX.WELCOME_LOGIN') }

    @@ -85,24 +80,24 @@ const LoginRender = function () {
    } - { this.state.loginPassphrase && + { this.state.loginPassphrase && this.state.enableEncryptSeed &&
    - - -
    this.toggleShouldEncryptSeed() }> - { translate('LOGIN.ENCRYPT_SEED') } -
    -
    + + +
    this.toggleShouldEncryptSeed() }> + { translate('LOGIN.ENCRYPT_SEED') } +
    +
    diff --git a/react/src/components/login/login.scss b/react/src/components/login/login.scss index 3a04ad0a2..89b9ad54f 100644 --- a/react/src/components/login/login.scss +++ b/react/src/components/login/login.scss @@ -20,7 +20,7 @@ button { position: absolute; font-weight: 500; font-size: 14px; - top: -22px; + top: -26px; right: 0; background-color: #757575; border: 0; @@ -163,4 +163,10 @@ option.login-option { .register-form { width: 540px; margin: 30px 0; +} + +.form-material .form-control.focus~.floating-label, +.form-material .form-control:focus~.floating-label, +.form-material .form-control:not(.empty)~.floating-label { + top: -14px; } \ No newline at end of file diff --git a/react/src/components/main/main.js b/react/src/components/main/main.js index b0aed64b5..c0b6dd7a6 100644 --- a/react/src/components/main/main.js +++ b/react/src/components/main/main.js @@ -60,15 +60,6 @@ class Main extends React.Component { ) ); } - - /*Store.dispatch(iguanaActiveHandle()); - const _iguanaActiveHandle = setInterval(function() { - Store.dispatch(iguanaActiveHandle()); - }, IGUANA_ACTIVE_HANDLE_TIMEOUT); - - this.setState(Object.assign({}, this.state, { - activeHandleInterval: _iguanaActiveHandle, - }));*/ } componentWillMount() { diff --git a/react/src/components/overrides.scss b/react/src/components/overrides.scss index 83ca8d105..5aca6a69f 100644 --- a/react/src/components/overrides.scss +++ b/react/src/components/overrides.scss @@ -382,6 +382,14 @@ select{ height: 75px; } +.page-header--spv { + height: 120px; + + .page-header-actions { + top: 32%; + } +} + .toggle-filters:hover { background-color: #f3f1f1; } @@ -419,4 +427,12 @@ select{ } } } +} + +.balance-placeholder--bold { + .pull-left { + font-weight: 500; + font-size: 16px; + padding-top: 16px !important; + } } \ No newline at end of file diff --git a/react/src/reducers/main.js b/react/src/reducers/main.js index 6639a079b..7b768dce3 100644 --- a/react/src/reducers/main.js +++ b/react/src/reducers/main.js @@ -7,7 +7,6 @@ import { export function Main(state = { isLoggedIn: false, - activeCoins: [], displayLoginSettingsModal: false, total: 0, }, action) { @@ -15,7 +14,6 @@ export function Main(state = { case GET_ACTIVE_COINS: return { ...state, - activeCoins: action.activeCoins, coins: action.coins, total: action.total, }; diff --git a/react/src/translate/en.js b/react/src/translate/en.js index e22fe18b8..71d1bf087 100644 --- a/react/src/translate/en.js +++ b/react/src/translate/en.js @@ -262,7 +262,6 @@ export const _lang = { 'FIAT_CURRENCY': 'Fiat Currency', 'EXPORT_KEYS': 'Export Keys', 'ONLY_ACTIVE_WIF_KEYS': 'Only the active coin wallet WIF keys will be shown here.
    ' + - 'If you don\'t see a WIF key for the coin you are looking for please active it by adding it from EasyDEX Dashboard.
    ' + 'You can use these WIF keys to import to another coin wallets.', 'PLEASE_KEEP_KEYS_SAFE': 'Please keep these keys extra safe and secure.', 'PASSPHRASE': 'Passphrase', @@ -563,14 +562,17 @@ export const _lang = { 'JUMBLR_MOTTO': 'Secure, Native and Decentralised Coin Anonymizer', }, 'SETTINGS': { + 'SPV_SERVER_LIST_DESC': 'Server list selection is only available for active coins that have more than 1 server to connect to.', + 'SPV_SERVERS': 'SPV servers list', + 'SPV_SINGLE_SERVER_NOTICE': 'There is only one electrum server available. SPV verification is coming from the same server providing all remote data.', 'KMD_MAIN_SYNC_ONLY': 'KMD main sync only', 'RPC_FETCH_ONLY_DESC': 'Fetch block synchronization data only. Skip any other requests that can deteriorate sync speed.', 'RESET_TO_DEFAULT': 'Reset to default', 'PROGRESS': 'Progress', 'SUPPORT_TICKETS': 'Support tickets', 'GET_SLACK_INVITE': 'Get Slack invite', - 'ADDRESS_LIST': 'Address list', - 'WIF_KEY_LIST': 'WIF (Wallet Import Format) key list', + 'ADDRESS_LIST': 'Address', + 'WIF_KEY_LIST': 'WIF (Wallet Import Format) key', 'CLI_RESPONSE': 'CLI response', 'PASSPHRASE_REQ': 'Passphrase is required.', 'SAVE_APP_CONFIG': 'Save app config', @@ -595,8 +597,7 @@ export const _lang = { 'DIR': 'Dir', 'ADD_PEER_IP': 'Add Peer IP', 'APP_SESSION': 'App Session', - 'EXPORT_KEYS_NOTE': 'Note: it\'s important that you provide the same passphrase you used to login to the wallet!
    ' + - 'In case passphrases will not match wallet is going to log you out of current session.', + 'EXPORT_KEYS_NOTE': 'Note: it\'s important that you provide the same passphrase you used to login to the wallet!', }, 'TX_INFO': { 'ADDRESS': 'address', From 0b74e8341fd414151288a9a94225bed133778552 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Sat, 7 Oct 2017 21:29:37 +0300 Subject: [PATCH 09/47] missing spv ui; spv bug fixes --- assets/mainWindow/css/loading.css | 12 ++- assets/mainWindow/js/loading.js | 48 ++++++++-- react/src/actions/actionCreators.js | 1 - react/src/actions/actions/addCoin.js | 3 + react/src/actions/actions/electrum.js | 88 ++++++++++++++++++- react/src/actions/actions/walletAuth.js | 1 - .../components/addcoin/addcoinOptionsAC.js | 2 +- .../dashboard/coinTile/coinTileItem.js | 6 +- .../dashboard/invoiceModal/invoiceModal.js | 19 ++-- .../dashboard/sendCoin/sendCoin.render.js | 2 +- .../walletsBalance/walletsBalance.js | 26 +++++- .../walletsBalance/walletsBalance.render.js | 8 +- .../dashboard/walletsNav/walletsNav.js | 4 +- .../dashboard/walletsNav/walletsNav.render.js | 13 ++- 14 files changed, 203 insertions(+), 30 deletions(-) diff --git a/assets/mainWindow/css/loading.css b/assets/mainWindow/css/loading.css index 6b798de35..903c4bdd1 100644 --- a/assets/mainWindow/css/loading.css +++ b/assets/mainWindow/css/loading.css @@ -149,6 +149,9 @@ body.agama-app-settings-window { .margin-right-20 { margin-right: 20px; } +.margin-left-20 { + margin-left: 20px; +} .margin-top-15 { margin-top: 15px; } @@ -437,10 +440,17 @@ button.toast-close-button { display: inherit; top: inherit; left: inherit; - right: 33.1%; padding: 2px 0; } +.dropdown-menu.native { + right: 256px; +} + +.dropdown-menu.lite { + right: 74px; +} + .dropdown-menu li { cursor: pointer; margin: 5px 0; diff --git a/assets/mainWindow/js/loading.js b/assets/mainWindow/js/loading.js index 0eab70648..0eb941742 100644 --- a/assets/mainWindow/js/loading.js +++ b/assets/mainWindow/js/loading.js @@ -1,14 +1,16 @@ // TODO: merge into react app let _configCopy; -function toggleDropdown() { - const _dropdown = $('.dropdown-menu'); +function toggleDropdown(type) { + const _dropdown = $('.dropdown-menu.' + (type === 'lite' ? 'lite' : 'native')); if (_dropdown.hasClass('hide')) { _dropdown.removeClass('hide'); } else { _dropdown.addClass('hide'); } + + $('.dropdown-menu.' + (type === 'lite' ? 'native' : 'lite')).addClass('hide');; } function initSettingsForm() { @@ -178,7 +180,8 @@ function openSettingsWindow() { const remote = require('electron').remote; const window = remote.getCurrentWindow(); - $('.dropdown-menu').addClass('hide'); + $('.dropdown-menu.lite').addClass('hide'); + $('.dropdown-menu.native').addClass('hide'); window.createAppSettingsWindow(); } @@ -186,7 +189,8 @@ function startKMDPassive() { const remote = require('electron').remote; const window = remote.getCurrentWindow(); - $('.dropdown-menu').addClass('hide'); + $('.dropdown-menu.lite').addClass('hide'); + $('.dropdown-menu.native').addClass('hide'); disableModeButtons(); window.startKMDNative('KMD', true); @@ -195,11 +199,40 @@ function startKMDPassive() { window.hide(); } +function startKMDPassive() { + const remote = require('electron').remote; + const window = remote.getCurrentWindow(); + + $('.dropdown-menu.lite').addClass('hide'); + $('.dropdown-menu.native').addClass('hide'); + disableModeButtons(); + + window.startKMDNative('KMD', true); + + window.createWindow('open'); + window.hide(); +} + +function startSPV(coin) { + const remote = require('electron').remote; + const window = remote.getCurrentWindow(); + + $('.dropdown-menu.lite').addClass('hide'); + $('.dropdown-menu.native').addClass('hide'); + disableModeButtons(); + + window.startSPV(coin); + + window.createWindow('open'); + window.hide(); +} + function closeMainWindow(isKmdOnly, isCustom) { const remote = require('electron').remote; const window = remote.getCurrentWindow(); - $('.dropdown-menu').addClass('hide'); + $('.dropdown-menu.lite').addClass('hide'); + $('.dropdown-menu.native').addClass('hide'); disableModeButtons(); if (!isCustom) { @@ -227,6 +260,8 @@ function disableModeButtons() { $('#normalStartBtn').attr('disabled', true); $('#settingsBtn').attr('disabled', true); $('#nativeOnlyBtnCarret').attr('disabled', true); + $('#spvBtn').attr('disabled', true); + $('#spvBtnCarret').attr('disabled', true); } function normalStart() { @@ -234,6 +269,7 @@ function normalStart() { let appConf = remote.getCurrentWindow().appConfig; appConf.iguanaLessMode = false; - $('.dropdown-menu').addClass('hide'); + $('.dropdown-menu.lite').addClass('hide'); + $('.dropdown-menu.native').addClass('hide'); disableModeButtons(); } \ No newline at end of file diff --git a/react/src/actions/actionCreators.js b/react/src/actions/actionCreators.js index fb0704c35..7e5d4c742 100644 --- a/react/src/actions/actionCreators.js +++ b/react/src/actions/actionCreators.js @@ -138,7 +138,6 @@ export function dashboardCoinsState(json) { return { type: GET_ACTIVE_COINS, coins: json, - activeCoins: Object.keys(json.native).length ? true : false, total: json.total, } } diff --git a/react/src/actions/actions/addCoin.js b/react/src/actions/actions/addCoin.js index dc91b504e..ac50c0a14 100644 --- a/react/src/actions/actions/addCoin.js +++ b/react/src/actions/actions/addCoin.js @@ -5,6 +5,7 @@ import { triggerToaster, toggleAddcoinModal, getDexCoins, + shepherdElectrumCoins, } from '../actionCreators'; import { startCurrencyAssetChain, @@ -72,6 +73,7 @@ export function shepherdElectrumAuth(seed) { .then(json => { console.warn(json); dispatch(activeHandle()); + dispatch(shepherdElectrumCoins()); }); } } @@ -269,6 +271,7 @@ export function addCoinResult(coin, mode) { dispatch(toggleAddcoinModal(false, false)); if (Number(mode) === 0) { dispatch(activeHandle()); + dispatch(shepherdElectrumCoins()); } dispatch(getDexCoins()); } diff --git a/react/src/actions/actions/electrum.js b/react/src/actions/actions/electrum.js index 3f2ce067d..5e6594964 100644 --- a/react/src/actions/actions/electrum.js +++ b/react/src/actions/actions/electrum.js @@ -10,6 +10,92 @@ import { sendToAddressState, } from '../actionCreators'; +export function shepherdElectrumSetServer(coin, address, port) { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/coins/server/set?address=${address}&port=${port}&coin=${coin}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + } + }) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'shepherdElectrumSetServer', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + console.warn(json); + resolve(json); + }); + }); +} + +export function shepherdElectrumCheckServerConnection(address, port) { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/servers/test?address=${address}&port=${port}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + } + }) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'shepherdElectrumCheckServerConnection', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + console.warn(json); + if (!json.result) { + resolve('error'); + } else { + resolve(json); + } + }); + }); +} + +export function shepherdElectrumKeys(seed) { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/keys?seed=${seed}&active=true&iguana=true`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + } + }) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'shepherdElectrumKeys', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + console.warn(json); + if (!json.result) { + resolve('error'); + } else { + resolve(json); + } + }); + }); +} + export function shepherdElectrumBalance(coin, address) { return dispatch => { return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/getbalance?coin=${coin}&address=${address}`, { @@ -45,7 +131,7 @@ export function shepherdElectrumBalanceState(json) { export function shepherdElectrumTransactions(coin, address) { return dispatch => { - return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/listtransactions?coin=${coin}&address=${address}&full=true`, { + return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/listtransactions?coin=${coin}&address=${address}&full=true&maxlength=20`, { method: 'GET', headers: { 'Content-Type': 'application/json', diff --git a/react/src/actions/actions/walletAuth.js b/react/src/actions/actions/walletAuth.js index 595942034..de18c32b8 100644 --- a/react/src/actions/actions/walletAuth.js +++ b/react/src/actions/actions/walletAuth.js @@ -7,7 +7,6 @@ import Config from '../../config'; import { triggerToaster, getMainAddressState, - updateErrosStack } from '../actionCreators'; export function encryptWallet(_passphrase, cb, coin) { diff --git a/react/src/components/addcoin/addcoinOptionsAC.js b/react/src/components/addcoin/addcoinOptionsAC.js index a3b38f6e6..468619fbc 100644 --- a/react/src/components/addcoin/addcoinOptionsAC.js +++ b/react/src/components/addcoin/addcoinOptionsAC.js @@ -24,7 +24,7 @@ class AddCoinOptionsAC extends React.Component { let _items = []; for (let i = 0; i < _assetChains.length; i++) { - const availableModes = _assetChains[i] === 'revs' ? 'native|spv' : 'native'; + const availableModes = _assetChains[i] === 'revs' || _assetChains[i] === 'jumblr' ? 'native|spv' : 'native'; _items.push(
    -
    +
    -
    +
    0 ? 'col-lg-3 col-xs-12' : 'hide' }>
    @@ -52,7 +52,7 @@ const WalletsBalanceRender = function() {
    -
    +
    0 ? 'col-lg-3 col-xs-12' : 'hide' }>
    @@ -72,7 +72,7 @@ const WalletsBalanceRender = function() {
    -
    +
    diff --git a/react/src/components/dashboard/walletsNav/walletsNav.js b/react/src/components/dashboard/walletsNav/walletsNav.js index dd8340e80..c90b4733d 100644 --- a/react/src/components/dashboard/walletsNav/walletsNav.js +++ b/react/src/components/dashboard/walletsNav/walletsNav.js @@ -104,9 +104,7 @@ const mapStateToProps = (state) => { activeSection: state.ActiveCoin.activeSection, activeAddress: state.ActiveCoin.activeAddress, }, - Dashboard: { - activeHandle: state.Dashboard.activeHandle, - }, + Dashboard: state.Dashboard, nativeOnly: Config.iguanaLessMode, }; }; diff --git a/react/src/components/dashboard/walletsNav/walletsNav.render.js b/react/src/components/dashboard/walletsNav/walletsNav.render.js index 3603f3f6e..0532307af 100644 --- a/react/src/components/dashboard/walletsNav/walletsNav.render.js +++ b/react/src/components/dashboard/walletsNav/walletsNav.render.js @@ -21,9 +21,20 @@ export const WalletsNavWithWalletRender = function() { return (
    + { this.props.ActiveCoin && this.props.ActiveCoin.mode === 'spv' && +
    + { translate('INDEX.MY') } { this.props && this.props.ActiveCoin ? this.props.ActiveCoin.coin : '-' } { translate('INDEX.ADDRESS') }: + { this.props && this.props.Dashboard && this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin] && this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub ? this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub : '-' } + +
    + }
    diff --git a/react/src/components/overrides.scss b/react/src/components/overrides.scss index 5aca6a69f..5155a4892 100644 --- a/react/src/components/overrides.scss +++ b/react/src/components/overrides.scss @@ -435,4 +435,11 @@ select{ font-size: 16px; padding-top: 16px !important; } +} + +.tx-interest { + display: block; + color: #4caf50; + font-size: 13px; + padding-top: 5px; } \ No newline at end of file From 0b33b49e07a0d27ca3efa3ef0e8b9d55fd2314f7 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Sun, 8 Oct 2017 17:51:12 +0300 Subject: [PATCH 11/47] single spv server warning icon --- .../dashboard/coinTile/coinTileItem.render.js | 6 ++++++ react/src/components/overrides.scss | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/react/src/components/dashboard/coinTile/coinTileItem.render.js b/react/src/components/dashboard/coinTile/coinTileItem.render.js index 3ad788643..f2aac196e 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.render.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.render.js @@ -1,4 +1,5 @@ import React from 'react'; +import { translate } from '../../../translate/translate'; const CoinTileItemRender = function() { const { item } = this.props; @@ -23,6 +24,11 @@ const CoinTileItemRender = function() {
    + { this.props.Dashboard && this.props.Dashboard.electrumCoins && this.props.Dashboard.electrumCoins[item.coin].serverList && this.props.Dashboard.electrumCoins[item.coin].serverList === 'none' && + + }
    ); }; diff --git a/react/src/components/overrides.scss b/react/src/components/overrides.scss index 5155a4892..9610b841e 100644 --- a/react/src/components/overrides.scss +++ b/react/src/components/overrides.scss @@ -442,4 +442,15 @@ select{ color: #4caf50; font-size: 13px; padding-top: 5px; +} + +.icon-spv-connection-warning { + position: absolute; + bottom: 15px; + right: 15px; + + &:before { + color: #f2a654; + font-size: 18px; + } } \ No newline at end of file From 343c262ec5a94e4d15f2088134b7146dea7ccec9 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 9 Oct 2017 13:38:06 +0300 Subject: [PATCH 12/47] send coin amount validation fix --- .../components/dashboard/coinTile/coinTileItem.render.js | 6 +++++- react/src/components/dashboard/sendCoin/sendCoin.js | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/react/src/components/dashboard/coinTile/coinTileItem.render.js b/react/src/components/dashboard/coinTile/coinTileItem.render.js index f2aac196e..35f9d4feb 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.render.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.render.js @@ -24,7 +24,11 @@ const CoinTileItemRender = function() {
    - { this.props.Dashboard && this.props.Dashboard.electrumCoins && this.props.Dashboard.electrumCoins[item.coin].serverList && this.props.Dashboard.electrumCoins[item.coin].serverList === 'none' && + { this.props.Dashboard && + this.props.Dashboard.electrumCoins && + this.props.Dashboard.electrumCoins[item.coin] && + this.props.Dashboard.electrumCoins[item.coin].serverList && + this.props.Dashboard.electrumCoins[item.coin].serverList === 'none' && diff --git a/react/src/components/dashboard/sendCoin/sendCoin.js b/react/src/components/dashboard/sendCoin/sendCoin.js index 18deae2c7..3ab1bead7 100644 --- a/react/src/components/dashboard/sendCoin/sendCoin.js +++ b/react/src/components/dashboard/sendCoin/sendCoin.js @@ -431,10 +431,9 @@ class SendCoin extends React.Component { if (this.props.ActiveCoin.mode === 'spv') { const _amount = this.state.amount; const _amountSats = this.state.amount * 100000000; - const _balanceSats = this.props.ActiveCoin.balance.sats; - const _fee = this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].txfee; + const _balanceSats = this.props.ActiveCoin.balance.balanceSats; - if (_amountSats > (Number(_balanceSats) + Number(_fee))) { + if (_amountSats > _balanceSats) { Store.dispatch( triggerToaster( translate('SEND.INSUFFICIENT_FUNDS'), @@ -446,9 +445,9 @@ class SendCoin extends React.Component { } console.warn('send val ' + this.state.amount); - console.warn('send val sats ' + (this.state.amount * 100000000)); + console.warn('send val sats ' + _amountSats); - console.warn(this.props.ActiveCoin.balance.sats); + console.warn(`balance ${this.props.ActiveCoin.balance.balanceSats}`); console.warn(this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].txfee); } From a2b63909caf31af284af5a3464d7283c10e1f3ec Mon Sep 17 00:00:00 2001 From: pbca26 Date: Tue, 10 Oct 2017 16:52:00 +0300 Subject: [PATCH 13/47] spv coin mode hide by default --- assets/mainWindow/js/loading.js | 14 +++++++++++++ react/src/actions/actions/addCoin.js | 3 ++- react/src/actions/actions/electrum.js | 2 +- .../addcoin/addcoinOptionsCrypto.js | 20 +++++++++++++++++-- .../addcoin/coin-selectors.render.js | 2 +- .../components/dashboard/sendCoin/sendCoin.js | 1 + .../walletsData/walletsData.render.js | 6 +++++- 7 files changed, 42 insertions(+), 6 deletions(-) diff --git a/assets/mainWindow/js/loading.js b/assets/mainWindow/js/loading.js index 0eb941742..5a3f837fe 100644 --- a/assets/mainWindow/js/loading.js +++ b/assets/mainWindow/js/loading.js @@ -272,4 +272,18 @@ function normalStart() { $('.dropdown-menu.lite').addClass('hide'); $('.dropdown-menu.native').addClass('hide'); disableModeButtons(); +} + +function init() { + const remote = require('electron').remote; + var window = remote.getCurrentWindow(); + var appConf = remote.getCurrentWindow().appConfig; + + if (!appConf.experimentalFeatures) { + $('#spvBtn').hide(); + $('#spvBtnCarret').hide(); + $('.dropdown-menu.native').css('right', '165px'); + $('#nativeOnlyBtnCarret').css('margin-right', '0'); + $('#settingsBtn').css('margin', '0'); + } } \ No newline at end of file diff --git a/react/src/actions/actions/addCoin.js b/react/src/actions/actions/addCoin.js index ac50c0a14..d015631ce 100644 --- a/react/src/actions/actions/addCoin.js +++ b/react/src/actions/actions/addCoin.js @@ -108,7 +108,8 @@ export function shepherdElectrumAddCoin(coin) { export function addCoin(coin, mode, startupParams) { console.warn(mode); - if (Number(mode) === 0) { + if (mode === 0 || + mode === '0') { console.warn('spv'); return dispatch => { dispatch(shepherdElectrumAddCoin(coin)); diff --git a/react/src/actions/actions/electrum.js b/react/src/actions/actions/electrum.js index 5e6594964..2f9abdd2c 100644 --- a/react/src/actions/actions/electrum.js +++ b/react/src/actions/actions/electrum.js @@ -156,7 +156,7 @@ export function shepherdElectrumTransactions(coin, address) { } export function shepherdElectrumTransactionsState(json) { - json = json.result.listtransactions; + json = json.result; if (json && json.error) { diff --git a/react/src/components/addcoin/addcoinOptionsCrypto.js b/react/src/components/addcoin/addcoinOptionsCrypto.js index 9c71f0dcb..b760743ad 100644 --- a/react/src/components/addcoin/addcoinOptionsCrypto.js +++ b/react/src/components/addcoin/addcoinOptionsCrypto.js @@ -1,17 +1,33 @@ import React from 'react'; import { translate } from '../../translate/translate'; -import Config from '../../config'; class AddCoinOptionsCrypto extends React.Component { constructor() { super(); + this.state = { + isExperimentalOn: false, + }; + } + + componentWillMount() { + let appConfig; + + try { + appConfig = window.require('electron').remote.getCurrentWindow().appConfig; + } catch (e) {} + + this.setState({ + isExperimentalOn: appConfig.experimentalFeatures, + }); } render() { return ( - + ); } diff --git a/react/src/components/addcoin/coin-selectors.render.js b/react/src/components/addcoin/coin-selectors.render.js index 60e21c0d1..8a1ab00b8 100644 --- a/react/src/components/addcoin/coin-selectors.render.js +++ b/react/src/components/addcoin/coin-selectors.render.js @@ -52,7 +52,7 @@ const CoinSelectorsRender = function(item, coin, i) {
    -
    +
    { formatValue(tx.amount) * _amountNegative || translate('DASHBOARD.UNKNOWN') } - { formatValue(tx.interest) } + { tx.interest && + +{ formatValue(Math.abs(tx.interest)) } + } ); } From 1241ce61d9fb4412000871a481cbcb279df01a4a Mon Sep 17 00:00:00 2001 From: pbca26 Date: Tue, 10 Oct 2017 22:40:59 +0300 Subject: [PATCH 14/47] proper errors handling --- react/src/actions/actions/electrum.js | 37 +++++- .../components/dashboard/sendCoin/sendCoin.js | 43 +++++- .../dashboard/sendCoin/sendCoin.render.js | 71 ++++++---- .../walletsBalance/walletsBalance.js | 4 + .../walletsBalance/walletsBalance.render.js | 122 +++++++++--------- .../dashboard/walletsData/walletsData.js | 22 +++- .../walletsData/walletsData.render.js | 2 +- react/src/components/overrides.scss | 4 + 8 files changed, 209 insertions(+), 96 deletions(-) diff --git a/react/src/actions/actions/electrum.js b/react/src/actions/actions/electrum.js index 2f9abdd2c..4a5fa35ff 100644 --- a/react/src/actions/actions/electrum.js +++ b/react/src/actions/actions/electrum.js @@ -206,10 +206,8 @@ export function shepherdElectrumCoinsState(json) { // value in sats export function shepherdElectrumSend(coin, value, sendToAddress, changeAddress) { - // console.log(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/createrawtx?coin=${coin}&address=${sendToAddress}&value=${value}&change=${changeAddress}&gui=true`); - return dispatch => { - return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/createrawtx?coin=${coin}&address=${sendToAddress}&value=${value}&change=${changeAddress}&gui=true&push=true`, { + return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/createrawtx?coin=${coin}&address=${sendToAddress}&value=${value}&change=${changeAddress}&gui=true&push=true&verify=true`, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -228,7 +226,38 @@ export function shepherdElectrumSend(coin, value, sendToAddress, changeAddress) .then(response => response.json()) .then(json => { console.warn(json); - dispatch(sendToAddressState(json.result)); + if (json.msg === 'error') { + dispatch(sendToAddressState(json)); + } else { + dispatch(sendToAddressState(json.result)); + } }); } } + +export function shepherdElectrumSendPreflight(coin, value, sendToAddress, changeAddress) { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/createrawtx?coin=${coin}&address=${sendToAddress}&value=${value}&change=${changeAddress}&gui=true&push=false&verify=true`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + } + }) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'shepherdElectrumSendPreflight', + 'Error', + 'error' + ) + ) + }) + .then(response => response.json()) + .then(json => { + console.warn(json); + resolve(json); + // dispatch(sendToAddressState(json.result)); + }); + }); +} \ No newline at end of file diff --git a/react/src/components/dashboard/sendCoin/sendCoin.js b/react/src/components/dashboard/sendCoin/sendCoin.js index 7b06c399a..5cd331134 100644 --- a/react/src/components/dashboard/sendCoin/sendCoin.js +++ b/react/src/components/dashboard/sendCoin/sendCoin.js @@ -8,7 +8,8 @@ import { sendNativeTx, getKMDOPID, clearLastSendToResponseState, - shepherdElectrumSend + shepherdElectrumSend, + shepherdElectrumSendPreflight } from '../../../actions/actionCreators'; import Store from '../../../store'; import { @@ -39,6 +40,8 @@ class SendCoin extends React.Component { substractFee: false, lastSendToResponse: null, coin: null, + spvVerificationWarning: false, + spvPreflightSendInProgress: false, }; this.updateInput = this.updateInput.bind(this); this.handleSubmit = this.handleSubmit.bind(this); @@ -347,6 +350,8 @@ class SendCoin extends React.Component { if (back) { this.setState({ currentStep: 0, + spvVerificationWarning: false, + spvPreflightSendInProgress: false, }); } else { Store.dispatch(clearLastSendToResponseState()); @@ -363,6 +368,8 @@ class SendCoin extends React.Component { addressSelectorOpen: false, renderAddressDropdown: true, substractFee: false, + spvVerificationWarning: false, + spvPreflightSendInProgress: false, }); } } @@ -370,17 +377,41 @@ class SendCoin extends React.Component { if (step === 1) { if (!this.validateSendFormData()) { return; + } else { + this.setState(Object.assign({}, this.state, { + spvPreflightSendInProgress: this.props.ActiveCoin.mode === 'spv' ? true : false, + currentStep: step, + })); + + // spv pre tx push request + if (this.props.ActiveCoin.mode === 'spv') { + shepherdElectrumSendPreflight( + this.props.ActiveCoin.coin, + this.state.amount * 100000000, + this.state.sendTo, + this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub + ) + .then((sendPreflight) => { + if (sendPreflight && + sendPreflight.msg === 'success') { + this.setState(Object.assign({}, this.state, { + spvVerificationWarning: !sendPreflight.result.utxoVerified, + spvPreflightSendInProgress: false, + })); + } else { + this.setState(Object.assign({}, this.state, { + spvPreflightSendInProgress: false, + })); + } + }); + } } } - if (step === 1 || - step === 2) { + if (step === 2) { this.setState(Object.assign({}, this.state, { currentStep: step, })); - } - - if (step === 2) { this.handleSubmit(); } } diff --git a/react/src/components/dashboard/sendCoin/sendCoin.render.js b/react/src/components/dashboard/sendCoin/sendCoin.render.js index 0225bdb1a..50f21207c 100644 --- a/react/src/components/dashboard/sendCoin/sendCoin.render.js +++ b/react/src/components/dashboard/sendCoin/sendCoin.render.js @@ -216,6 +216,17 @@ export const SendRender = function() {
    } + { this.state.spvPreflightSendInProgress && +
    Verifying transaction data...
    + } + { this.state.spvVerificationWarning && +
    + Warning: your wallet data is verified only by a single server!
    + If you still want to continue press "Confirm". +
    + }
    - - - - - - - - - - - - - - - - - -
    { translate('INDEX.KEY') }{ translate('INDEX.INFO') }
    - Result - - success -
    Transaction ID{ this.props.ActiveCoin.mode === 'spv' ? (this.state.lastSendToResponse && this.state.lastSendToResponse.txid ? this.state.lastSendToResponse.txid : '') : this.state.lastSendToResponse }
    + { this.state.lastSendToResponse && + !this.state.lastSendToResponse.msg && + + + + + + + + + + + + + + + + + +
    { translate('INDEX.KEY') }{ translate('INDEX.INFO') }
    + Result + + success +
    Transaction ID{ this.props.ActiveCoin.mode === 'spv' ? (this.state.lastSendToResponse && this.state.lastSendToResponse.txid ? this.state.lastSendToResponse.txid : '') : this.state.lastSendToResponse }
    + } + { !this.state.lastSendToResponse && +
    Processing transaction...
    + } + { this.state.lastSendToResponse && + this.state.lastSendToResponse.msg && + this.state.lastSendToResponse.msg === 'error' && +
    +
    + Error +
    +
    { this.state.lastSendToResponse.result }
    +
    + }
    diff --git a/react/src/components/dashboard/walletsBalance/walletsBalance.js b/react/src/components/dashboard/walletsBalance/walletsBalance.js index e9c939e2b..4026cc25a 100755 --- a/react/src/components/dashboard/walletsBalance/walletsBalance.js +++ b/react/src/components/dashboard/walletsBalance/walletsBalance.js @@ -53,6 +53,10 @@ class WalletsBalance extends React.Component { let _balance = 0; const _mode = this.props.ActiveCoin.mode; + if (this.props.ActiveCoin.balance === 'connection error or incomplete data') { + _balance = '-777'; + } + if (_mode === 'native') { if (type === 'total' && this.props.ActiveCoin.balance && diff --git a/react/src/components/dashboard/walletsBalance/walletsBalance.render.js b/react/src/components/dashboard/walletsBalance/walletsBalance.render.js index 84265dc9d..4f2121db6 100644 --- a/react/src/components/dashboard/walletsBalance/walletsBalance.render.js +++ b/react/src/components/dashboard/walletsBalance/walletsBalance.render.js @@ -8,90 +8,92 @@ const WalletsBalanceRender = function() {
    -
    -
    -
    -
    - -
    -
    -
    - { this.props.ActiveCoin.coin !== 'CHIPS' && this.props.ActiveCoin.mode !== 'spv' && - - } - { this.props.ActiveCoin.coin === 'CHIPS' || this.props.ActiveCoin.mode === 'spv' ? translate('INDEX.BALANCE') : translate('INDEX.TRANSPARENT_BALANCE') } + { this.renderBalance('transparent') !== -777 && +
    +
    +
    +
    + +
    +
    +
    + { this.props.ActiveCoin.coin !== 'CHIPS' && this.props.ActiveCoin.mode !== 'spv' && + + } + { this.props.ActiveCoin.coin === 'CHIPS' || this.props.ActiveCoin.mode === 'spv' ? translate('INDEX.BALANCE') : translate('INDEX.TRANSPARENT_BALANCE') } +
    + + { Config.roundValues ? formatValue(this.renderBalance('transparent')) : this.renderBalance('transparent') } +
    - - { Config.roundValues ? formatValue(this.renderBalance('transparent')) : this.renderBalance('transparent') } -
    -
    - -
    0 ? 'col-lg-3 col-xs-12' : 'hide' }> -
    -
    -
    -
    - - { translate('INDEX.Z_BALANCE') } -
    - - { Config.roundValues ? formatValue(this.renderBalance('private')) : this.renderBalance('private') } - -
    -
    -
    -
    -
    0 ? 'col-lg-3 col-xs-12' : 'hide' }> -
    -
    +
    0 ? 'col-lg-3 col-xs-12' : 'hide' }> +
    - - { translate('INDEX.INTEREST_EARNED') } + + { translate('INDEX.Z_BALANCE') }
    - { Config.roundValues ? formatValue(this.renderBalance('interest')) : this.renderBalance('interest') } + title={ this.renderBalance('private') }> + { Config.roundValues ? formatValue(this.renderBalance('private')) : this.renderBalance('private') }
    -
    -
    -
    -
    -
    -
    -
    - - { translate('INDEX.TOTAL_BALANCE') } +
    0 ? 'col-lg-3 col-xs-12' : 'hide' }> +
    +
    +
    +
    +
    + + { translate('INDEX.INTEREST_EARNED') } +
    + + { Config.roundValues ? formatValue(this.renderBalance('interest')) : this.renderBalance('interest') } + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + { translate('INDEX.TOTAL_BALANCE') } +
    + + { Config.roundValues ? formatValue(this.renderBalance('total')) : this.renderBalance('total') } +
    - - { Config.roundValues ? formatValue(this.renderBalance('total')) : this.renderBalance('total') } -
    -
    + }
    ); }; diff --git a/react/src/components/dashboard/walletsData/walletsData.js b/react/src/components/dashboard/walletsData/walletsData.js index 6905c8e9d..e5a166cf2 100644 --- a/react/src/components/dashboard/walletsData/walletsData.js +++ b/react/src/components/dashboard/walletsData/walletsData.js @@ -211,6 +211,8 @@ class WalletsData extends React.Component { if (this.props.ActiveCoin.txhistory && this.props.ActiveCoin.txhistory !== 'loading' && this.props.ActiveCoin.txhistory !== 'no data' && + this.props.ActiveCoin.txhistory !== 'connection error or incomplete data' && + this.props.ActiveCoin.txhistory !== 'cant get current height' && this.props.ActiveCoin.txhistory.length) { _stateChange = Object.assign({}, _stateChange, { itemsList: this.props.ActiveCoin.txhistory, @@ -230,6 +232,11 @@ class WalletsData extends React.Component { _stateChange = Object.assign({}, _stateChange, { itemsList: 'loading', }); + } else if ((this.props.ActiveCoin.txhistory && this.props.ActiveCoin.txhistory === 'connection error or incomplete data') || + (this.props.ActiveCoin.txhistory && this.props.ActiveCoin.txhistory === 'cant get current height')) { + _stateChange = Object.assign({}, _stateChange, { + itemsList: 'connection error', + }); } this.setState(Object.assign({}, _stateChange)); @@ -270,6 +277,17 @@ class WalletsData extends React.Component { { translate('INDEX.NO_DATA') } ); + } else if (this.state.itemsList === 'connection error') { + return ( + + + Connection error! + +
    Try to connect to another SPV server. +
    + + + ); } else if (this.state.itemsList && this.state.itemsList.length) { return TxHistoryListRender.call(this); } @@ -448,9 +466,7 @@ const mapStateToProps = (state) => { showTransactionInfo: state.ActiveCoin.showTransactionInfo, progress: state.ActiveCoin.progress, }, - Main: { - coins: state.Main.coins, - }, + Main: state.Main, Dashboard: state.Dashboard, }; }; diff --git a/react/src/components/dashboard/walletsData/walletsData.render.js b/react/src/components/dashboard/walletsData/walletsData.render.js index c9cef24fe..c1af55a15 100644 --- a/react/src/components/dashboard/walletsData/walletsData.render.js +++ b/react/src/components/dashboard/walletsData/walletsData.render.js @@ -211,7 +211,7 @@ export const WalletsDataRender = function() {
    - { (this.props.ActiveCoin.txhistory !== 'loading' && this.props.ActiveCoin.txhistory !== 'no data') && + { (this.props.ActiveCoin.txhistory !== 'loading' && this.props.ActiveCoin.txhistory !== 'no data' && this.props.ActiveCoin.txhistory !== 'connection error') &&
    Date: Tue, 10 Oct 2017 13:26:17 -0700 Subject: [PATCH 15/47] 'AGT-99' --- react/src/actions/actions/addCoin.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/react/src/actions/actions/addCoin.js b/react/src/actions/actions/addCoin.js index d015631ce..238d48c52 100644 --- a/react/src/actions/actions/addCoin.js +++ b/react/src/actions/actions/addCoin.js @@ -273,8 +273,20 @@ export function addCoinResult(coin, mode) { if (Number(mode) === 0) { dispatch(activeHandle()); dispatch(shepherdElectrumCoins()); + } else { + setTimeout(function() { + dispatch(activeHandle()); + dispatch(getDexCoins()); + }, 500); + setTimeout(function() { + dispatch(activeHandle()); + dispatch(getDexCoins()); + }, 1000); + setTimeout(function() { + dispatch(activeHandle()); + dispatch(getDexCoins()); + }, 5000); } - dispatch(getDexCoins()); } } From d3c1e88d8a978761e0c02c68400b32b5ebf367ee Mon Sep 17 00:00:00 2001 From: pbca26 Date: Wed, 11 Oct 2017 12:23:38 +0300 Subject: [PATCH 16/47] AGT-103 --- react/src/actions/actions/addCoin.js | 16 ++++++++++++++++ .../dashboard/walletsData/walletsData.js | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/react/src/actions/actions/addCoin.js b/react/src/actions/actions/addCoin.js index 238d48c52..20fe2562a 100644 --- a/react/src/actions/actions/addCoin.js +++ b/react/src/actions/actions/addCoin.js @@ -273,7 +273,23 @@ export function addCoinResult(coin, mode) { if (Number(mode) === 0) { dispatch(activeHandle()); dispatch(shepherdElectrumCoins()); + + setTimeout(function() { + dispatch(activeHandle()); + dispatch(shepherdElectrumCoins()); + }, 500); + setTimeout(function() { + dispatch(activeHandle()); + dispatch(shepherdElectrumCoins()); + }, 1000); + setTimeout(function() { + dispatch(activeHandle()); + dispatch(shepherdElectrumCoins()); + }, 2000); } else { + dispatch(activeHandle()); + dispatch(getDexCoins()); + setTimeout(function() { dispatch(activeHandle()); dispatch(getDexCoins()); diff --git a/react/src/components/dashboard/walletsData/walletsData.js b/react/src/components/dashboard/walletsData/walletsData.js index e5a166cf2..5918200fb 100644 --- a/react/src/components/dashboard/walletsData/walletsData.js +++ b/react/src/components/dashboard/walletsData/walletsData.js @@ -283,7 +283,7 @@ class WalletsData extends React.Component { Connection error! -
    Try to connect to another SPV server. +
    Try to connect to another SPV server. To do that go to "Settings" -> "SPV Server List" tab, choose new server and click "OK".
    From cbbabc3cc9be9de8a84b3b64865266afe0a9c770 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Wed, 11 Oct 2017 12:48:41 +0300 Subject: [PATCH 17/47] jumblr deposit address toaster fix --- react/src/components/dashboard/jumblr/jumblr.js | 7 ++++--- react/src/components/dashboard/receiveCoin/receiveCoin.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/react/src/components/dashboard/jumblr/jumblr.js b/react/src/components/dashboard/jumblr/jumblr.js index 5e669b3ed..cc74b8fdd 100755 --- a/react/src/components/dashboard/jumblr/jumblr.js +++ b/react/src/components/dashboard/jumblr/jumblr.js @@ -358,7 +358,7 @@ class Jumblr extends React.Component { 'error' ) ); - } else if (json.result && json.result.result === 0) { + } else if (json.result && (json.result.result === 0 || json.result.result === null)) { this.setState(Object.assign({}, this.state, { jumblrDepositAddress: { address: _genKeys.address, @@ -367,9 +367,10 @@ class Jumblr extends React.Component { })); Store.dispatch( triggerToaster( - translate('TOASTR.JUMBLR_DEPOSIT_ADDRESS_SET'), + translate('TOASTR.JUMBLR_DEPOSIT_ADDRESS_SET') + ' to ' + _genKeys.address, 'Jumblr', - 'success' + 'success', + false ) ); } diff --git a/react/src/components/dashboard/receiveCoin/receiveCoin.js b/react/src/components/dashboard/receiveCoin/receiveCoin.js index d312f9c40..d80bf2ad0 100644 --- a/react/src/components/dashboard/receiveCoin/receiveCoin.js +++ b/react/src/components/dashboard/receiveCoin/receiveCoin.js @@ -126,7 +126,7 @@ class ReceiveCoin extends React.Component { return items; } else { - if (this.props.electrumCoins && + if (this.props.electrumCoins && this.props.mode === 'spv' && type === 'public') { let items = []; From 0c7363e2cd5485300147b6db302f84a4e038b4f6 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Wed, 11 Oct 2017 15:21:36 +0300 Subject: [PATCH 18/47] mnz ac native --- react/src/assets/images/cryptologo/mnz.png | Bin 0 -> 6502 bytes react/src/components/addcoin/addcoinOptionsAC.js | 1 + react/src/components/addcoin/payload.js | 9 ++++++++- react/src/translate/en.js | 2 +- react/src/util/coinHelper.js | 4 ++++ 5 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 react/src/assets/images/cryptologo/mnz.png diff --git a/react/src/assets/images/cryptologo/mnz.png b/react/src/assets/images/cryptologo/mnz.png new file mode 100644 index 0000000000000000000000000000000000000000..4be73856366f88257836ab534a3105ab6be35b43 GIT binary patch literal 6502 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 f%4b;O*ndM{Kg$LL3L}UB-T_jQQ Date: Wed, 11 Oct 2017 20:15:09 +0300 Subject: [PATCH 19/47] spv trigger dashboard update if server was changed --- react/src/actions/actionCreators.js | 8 +++++++ react/src/actions/storeType.js | 3 ++- react/src/assets/images/cryptologo/mnz.png | Bin 6502 -> 19296 bytes .../dashboard/coinTile/coinTileItem.js | 21 ++++++++++++++++++ .../settings/settings.spvServersPanel.js | 2 ++ .../dashboard/walletsData/walletsData.js | 3 ++- react/src/reducers/dashboard.js | 7 ++++++ 7 files changed, 42 insertions(+), 2 deletions(-) diff --git a/react/src/actions/actionCreators.js b/react/src/actions/actionCreators.js index 7e5d4c742..e8cc0582d 100644 --- a/react/src/actions/actionCreators.js +++ b/react/src/actions/actionCreators.js @@ -27,6 +27,7 @@ import { GET_PIN_LIST, DASHBOARD_SYNC_ONLY_UPDATE, DISPLAY_IMPORT_KEY_MODAL, + ELECTRUM_SERVER_CHANGED, } from './storeType'; export * from './actions/nativeSyncInfo'; @@ -279,4 +280,11 @@ export function displayImportKeyModal(display) { type: DISPLAY_IMPORT_KEY_MODAL, displayImportKeyModal: display, } +} + +export function electrumServerChanged(isChanged) { + return { + type: ELECTRUM_SERVER_CHANGED, + eletrumServerChanged: isChanged, + } } \ No newline at end of file diff --git a/react/src/actions/storeType.js b/react/src/actions/storeType.js index e25876ae1..59e282edf 100644 --- a/react/src/actions/storeType.js +++ b/react/src/actions/storeType.js @@ -44,4 +44,5 @@ export const DISPLAY_IMPORT_KEY_MODAL = 'DISPLAY_IMPORT_KEY_MODAL'; export const LOGIN = 'LOGIN'; export const DASHBOARD_ELECTRUM_BALANCE = 'DASHBOARD_ELECTRUM_BALANCE'; export const DASHBOARD_ELECTRUM_TRANSACTIONS = 'DASHBOARD_ELECTRUM_TRANSACTIONS'; -export const DASHBOARD_ELECTRUM_COINS = 'DASHBOARD_ELECTRUM_COINS'; \ No newline at end of file +export const DASHBOARD_ELECTRUM_COINS = 'DASHBOARD_ELECTRUM_COINS'; +export const ELECTRUM_SERVER_CHANGED = 'ELECTRUM_SERVER_CHANGED'; \ No newline at end of file diff --git a/react/src/assets/images/cryptologo/mnz.png b/react/src/assets/images/cryptologo/mnz.png index 4be73856366f88257836ab534a3105ab6be35b43..1c1a2affa771ee3485f03c4f99f22917dcb29559 100644 GIT binary patch literal 19296 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 FV1n5JnnwTt literal 6502 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 f%4b;O*ndM{Kg$LL3L}UB-T_jQQ { + Store.dispatch(electrumServerChanged(false)); + }, 100); + } + } + render() { return CoinTileItemRender.call(this); } } + const mapStateToProps = (state) => { return { ActiveCoin: { coin: state.ActiveCoin.coin, + mode: state.ActiveCoin.mode, addresses: state.ActiveCoin.addresses, mainBasiliskAddress: state.ActiveCoin.mainBasiliskAddress, progress: state.ActiveCoin.progress, diff --git a/react/src/components/dashboard/settings/settings.spvServersPanel.js b/react/src/components/dashboard/settings/settings.spvServersPanel.js index 50490a773..40f3ce1ec 100644 --- a/react/src/components/dashboard/settings/settings.spvServersPanel.js +++ b/react/src/components/dashboard/settings/settings.spvServersPanel.js @@ -5,6 +5,7 @@ import { shepherdElectrumCheckServerConnection, shepherdElectrumSetServer, shepherdElectrumCoins, + electrumServerChanged, triggerToaster, } from '../../../actions/actionCreators'; import Store from '../../../store'; @@ -47,6 +48,7 @@ class SPVServersPanel extends React.Component { 'success' ) ); + Store.dispatch(electrumServerChanged(true)); Store.dispatch(shepherdElectrumCoins()); }); } else { diff --git a/react/src/components/dashboard/walletsData/walletsData.js b/react/src/components/dashboard/walletsData/walletsData.js index 5918200fb..8a68f4416 100644 --- a/react/src/components/dashboard/walletsData/walletsData.js +++ b/react/src/components/dashboard/walletsData/walletsData.js @@ -283,7 +283,8 @@ class WalletsData extends React.Component { Connection error! -
    Try to connect to another SPV server. To do that go to "Settings" -> "SPV Server List" tab, choose new server and click "OK". +
    Try to connect to another SPV server. +
    To do that go to "Settings", select "SPV Server List" tab, choose new server and click "OK".
    diff --git a/react/src/reducers/dashboard.js b/react/src/reducers/dashboard.js index cc3cfdf68..94d37e51e 100644 --- a/react/src/reducers/dashboard.js +++ b/react/src/reducers/dashboard.js @@ -6,6 +6,7 @@ import { DASHBOARD_SYNC_ONLY_UPDATE, DISPLAY_IMPORT_KEY_MODAL, DASHBOARD_ELECTRUM_COINS, + ELECTRUM_SERVER_CHANGED, } from '../actions/storeType'; export function Dashboard(state = { @@ -16,6 +17,7 @@ export function Dashboard(state = { skipFullDashboardUpdate: false, displayImportKeyModal: false, electrumCoins: {}, + eletrumServerChanged: false, }, action) { switch (action.type) { case DASHBOARD_ELECTRUM_COINS: @@ -56,6 +58,11 @@ export function Dashboard(state = { ...state, skipFullDashboardUpdate: action.skipFullDashboardUpdate, }; + case ELECTRUM_SERVER_CHANGED: + return { + ...state, + eletrumServerChanged: action.eletrumServerChanged, + }; default: return state; } From dd896ef840c9024e9803cec925f23552e64c4996 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Wed, 11 Oct 2017 20:21:40 +0300 Subject: [PATCH 20/47] jumblr placeholder render fix --- react/src/components/dashboard/jumblr/jumblr.render.js | 2 +- react/src/translate/en.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/react/src/components/dashboard/jumblr/jumblr.render.js b/react/src/components/dashboard/jumblr/jumblr.render.js index f2c919dd6..ddad101cd 100644 --- a/react/src/components/dashboard/jumblr/jumblr.render.js +++ b/react/src/components/dashboard/jumblr/jumblr.render.js @@ -206,7 +206,7 @@ export const JumblrRender = function() {

    { translate('JUMBLR.THIS_IS_YOUR_MAIN_RECOVERY') }

    { translate('JUMBLR.ALL_JUMBLR_ADDRESSES_CAN_BE') }

    - { translate('JUMBLR.TIP') }: { translate('JUMBLR.DONT_USE_SMART_EDITORS') } + { translate('JUMBLR.TIP') }: { this.renderLB('JUMBLR.DONT_USE_SMART_EDITORS') }

    diff --git a/react/src/translate/en.js b/react/src/translate/en.js index 7d8ea770b..351da3d22 100644 --- a/react/src/translate/en.js +++ b/react/src/translate/en.js @@ -333,7 +333,7 @@ export const _lang = { 'PLEASE_WRITE_DOWN_PASSPHRASE': 'Please write down your Jumblr passphrase and keept it safe.', 'THIS_IS_YOUR_MAIN_RECOVERY': 'This is your main recovery passphrase.', 'ALL_JUMBLR_ADDRESSES_CAN_BE': 'All Jumblr addresses can be regenrated based on it.', - 'DONT_USE_SMART_EDITORS': 'do not use smart editors to store your passphrase as they tend to add extra characters.
    This may result in passphrase mismatch with the original passphrase.', + 'DONT_USE_SMART_EDITORS': 'do not use smart editors to store your passphrase as they tend to add extra characters.
    This may result in passphrase mismatch with the original passphrase.', 'PASSPHRASE_COPIED': 'Passphrase copied', 'CREATE_JUMBLR_DEPOSIT_ADDRESS': 'Create Jumblr deposit address', 'YOUR_JUMBLR_DEPOSIT_ADDRESS': 'Your Jumblr deposit address', From 8e510baf53690e1f5053a2a24ed7fc47e9c16f50 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Thu, 12 Oct 2017 16:18:18 +0300 Subject: [PATCH 21/47] AGT-66 user friendly debug.log enoent message --- react/src/actions/actions/addCoin.js | 4 + .../components/addcoin/addcoinOptionsAC.js | 2 +- .../dashboard/_sendCoin/sendCoin.js | 917 ------------------ .../dashboard/_sendCoin/sendCoin.render.js | 402 -------- .../claimInterestModal/claimInterestModal.js | 2 + .../dashboard/coinTile/coinTileItem.js | 2 - .../coindDownModal/coindDownModal.render.js | 8 +- .../src/components/dashboard/jumblr/jumblr.js | 2 + 8 files changed, 16 insertions(+), 1323 deletions(-) delete mode 100644 react/src/components/dashboard/_sendCoin/sendCoin.js delete mode 100644 react/src/components/dashboard/_sendCoin/sendCoin.render.js diff --git a/react/src/actions/actions/addCoin.js b/react/src/actions/actions/addCoin.js index 20fe2562a..bf0fe81be 100644 --- a/react/src/actions/actions/addCoin.js +++ b/react/src/actions/actions/addCoin.js @@ -273,18 +273,22 @@ export function addCoinResult(coin, mode) { if (Number(mode) === 0) { dispatch(activeHandle()); dispatch(shepherdElectrumCoins()); + dispatch(getDexCoins()); setTimeout(function() { dispatch(activeHandle()); dispatch(shepherdElectrumCoins()); + dispatch(getDexCoins()); }, 500); setTimeout(function() { dispatch(activeHandle()); dispatch(shepherdElectrumCoins()); + dispatch(getDexCoins()); }, 1000); setTimeout(function() { dispatch(activeHandle()); dispatch(shepherdElectrumCoins()); + dispatch(getDexCoins()); }, 2000); } else { dispatch(activeHandle()); diff --git a/react/src/components/addcoin/addcoinOptionsAC.js b/react/src/components/addcoin/addcoinOptionsAC.js index 8745cdc35..5376a0487 100644 --- a/react/src/components/addcoin/addcoinOptionsAC.js +++ b/react/src/components/addcoin/addcoinOptionsAC.js @@ -25,7 +25,7 @@ class AddCoinOptionsAC extends React.Component { let _items = []; for (let i = 0; i < _assetChains.length; i++) { - const availableModes = _assetChains[i] === 'revs' || _assetChains[i] === 'jumblr' ? 'native|spv' : 'native'; + const availableModes = _assetChains[i] === 'revs' || _assetChains[i] === 'jumblr' || _assetChains[i] === 'wlc' || _assetChains[i] === 'mnz' ? 'native|spv' : 'native'; _items.push(
    - ); - } - - 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( -
    { _lastSendToResponse.substring(i * substrLength, substrLength * i + substrLength) }
    - ); - } - - 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 ( - { _lastSendToResponse[key] === true ? 'true' : 'success' } - ); - } else { - if (key === 'result' && - _lastSendToResponse.result && - typeof _lastSendToResponse.result !== 'object') { - return ( - { _lastSendToResponse.result } - ); - } else { - return ( - false - ); - } - } - } else if (key === 'error') { - const _lastSendToResponse = this.props.ActiveCoin.lastSendToResponse; - - if (Object.keys(_lastSendToResponse[key]).length) { - return ( - { JSON.stringify(_lastSendToResponse[key], null, '\t') } - ); - } else { - return ( - { _lastSendToResponse[key] } - ); - } - } else if (key === 'sendrawtransaction') { - const _lastSendToResponse = this.props.ActiveCoin.lastSendToResponse; - - if (_lastSendToResponse[key] === 'success') { - return ( - true - ); - } else { - return ( - false - ); - } - } else if (key === 'txid' || key === 'sent') { - const _lastSendToResponse = this.props.ActiveCoin.lastSendToResponse; - - return ( - { _lastSendToResponse[key] } - ); - } 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); \ No newline at end of file diff --git a/react/src/components/dashboard/_sendCoin/sendCoin.render.js b/react/src/components/dashboard/_sendCoin/sendCoin.render.js deleted file mode 100644 index 4ca3d8de7..000000000 --- a/react/src/components/dashboard/_sendCoin/sendCoin.render.js +++ /dev/null @@ -1,402 +0,0 @@ -import React from 'react'; -import { translate } from '../../../translate/translate'; -import { - secondsElapsedToString, - secondsToString -} from '../../../util/time'; - -import QRModal from '../qrModal/qrModal'; - -export const UTXOCacheInfoRender = function(refreshCacheData, isReadyToUpdate, waitUntilCallIsFinished, timestamp) { - const _progress = 100 - this.state.currentStackLength * 100 / this.state.totalStackLength; - - return ( -
    -
    - { translate('SEND.TOTAL_UTXO_AVAILABLE') }: - { refreshCacheData ? refreshCacheData.data && refreshCacheData.data.length : translate('SEND.PRESS_UPDATE_BTN') }
    -
    - { translate('SEND.LAST_UPDATED') } @ - { secondsToString(refreshCacheData ? refreshCacheData.timestamp : 0, true) } |  - { secondsElapsedToString(timestamp || 0) }&nbps; - { translate('SEND.AGO') }
    -
    -
    - { translate('SEND.NEXT_UPDATE_IN') } { secondsElapsedToString(600 - timestamp) }s -
    -
    -
    - { translate('SEND.PROCESSING_REQ') }: { this.state.currentStackLength } / { this.state.totalStackLength } -
    -
    - -
    - ); -}; - -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( - - { key } - { this.renderKey(key) } - - ); - } - } - - return items; - } else { - return ( - - -
    -
    - { translate('SEND.PROCESSING_TRANSACTION') }...
    - { translate('SEND.NOTE_IT_WILL_TAKE') }. -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - ); - } -} - -export const OASendUIRender = function() { - return ( -
    -
    - - -
    -
    - -
    -
    - ); -}; - -export const SendApiTypeSelectorRender = function() { - return ( -
    -
    - - -
    - { translate('SEND.SEND_VIA') } (sendtoaddress API) -
    -
    -
    -
    - -
    -
    - ); -}; - -export const SendCoinRender = function() { - return ( -
    -
    -
    -
    - 1 -
    - { translate('INDEX.FILL_SEND_FORM') } -

    { translate('INDEX.FILL_SEND_DETAILS') }

    -
    -
    -
    - 2 -
    - { translate('INDEX.CONFIRMING') } -

    { translate('INDEX.CONFIRM_DETAILS') }

    -
    -
    -
    - 3 -
    - { translate('INDEX.PROCESSING_TX') } -

    { translate('INDEX.PROCESSING_DETAILS') }

    -
    -
    -
    - -
    -
    -

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

    -
    -
    -
    - { this.renderSendApiTypeSelector() } -
    -
    - - { this.renderAddressList() } -
    -
    - { this.renderOASendUI() } -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - { translate('INDEX.TOTAL') }  - ({ translate('INDEX.AMOUNT_SM') } - { translate('INDEX.FEE') }): -   - { this.getTotalAmount() } { this.props.ActiveCoin.coin } -
    -
    - - -
    - { translate('INDEX.DONT_SEND') } -
    -
    -
    - { this.renderUTXOCacheInfo()} -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - { translate('INDEX.TO') } -
    -
    { this.state.sendTo }
    -
    - { this.state.amount } { this.props.ActiveCoin.coin } -
    -
    { translate('INDEX.TX_FEE_REQ') }
    -
    - { this.state.fee } { this.props.ActiveCoin.coin } -
    -
    -
    - -
    -
    - { translate('INDEX.FROM') } -
    -
    - { this.props.Dashboard.activeHandle[this.props.ActiveCoin.coin] } -
    -
    - { Number(this.state.amount) - Number(this.state.fee) } { this.props.ActiveCoin.coin } -
    -
    -
    - this.changeSendCoinStep(0) }>{ translate('INDEX.BACK') } -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -

    - { translate('INDEX.TRANSACTION_RESULT') } -

    -
    - { translate('SEND.YOU_PICKED_OPT') } -
    - - - - - - - - - { this.renderSendCoinResponse() } - -
    { translate('INDEX.KEY') }{ translate('INDEX.INFO') }
    -
    -
    - -
    -
    -
    -
    -
    -
    - ); -}; \ No newline at end of file diff --git a/react/src/components/dashboard/claimInterestModal/claimInterestModal.js b/react/src/components/dashboard/claimInterestModal/claimInterestModal.js index cd9e7324b..9acbbcc56 100755 --- a/react/src/components/dashboard/claimInterestModal/claimInterestModal.js +++ b/react/src/components/dashboard/claimInterestModal/claimInterestModal.js @@ -16,6 +16,8 @@ import { _ClaimInterestTableRender } from './claimInterestModal.render'; +// TODO: promises + class ClaimInterestModal extends React.Component { constructor() { super(); diff --git a/react/src/components/dashboard/coinTile/coinTileItem.js b/react/src/components/dashboard/coinTile/coinTileItem.js index 398df93e8..746759a10 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.js @@ -165,14 +165,12 @@ class CoinTileItem extends React.Component { } componentWillReceiveProps(props) { - console.warn(props); if (this.props && this.props.Dashboard && this.props.Dashboard.eletrumServerChanged && this.props.ActiveCoin.mode === 'spv' && this.props.Dashboard && this.props.Dashboard.activeSection === 'wallets') { - console.warn('trigger spv dashboard update'); Store.dispatch(shepherdElectrumBalance(this.props.ActiveCoin.coin, this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub)); Store.dispatch(shepherdElectrumTransactions(this.props.ActiveCoin.coin, this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub)); Store.dispatch(electrumServerChanged(false)); diff --git a/react/src/components/dashboard/coindDownModal/coindDownModal.render.js b/react/src/components/dashboard/coindDownModal/coindDownModal.render.js index 4b2259670..c35756d55 100644 --- a/react/src/components/dashboard/coindDownModal/coindDownModal.render.js +++ b/react/src/components/dashboard/coindDownModal/coindDownModal.render.js @@ -2,6 +2,12 @@ import React from 'react'; import { translate } from '../../../translate/translate'; const CoindDownModalRender = function() { + let _debuglog = this.props.debugLog || ''; + + if (_debuglog.indexOf('ENOENT') > -1) { + _debuglog = 'Error: ' + (this.props.ActiveCoin.coin === 'KMD' ? 'Komodod' : `Komodod / ${this.props.ActiveCoin.coin}`) + ' debug.log is empty. Looks like daemon didn\'t start properly. Please retry.'; + } + return (
    + value={ _debuglog }>
    +
    +
    +
    +
    +
    + }
    diff --git a/react/src/components/dashboard/walletsInfo/walletsInfo.js b/react/src/components/dashboard/walletsInfo/walletsInfo.js index 38d950394..6366b1cd4 100644 --- a/react/src/components/dashboard/walletsInfo/walletsInfo.js +++ b/react/src/components/dashboard/walletsInfo/walletsInfo.js @@ -8,12 +8,22 @@ class WalletsInfo extends React.Component { constructor() { super(); this.openClaimInterestModal = this.openClaimInterestModal.bind(this); + this.displayClaimInterestUI = this.displayClaimInterestUI.bind(this); } openClaimInterestModal() { Store.dispatch(toggleClaimInterestModal(true)); } + displayClaimInterestUI() { + if (this.props.ActiveCoin && + this.props.ActiveCoin.balance && + this.props.ActiveCoin.balance.balance && + this.props.ActiveCoin.balance.balance > 0) { + return true; + } + } + render() { if (this.props && this.props.ActiveCoin && diff --git a/react/src/components/dashboard/walletsInfo/walletsInfo.render.js b/react/src/components/dashboard/walletsInfo/walletsInfo.render.js index 0ed75c4fa..243cfd69f 100644 --- a/react/src/components/dashboard/walletsInfo/walletsInfo.render.js +++ b/react/src/components/dashboard/walletsInfo/walletsInfo.render.js @@ -1,6 +1,5 @@ import React from 'react'; import { translate } from '../../../translate/translate'; -import ClaimInterestModal from '../claimInterestModal/claimInterestModal'; const WalletsInfoRender = function() { if (this.props.ActiveCoin.mode === 'native') { @@ -44,13 +43,12 @@ const WalletsInfoRender = function() {
    - { this.props.ActiveCoin.coin === 'KMD' && + { this.props.ActiveCoin.coin === 'KMD' && this.displayClaimInterestUI() &&
    -
    }
    diff --git a/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.js b/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.js new file mode 100644 index 000000000..338474361 --- /dev/null +++ b/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.js @@ -0,0 +1,206 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { + toggleZcparamsFetchModal, + downloadZCashParamsPromise, +} from '../../../actions/actionCreators'; +import Store from '../../../store'; +import Config from '../../../config'; +import { translate } from '../../../translate/translate'; + +import ZcparamsFetchModalRender from './zcparamsFetchModal.render'; + +import { SocketProvider } from 'socket.io-react'; +import io from 'socket.io-client'; + +const socket = io.connect(`http://127.0.0.1:${Config.agamaPort}`); + +let updateProgressBar = { + zcparams: { + proving: -1, + verifying: -1, + }, +}; + +class ZcparamsFetchModal extends React.Component { + constructor() { + super(); + this.state = { + display: true, + updateLog: [], + zcparamsSources: {}, + dlOption: 'agama.komodoplatform.com', + done: false, + }; + this.dismiss = this.dismiss.bind(this); + this._downloadZCashParamsPromise = this._downloadZCashParamsPromise.bind(this); + } + + _downloadZCashParamsPromise() { + let _updateLog = []; + + updateProgressBar.zcparams = { + proving: 0, + verifying: 0, + }; + _updateLog.push(`Downloading Zcash keys...`); + this.setState(Object.assign({}, this.state, { + updateLog: _updateLog, + done: false, + })); + + downloadZCashParamsPromise(this.state.dlOption); + } + + dismiss() { + Store.dispatch(toggleZcparamsFetchModal(false)); + } + + componentWillUnmount() { + socket.removeAllListeners('zcparams', msg => this.updateSocketsData(msg)); + } + + componentWillMount() { + let _zcparamsSources; + + socket.on('zcparams', msg => this.updateSocketsData(msg)); + + try { + _zcparamsSources = window.require('electron').remote.getCurrentWindow().zcashParamsDownloadLinks; + } catch (e) {} + + this.setState(Object.assign({}, this.state, { + zcparamsSources: _zcparamsSources, + })); + } + + componentWillReceiveProps(nextProps) { + if (this.props.displayZcparamsModal !== nextProps.displayZcparamsModal) { + this.setState(Object.assign({}, this.state, { + display: nextProps.displayZcparamsModal, + })); + } + } + + updateSocketsData(data) { + if (data && + data.msg && + data.msg.type === 'zcpdownload') { + if (data.msg.status === 'progress' && + data.msg.progress && + data.msg.progress < 100) { + this.setState(Object.assign({}, this.state, { + updateProgressPatch: data.msg.progress, + })); + updateProgressBar.zcparams[data.msg.file] = data.msg.progress; + } else { + if (data.msg.status === 'progress' && + data.msg.progress && + data.msg.progress === 100) { + let _updateLog = this.state.updateLog; + this.setState(Object.assign({}, this.state, { + updateLog: _updateLog, + })); + updateProgressBar.zcparams[data.msg.file] = 100; + } else if (data.msg.status === 'done') { + let _updateLog = this.state.updateLog; + + if (data.msg.file === 'proving') { + _updateLog = []; + _updateLog.push('Both Zcash param keys are downloaded and verified!'); + _updateLog.push('Please restart the app.'); + this.setState(Object.assign({}, this.state, { + updateLog: _updateLog, + done: true, + })); + } else { + this.setState(Object.assign({}, this.state, { + updateLog: _updateLog, + })); + } + updateProgressBar.zcparams[data.msg.file] = -1; + } else if (data.msg.status === 'error') { + let _updateLog = this.state.updateLog; + + _updateLog.push(`Zcash param ${data.msg.file} verification error!`); + this.setState(Object.assign({}, this.state, { + updateLog: _updateLog, + done: true, + })); + updateProgressBar.zcparams = { + proving: -1, + verifying: -1, + }; + } + } + } + } + + renderUpdateStatus() { + const _updateLogLength = this.state.updateLog.length; + let items = []; + let patchProgressBar = null; + + for (let i = 0; i < _updateLogLength; i++) { + items.push( +
    { this.state.updateLog[i] }
    + ); + } + + if (_updateLogLength) { + return ( +
    +
    { translate('SETTINGS.PROGRESS') }:
    +
    { items }
    +
    -1 && !this.state.done ? 'progress progress-sm' : 'hide' }> +
    +
    +
    +
    + ); + } else { + return null; + } + } + + renderDLOptions() { + let _items = []; + let _dlOptions = this.state.zcparamsSources; + + for (let key in _dlOptions) { + _items.push( + + ); + } + + return _items; + } + + updateInput(e) { + this.setState({ + [e.target.name]: e.target.value, + }); + } + + render() { + if (this.state.display) { + return ZcparamsFetchModalRender.call(this); + } + + return null; + } +} + +const mapStateToProps = (state) => { + return { + displayZcparamsModal: state.Dashboard.displayZcparamsModal, + }; +}; + +export default connect(mapStateToProps)(ZcparamsFetchModal); diff --git a/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.render.js b/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.render.js new file mode 100644 index 000000000..e89562cf8 --- /dev/null +++ b/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.render.js @@ -0,0 +1,61 @@ +import React from 'react'; +import { translate } from '../../../translate/translate'; + +const ZcparamsFetchModalRender = function() { + return ( +
    +
    +
    +
    +
    + +

    ZCash Params Fetch

    +
    +
    +
    +
    +
    + Select resource to download Zcash params keys from +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + { this.renderUpdateStatus() } +
    +
    +
    +
    +
    +
    +
    +
    +
    + ); +}; + +export default ZcparamsFetchModalRender; \ No newline at end of file diff --git a/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.scss b/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.scss new file mode 100644 index 000000000..4d7f60bb2 --- /dev/null +++ b/react/src/components/dashboard/zcparamsFetchModal/zcparamsFetchModal.scss @@ -0,0 +1,19 @@ +.zcparams-fetch-modal { + .modal-body { + height: 400px; + + > div { + height: 100%; + } + .page-content { + width: 90%; + height: 100%; + } + .center { + .col-sm-6 { + margin: 0 auto; + float: none; + } + } + } +} \ No newline at end of file diff --git a/react/src/components/overrides.scss b/react/src/components/overrides.scss index 1a80716a9..bdccfc56f 100644 --- a/react/src/components/overrides.scss +++ b/react/src/components/overrides.scss @@ -398,7 +398,8 @@ select{ padding-left: 0; } -.alert-info { +.alert-info, +.alert-warning { width: 97.1%; margin: 0 auto; } @@ -462,4 +463,9 @@ select{ .link { cursor: hand; cursor: pointer; +} + +.dashboard-claim-interest-btn { + padding: 4px 14px 4px 7px; + margin-left: 20px; } \ No newline at end of file From 317840d299c4041cb7d1c49f1d7d62209fb40a78 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Sun, 15 Oct 2017 01:40:56 +0300 Subject: [PATCH 31/47] kmd main win maxconnections workaround --- .../dashboard/settings/settings.panelUtils.js | 8 +- .../dashboard/walletsData/walletsData.js | 2 + .../walletsProgress/walletsProgress.js | 121 ++++++++++++++++-- .../walletsProgress/walletsProgress.render.js | 32 ++++- react/src/components/overrides.scss | 4 + 5 files changed, 148 insertions(+), 19 deletions(-) diff --git a/react/src/components/dashboard/settings/settings.panelUtils.js b/react/src/components/dashboard/settings/settings.panelUtils.js index e07614fda..325bfb829 100644 --- a/react/src/components/dashboard/settings/settings.panelUtils.js +++ b/react/src/components/dashboard/settings/settings.panelUtils.js @@ -33,10 +33,14 @@ export function setupAccordion(info) { if (!singleChild) { info.kids.forEach((child, i) => { const { openByDefault } = child ? child.props : false; - if (singleOpen && activeSections.length === 0 && openByDefault) { + + if (singleOpen && + activeSections.length === 0 && + openByDefault) { activeSections.push(`panel-sec-${i}`); } - if (!singleOpen && openByDefault) { + if (!singleOpen && + openByDefault) { activeSections.push(`panel-sec-${i}`); } }); diff --git a/react/src/components/dashboard/walletsData/walletsData.js b/react/src/components/dashboard/walletsData/walletsData.js index 1bb942ee6..036daaf79 100644 --- a/react/src/components/dashboard/walletsData/walletsData.js +++ b/react/src/components/dashboard/walletsData/walletsData.js @@ -85,6 +85,8 @@ class WalletsData extends React.Component { displayClaimInterestUI() { if (this.props.ActiveCoin && + this.props.ActiveCoin.coin === 'KMD' && + this.props.ActiveCoin.mode === 'native' && this.props.ActiveCoin.balance && this.props.ActiveCoin.balance.interest && this.props.ActiveCoin.balance.interest > 0) { diff --git a/react/src/components/dashboard/walletsProgress/walletsProgress.js b/react/src/components/dashboard/walletsProgress/walletsProgress.js index ccbbe820c..dcee90fef 100644 --- a/react/src/components/dashboard/walletsProgress/walletsProgress.js +++ b/react/src/components/dashboard/walletsProgress/walletsProgress.js @@ -1,6 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; import { translate } from '../../../translate/translate'; +import { + triggerToaster, +} from '../../../actions/actionCreators'; +import Store from '../../../store'; import { SyncErrorBlocksRender, SyncPercentageRender, @@ -8,7 +12,7 @@ import { TranslationComponentsRender, ChainActivationNotificationRender, VerifyingBlocksRender, - WalletsProgressRender + WalletsProgressRender, } from './walletsProgress.render'; class WalletsProgress extends React.Component { @@ -16,8 +20,71 @@ class WalletsProgress extends React.Component { super(); this.state = { prevProgress: {}, + isWindows: false, + isWindowsWorkaroundEnabled: false, }; - this.isFullySynced = this.isFullySynced.bind(this); + this.isWinSyncPercBelowThreshold = this.isWinSyncPercBelowThreshold.bind(this); + this.applyWindowsSyncWorkaround = this.applyWindowsSyncWorkaround.bind(this); + } + + componentWillMount() { + const _mainWindow = window.require('electron').remote.getCurrentWindow(); + let _isWindows; + + try { + _isWindows = _mainWindow.isWindows; + } catch (e) {} + + if (_isWindows) { + _mainWindow.getMaxconKMDConf() + .then((res) => { + if (!res || + Number(res) !== 1) { + this.setState({ + isWindowsWorkaroundEnabled: false, + isWindows: _isWindows, + }); + } else { + this.setState({ + isWindowsWorkaroundEnabled: true, + isWindows: _isWindows, + }); + } + }); + } + } + + applyWindowsSyncWorkaround() { + const _mainWindow = window.require('electron').remote.getCurrentWindow(); + + _mainWindow.setMaxconKMDConf(1) + .then((res) => { + if (res) { + this.setState({ + isWindowsWorkaroundEnabled: true, + }); + + Store.dispatch( + triggerToaster( + 'Windows sync workaround is applied. Closing the app...', + translate('TOASTR.WALLET_NOTIFICATION'), + 'success' + ) + ); + + setTimeout(() => { + _mainWindow.appExit(); + }, 2000); + } else { + Store.dispatch( + triggerToaster( + 'Unable to apply Windows sync workaround', + translate('TOASTR.WALLET_NOTIFICATION'), + 'error' + ) + ); + } + }); } componentWillReceiveProps(props) { @@ -31,6 +98,7 @@ class WalletsProgress extends React.Component { Number(this.state.prevProgress.longestchain) > 0) { _progress.longestchain = this.state.prevProgress.longestchain; } + this.setState(Object.assign({}, this.state, { prevProgress: _progress, })); @@ -39,18 +107,49 @@ class WalletsProgress extends React.Component { prevProgress: props.ActiveCoin.progress, })); } - } - isFullySynced() { - const _progress = this.props.ActiveCoin.progress; + if (this.isWinSyncPercBelowThreshold() !== -777 && + this.state.isWindowsWorkaroundEnabled && + !this.isWinSyncPercBelowThreshold()) { + const _mainWindow = window.require('electron').remote.getCurrentWindow(); + + _mainWindow.setMaxconKMDConf() + .then((res) => { + if (res) { + this.setState({ + isWindowsWorkaroundEnabled: false, + }); + + Store.dispatch( + triggerToaster( + 'Current sync state reached 80% level. Windows sync workaround is disabled. Changes will be applied next time you start the app.', + translate('TOASTR.WALLET_NOTIFICATION'), + 'info', + false + ) + ); + } else { + Store.dispatch( + triggerToaster( + 'Unable to apply Windows sync workaround', + translate('TOASTR.WALLET_NOTIFICATION'), + 'error' + ) + ); + } + }); + } + } - if ((Number(_progress.balances) + - Number(_progress.validated) + - Number(_progress.bundles) + - Number(_progress.utxo)) / 4 === 100) { - return true; + isWinSyncPercBelowThreshold() { + if (this.state.prevProgress && + this.state.prevProgress.longestchain && + this.state.prevProgress.blocks) { + if (Number(this.state.prevProgress.blocks) * 100 / Number(this.state.prevProgress.longestchain) < 80) { + return true; + } } else { - return false; + return -777; } } diff --git a/react/src/components/dashboard/walletsProgress/walletsProgress.render.js b/react/src/components/dashboard/walletsProgress/walletsProgress.render.js index 0b46f5d2c..ae34f8e4c 100644 --- a/react/src/components/dashboard/walletsProgress/walletsProgress.render.js +++ b/react/src/components/dashboard/walletsProgress/walletsProgress.render.js @@ -98,12 +98,32 @@ export const TranslationComponentsRender = function(translationID) { export const ChainActivationNotificationRender = function() { if (this.props.ActiveCoin.coin !== 'CHIPS') { return ( -
    -

    - { translate('INDEX.ACTIVATING_CHAIN') }  - { this.props.ActiveCoin.rescanInProgress ? (this.renderRescanProgress() ? `: ${this.renderRescanProgress().toFixed(2)}% ${translate('INDEX.PROGRESS_RESCANNING_BLOCKS')}` : translate('INDEX.PROGRESS_RESCANNING_BLOCKS')) : this.renderActivatingBestChainProgress() } -

    -

    { this.renderLB('INDEX.KMD_STARTED') }

    +
    +
    +

    + { translate('INDEX.ACTIVATING_CHAIN') }  + { this.props.ActiveCoin.rescanInProgress ? (this.renderRescanProgress() ? `: ${this.renderRescanProgress().toFixed(2)}% ${translate('INDEX.PROGRESS_RESCANNING_BLOCKS')}` : translate('INDEX.PROGRESS_RESCANNING_BLOCKS')) : this.renderActivatingBestChainProgress() } +

    +

    { this.renderLB('INDEX.KMD_STARTED') }

    +
    + { this.state.isWindows && + !this.state.isWindowsWorkaroundEnabled && + this.isWinSyncPercBelowThreshold() && + this.props.ActiveCoin && + this.props.ActiveCoin.mode === 'native' && + this.props.ActiveCoin.coin === 'KMD' && +
    +

    Slow sync speed on Windows? Try this workaround.

    +

    It will add maxconnections=1 in komodo.conf file and force close the app.

    +

    Please run the app again after that.

    + +
    + }
    ); } else { diff --git a/react/src/components/overrides.scss b/react/src/components/overrides.scss index bdccfc56f..29f52e9fb 100644 --- a/react/src/components/overrides.scss +++ b/react/src/components/overrides.scss @@ -468,4 +468,8 @@ select{ .dashboard-claim-interest-btn { padding: 4px 14px 4px 7px; margin-left: 20px; +} + +.win-sync-workaround-btn { + padding: 4px 7px; } \ No newline at end of file From 6346ab5d80ab61c273859e9c14f09b31dbb447f9 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 16 Oct 2017 04:06:55 -0700 Subject: [PATCH 32/47] reindex blocks handling fix --- .../components/dashboard/walletsProgress/walletsProgress.js | 4 ++-- .../dashboard/walletsProgress/walletsProgress.render.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/react/src/components/dashboard/walletsProgress/walletsProgress.js b/react/src/components/dashboard/walletsProgress/walletsProgress.js index dcee90fef..3fe640cdb 100644 --- a/react/src/components/dashboard/walletsProgress/walletsProgress.js +++ b/react/src/components/dashboard/walletsProgress/walletsProgress.js @@ -229,7 +229,7 @@ class WalletsProgress extends React.Component { _progress.code && _progress.code === -28 && this.props.Settings.debugLog) { - if (_progress.message == 'Activating best chain...') { + if (_progress.message === 'Activating best chain...') { const _parseProgress = this.parseActivatingBestChainProgress(); if (_parseProgress && @@ -264,7 +264,7 @@ class WalletsProgress extends React.Component { renderRescanProgress() { if (this.props.Settings.debugLog.indexOf('Still rescanning') > -1 && - this.props.ActiveCoin.rescanInProgress) { + ((this.props.ActiveCoin.rescanInProgress) || (this.props.ActiveCoin.progress && this.props.ActiveCoin.progress.code && this.props.ActiveCoin.progress.code === -28 && this.props.ActiveCoin.progress.message === 'Rescanning...'))) { const temp = this.props.Settings.debugLog.split(' '); let currentProgress; diff --git a/react/src/components/dashboard/walletsProgress/walletsProgress.render.js b/react/src/components/dashboard/walletsProgress/walletsProgress.render.js index ae34f8e4c..900a2f52e 100644 --- a/react/src/components/dashboard/walletsProgress/walletsProgress.render.js +++ b/react/src/components/dashboard/walletsProgress/walletsProgress.render.js @@ -99,16 +99,16 @@ export const ChainActivationNotificationRender = function() { if (this.props.ActiveCoin.coin !== 'CHIPS') { return (
    -
    +

    { translate('INDEX.ACTIVATING_CHAIN') }  - { this.props.ActiveCoin.rescanInProgress ? (this.renderRescanProgress() ? `: ${this.renderRescanProgress().toFixed(2)}% ${translate('INDEX.PROGRESS_RESCANNING_BLOCKS')}` : translate('INDEX.PROGRESS_RESCANNING_BLOCKS')) : this.renderActivatingBestChainProgress() } + { this.props.ActiveCoin.rescanInProgress || (this.props.ActiveCoin.progress && this.props.ActiveCoin.progress.code && this.props.ActiveCoin.progress.code === -28 && this.props.ActiveCoin.progress.message === 'Rescanning...') ? (this.renderRescanProgress() ? `: ${this.renderRescanProgress().toFixed(2)}% ${translate('INDEX.PROGRESS_RESCANNING_BLOCKS')}` : translate('INDEX.PROGRESS_RESCANNING_BLOCKS')) : this.renderActivatingBestChainProgress() }

    { this.renderLB('INDEX.KMD_STARTED') }

    { this.state.isWindows && !this.state.isWindowsWorkaroundEnabled && - this.isWinSyncPercBelowThreshold() && + this.isWinSyncPercBelowThreshold() === true && this.props.ActiveCoin && this.props.ActiveCoin.mode === 'native' && this.props.ActiveCoin.coin === 'KMD' && From bb364196e3c85ac4d8d71ebd40a7ced2a662a8f1 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 16 Oct 2017 18:12:30 +0300 Subject: [PATCH 33/47] native send not fully synced warning --- react/src/actions/actions/nativeSyncInfo.js | 2 +- .../src/components/dashboard/sendCoin/sendCoin.js | 14 +++++++++++++- .../dashboard/sendCoin/sendCoin.render.js | 5 +++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/react/src/actions/actions/nativeSyncInfo.js b/react/src/actions/actions/nativeSyncInfo.js index d87e5a54e..cff4fb95d 100644 --- a/react/src/actions/actions/nativeSyncInfo.js +++ b/react/src/actions/actions/nativeSyncInfo.js @@ -15,7 +15,7 @@ export function nativeGetinfoFailureState() { } } -// TODO: use debug.log instead +// TODO: use blockchaininfo rpc export function getSyncInfoNativeKMD(skipDebug, json, skipRemote) { let _json = json; diff --git a/react/src/components/dashboard/sendCoin/sendCoin.js b/react/src/components/dashboard/sendCoin/sendCoin.js index 5cd331134..54199af28 100644 --- a/react/src/components/dashboard/sendCoin/sendCoin.js +++ b/react/src/components/dashboard/sendCoin/sendCoin.js @@ -22,7 +22,6 @@ import { isPositiveNumber } from '../../../util/number'; // TODO: - add links to explorers // - render z address trim -// spv, request utxo and check if any failed to verify, display alert notice class SendCoin extends React.Component { constructor(props) { @@ -53,6 +52,7 @@ class SendCoin extends React.Component { this.SendFormRender = _SendFormRender.bind(this); this.isTransparentTx = this.isTransparentTx.bind(this); this.toggleSubstractFee = this.toggleSubstractFee.bind(this); + this.isFullySynced = this.isFullySynced.bind(this); } SendFormRender() { @@ -554,6 +554,17 @@ class SendCoin extends React.Component { return false; } + isFullySynced() { + if (this.props.ActiveCoin.progress && + this.props.ActiveCoin.progress.longestchain && + this.props.ActiveCoin.progress.blocks && + this.props.ActiveCoin.progress.longestchain > 0 && + this.props.ActiveCoin.progress.blocks > 0 && + Number(this.state.prevProgress.blocks) * 100 / Number(this.state.prevProgress.longestchain) === 100) { + return true; + } + } + render() { if (this.props && this.props.ActiveCoin && @@ -575,6 +586,7 @@ const mapStateToProps = (state, props) => { balance: state.ActiveCoin.balance, activeSection: state.ActiveCoin.activeSection, lastSendToResponse: state.ActiveCoin.lastSendToResponse, + progress: state.ActiveCoin.progress, }, Dashboard: state.Dashboard, }; diff --git a/react/src/components/dashboard/sendCoin/sendCoin.render.js b/react/src/components/dashboard/sendCoin/sendCoin.render.js index 50f21207c..6c89d8632 100644 --- a/react/src/components/dashboard/sendCoin/sendCoin.render.js +++ b/react/src/components/dashboard/sendCoin/sendCoin.render.js @@ -121,6 +121,11 @@ export const _SendFormRender = function() { { this.props.ActiveCoin.coin }
    + { !this.isFullySynced() && this.props.ActiveCoin && this.props.ActiveCoin.mode === 'native' && +
    + Your wallet is not fully synced! Please wait until it reached 100% to avoid possible transaction send implications. +
    + }
    } diff --git a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js index 328208536..6aa60a82c 100644 --- a/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js +++ b/react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js @@ -140,15 +140,13 @@ const WalletsTxInfoRender = function(txInfo) { { this.capitalizeFirstLetter('vjoinsplit') } - { txInfo.vjoinsplit } // native - { txInfo.time ? secondsToString(txInfo.time) : '' } // electrum + { txInfo.vjoinsplit } { this.capitalizeFirstLetter('details') } - { txInfo.details } // native - { txInfo.timereceived ? secondsToString(txInfo.timereceived) : '' } // electrum + { txInfo.details } @@ -158,8 +156,8 @@ const WalletsTxInfoRender = function(txInfo) { { this.state.activeTab === 2 &&
    @@ -178,13 +176,13 @@ const WalletsTxInfoRender = function(txInfo) {
    } { !this.state.txDetails && -
    Loading...
    +
    { translate('INDEX.LOADING') }...
    }
    - { this.state.txDetails && + { this.state.txDetails && this.props.ActiveCoin.coin !== 'CHIPS' && -

    ZCash Params Fetch

    +

    { translate('ZCPARAMS_FETCH.ZCPARAMS_FETCH') }

    -
    - Select resource to download Zcash params keys from -
    +
    { translate('ZCPARAMS_FETCH.SELECT_ZCPARAMS_SOURCE') }
    @@ -40,7 +38,7 @@ const ZcparamsFetchModalRender = function() { + onClick={ this._downloadZCashParamsPromise }>{ translate('ZCPARAMS_FETCH.DOWNLOAD') }
    diff --git a/react/src/components/login/login.render.js b/react/src/components/login/login.render.js index 23a3805d2..33eb6d08e 100644 --- a/react/src/components/login/login.render.js +++ b/react/src/components/login/login.render.js @@ -15,7 +15,7 @@ const LoginRender = function () { src="assets/images/agama-login-logo.svg" width="200" height="160" - alt="SuperNET Iguana" /> + alt="SuperNET Agama" />
    @@ -48,7 +48,7 @@ const LoginRender = function () { { translate('INDEX.WELCOME_LOGIN') } { this.props.Login.pinList.length > 0 && - You can login be entering a login seed or by selecting a pin + { translate('LOGIN.PIN_LOGIN_INFO') } }
    0 && -
    -
    -
    -
    -
    -
    OR
    -
    -
    +
    +
    +
    +
    +
    +
    + { translate('INDEX.OR') }
    +
    +
    +
    -
    } { this.props.Login.pinList.length > 0 && -
    -
    - -
    -
    - +
    +
    + +
    +
    + +
    -
    } + { this.displayClaimInterestUI() === 777 && +
    + { translate('DASHBOARD.CLAIM_INTEREST_HELPER_BAR_P1') } { this.props.ActiveCoin.balance.interest } KMD { translate('DASHBOARD.CLAIM_INTEREST_HELPER_BAR_P2') }. + +
    + } + { this.displayClaimInterestUI() === -777 && +
    + { translate('DASHBOARD.CLAIM_INTEREST_HELPER_BAR_ALT_P1') }. + +
    + }
    diff --git a/react/src/translate/en.js b/react/src/translate/en.js index 3c7210bd4..e7e99fc05 100644 --- a/react/src/translate/en.js +++ b/react/src/translate/en.js @@ -455,8 +455,10 @@ export const _lang = { 'FAILED_TX_INFO': 'Transaction failed or block reindexing is in progress. Failed transactions usually rejected after a while resulting in your funds being credited back.', 'SPV_CLAIMED_INTEREST': 'Claimed interest', 'CLAIM_INTEREST_HELPER_BAR_P1': 'You have', - 'CLAIM_INTEREST_HELPER_BAR_P2': 'to claim.', + 'CLAIM_INTEREST_HELPER_BAR_P2': 'to claim', 'CLAIM_INTEREST_HELPER_BAR_P3': 'Claim now', + 'CLAIM_INTEREST_HELPER_BAR_ALT_P1': 'You don\'t have any interest to claim', + 'CLAIM_INTEREST_HELPER_BAR_ALT_P2': 'Check UTXO', 'SEARCH': 'Search', 'WIN_SYNC_WORKAROUND_APPLIED': 'Windows sync workaround is applied. Closing the app...', 'WIN_SYNC_WORKAROUND_APPLY_FAILED': 'Unable to apply Windows sync workaround!', From 82aed04e5f7aa840c71e45295a8147079801581d Mon Sep 17 00:00:00 2001 From: pbca26 Date: Thu, 19 Oct 2017 23:28:10 +0300 Subject: [PATCH 42/47] possible win sync workaround fix --- .../components/dashboard/walletsProgress/walletsProgress.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/react/src/components/dashboard/walletsProgress/walletsProgress.js b/react/src/components/dashboard/walletsProgress/walletsProgress.js index d2fcf84b7..45c486ece 100644 --- a/react/src/components/dashboard/walletsProgress/walletsProgress.js +++ b/react/src/components/dashboard/walletsProgress/walletsProgress.js @@ -144,7 +144,9 @@ class WalletsProgress extends React.Component { isWinSyncPercBelowThreshold() { if (this.state.prevProgress && this.state.prevProgress.longestchain && - this.state.prevProgress.blocks) { + this.state.prevProgress.blocks && + this.state.prevProgress.longestchain > 0 && + this.state.prevProgress.blocks > 0) { if (Number(this.state.prevProgress.blocks) * 100 / Number(this.state.prevProgress.longestchain) < 80) { return true; } From f93ca218ca60cad2956c076e2cdc103d89cf2350 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Fri, 20 Oct 2017 14:13:23 +0300 Subject: [PATCH 43/47] AGT-115, tab resize interval --- .../settings/settings.appSettingsPanel.js | 27 ++++++++++++++----- .../settings/settings.debugLogPanel.js | 23 +++++++++++----- .../components/dashboard/settings/settings.js | 2 +- .../dashboard/settings/settings.panel.js | 1 - .../dashboard/settings/settings.panelBody.js | 23 ++++++++++++---- 5 files changed, 57 insertions(+), 19 deletions(-) diff --git a/react/src/components/dashboard/settings/settings.appSettingsPanel.js b/react/src/components/dashboard/settings/settings.appSettingsPanel.js index 09b89efc8..791dee63f 100644 --- a/react/src/components/dashboard/settings/settings.appSettingsPanel.js +++ b/react/src/components/dashboard/settings/settings.appSettingsPanel.js @@ -60,7 +60,8 @@ class AppSettingsPanel extends React.Component { if (key.indexOf('__') === -1) { _appSettingsPristine[key] = this.state.appConfigSchema[key] && this.state.appConfigSchema[key].type === 'number' ? Number(_appSettings[key]) : _appSettings[key]; - if (this.state.appConfigSchema[key] && this.state.appConfigSchema[key].type === 'folder' && + if (this.state.appConfigSchema[key] && + this.state.appConfigSchema[key].type === 'folder' && _appSettings[key] && _appSettings[key].length) { const _testLocation = window.require('electron').remote.getCurrentWindow().testLocation; @@ -72,18 +73,20 @@ class AppSettingsPanel extends React.Component { isError = true; Store.dispatch( triggerToaster( - translate('TOASTR.KOMODO_DATADIR_INVALID'), + this.renderLB('TOASTR.KOMODO_DATADIR_INVALID'), translate('INDEX.SETTINGS'), - 'error' + 'error', + false ) ); } else if (res === false) { isError = true; Store.dispatch( triggerToaster( - translate('TOASTR.KOMODO_DATADIR_NOT_DIR'), + this.renderLB('TOASTR.KOMODO_DATADIR_NOT_DIR'), translate('INDEX.SETTINGS'), - 'error' + 'error', + false ) ); } else { @@ -102,6 +105,17 @@ class AppSettingsPanel extends React.Component { } } + renderLB(_translationID) { + const _translationComponents = translate(_translationID).split('
    '); + + return _translationComponents.map((_translation) => + + {_translation} +
    +
    + ); + } + renderConfigEditForm() { let items = []; const _appConfig = this.state.appSettings; @@ -257,7 +271,8 @@ class AppSettingsPanel extends React.Component { let _appSettings = this.state.appSettings; let _appSettingsPrev = Object.assign({}, _appSettings); - if (!childKey && this.state.appConfigSchema[parentKey].type === 'boolean') { + if (!childKey && + this.state.appConfigSchema[parentKey].type === 'boolean') { _appSettings[parentKey] = typeof _appSettings[parentKey] !== undefined ? !_appSettings[parentKey] : !this.state.appSettings[parentKey]; } else if (childKey && this.state.appConfigSchema[parentKey][childKey].type === 'boolean') { _appSettings[parentKey][childKey] = typeof _appSettings[parentKey][childKey] !== undefined ? !_appSettings[parentKey][childKey] : !this.state.appSettings[parentKey][childKey]; diff --git a/react/src/components/dashboard/settings/settings.debugLogPanel.js b/react/src/components/dashboard/settings/settings.debugLogPanel.js index 0cac3c845..08405e52d 100644 --- a/react/src/components/dashboard/settings/settings.debugLogPanel.js +++ b/react/src/components/dashboard/settings/settings.debugLogPanel.js @@ -17,6 +17,7 @@ class DebugLogPanel extends React.Component { debugTarget: 'iguana', nativeOnly: Config.iguanaLessMode, toggleAppRuntimeLog: false, + pristine: true, }; this.readDebugLog = this.readDebugLog.bind(this); this.updateInput = this.updateInput.bind(this); @@ -26,17 +27,21 @@ class DebugLogPanel extends React.Component { } readDebugLog() { + this.setState(Object.assign({}, this.state, { + pristine: false, + })); + Store.dispatch( getDebugLog( - this.state.debugTarget, + 'komodo', this.state.debugLinesCount ) ); } renderAppRuntimeLog() { - let _items = []; const _appRuntimeLog = this.state.appRuntimeLog; + let _items = []; for (let i = 0; i < _appRuntimeLog.length; i++) { _items.push( @@ -74,7 +79,8 @@ class DebugLogPanel extends React.Component { } renderDebugLogData() { - if (this.props.Settings.debugLog) { + if (this.props.Settings.debugLog && + !this.state.pristine) { const _debugLogDataRows = this.props.Settings.debugLog.split('\n'); if (_debugLogDataRows && @@ -106,7 +112,9 @@ class DebugLogPanel extends React.Component { return (
    - { 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.native && + Object.keys(this.props.Main.coins.native).length > 0 &&

    { translate('INDEX.DEBUG_LOG_DESC') }

    }
    @@ -123,10 +131,13 @@ class DebugLogPanel extends React.Component { Show app runtime log + onClick={ this.toggleAppRuntimeLog }>{ translate('SETTINGS.SHOW_APP_RUNTIME_LOG') }
    - { !this.state.toggleAppRuntimeLog && this.props.Main.coins && this.props.Main.coins.native && Object.keys(this.props.Main.coins.native).length > 0 && + { !this.state.toggleAppRuntimeLog && + this.props.Main.coins && + this.props.Main.coins.native && + Object.keys(this.props.Main.coins.native).length > 0 &&
    { + if (this.props.active) { + this.setState({ + sectionHeight: `${this.accordionContent.scrollHeight}px`, + }); + } + }, 500); + } + + componentWillUnmount() { + clearInterval(this.accordionResizeInterval); } componentWillReceiveProps(props) { @@ -78,10 +91,10 @@ class PanelSection extends React.Component { }); return( -
    this.toggleSection()}> -
    +
    +
    this.toggleSection()} + className="panel-heading"> {title} From e45a78c50afa1860fdadc69a91605af15cf663f9 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Fri, 20 Oct 2017 14:13:56 +0300 Subject: [PATCH 44/47] AGT-114 --- .../src/components/dashboard/walletsProgress/walletsProgress.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react/src/components/dashboard/walletsProgress/walletsProgress.js b/react/src/components/dashboard/walletsProgress/walletsProgress.js index 45c486ece..3f1caf940 100644 --- a/react/src/components/dashboard/walletsProgress/walletsProgress.js +++ b/react/src/components/dashboard/walletsProgress/walletsProgress.js @@ -342,7 +342,7 @@ class WalletsProgress extends React.Component { if (_blocks && _blocks[0]) { return ( - `: ${_blocks[0]} ({ translate('DASHBOARD.CURRENT_BLOCK_SM') })` + `: ${_blocks[0]} (${ translate('DASHBOARD.CURRENT_BLOCK_SM') })` ); } else { return null; From 2716f20565b1be0461387aa084f56df8074933ce Mon Sep 17 00:00:00 2001 From: pbca26 Date: Fri, 20 Oct 2017 14:17:26 +0300 Subject: [PATCH 45/47] custom seed better requirements desc --- react/src/components/login/login.js | 4 +- react/src/components/login/login.render.js | 82 +++++++++++----------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/react/src/components/login/login.js b/react/src/components/login/login.js index 603c6c4a1..286c68898 100644 --- a/react/src/components/login/login.js +++ b/react/src/components/login/login.js @@ -402,7 +402,9 @@ class Login extends React.Component { isSeedBlank: isSeedBlank ? true : false, }); - if (enteredSeedsMatch && !isSeedBlank && _customSeed !== null) { + if (enteredSeedsMatch && + !isSeedBlank && + _customSeed !== null) { this.toggleSeedBackupModal(); } } diff --git a/react/src/components/login/login.render.js b/react/src/components/login/login.render.js index 33eb6d08e..bb8b64acb 100644 --- a/react/src/components/login/login.render.js +++ b/react/src/components/login/login.render.js @@ -80,50 +80,51 @@ const LoginRender = function () {
    } - { this.state.loginPassphrase && this.state.enableEncryptSeed && -
    -
    - -