diff --git a/react/src/actions/actions/addCoin.js b/react/src/actions/actions/addCoin.js index ab2e2181d..642631db7 100644 --- a/react/src/actions/actions/addCoin.js +++ b/react/src/actions/actions/addCoin.js @@ -243,7 +243,7 @@ export function shepherdHerd(coin, mode, path, startupParams) { console.warn(acData); dispatch( triggerToaster( - `Error starting ${coin} daemon. Port ${acData.rpc} is already taken!`, // translate + translate('TOASTR.ERROR_STARTING_DAEMON', coin) + ' ' + translate('TOASTR.PORT_IS_TAKEN', acData.rpc), translate('TOASTR.SERVICE_NOTIFICATION'), 'error', false diff --git a/react/src/actions/actions/coinList.js b/react/src/actions/actions/coinList.js index c078fb0ba..7a69e197e 100644 --- a/react/src/actions/actions/coinList.js +++ b/react/src/actions/actions/coinList.js @@ -1,6 +1,54 @@ import { triggerToaster } from '../actionCreators'; import Config from '../../config'; +export function shepherdElectrumLock() { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/lock`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: '', + }) + .catch((error) => { + console.log(error); + dispatch( + triggerToaster( + 'shepherdElectrumLock', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => resolve(json)) + }); +} + +export function shepherdElectrumLogout() { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/logout`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: '', + }) + .catch((error) => { + console.log(error); + dispatch( + triggerToaster( + 'shepherdElectrumLogout', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => resolve(json)) + }); +} + export function shepherdStopCoind(coin) { return new Promise((resolve, reject) => { fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/coind/stop`, { @@ -25,14 +73,19 @@ export function shepherdStopCoind(coin) { }); } -export function shepherdRemoveCoin(coin) { +export function shepherdRemoveCoin(coin, mode) { return new Promise((resolve, reject) => { fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/coins/remove`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: coin === 'KMD' ? '' : JSON.stringify({ chain: coin }), + body: JSON.stringify(coin === 'KMD' && mode === 'native' ? { + mode, + } : { + mode, + chain: coin, + }), }) .catch((error) => { console.log(error); diff --git a/react/src/components/addcoin/addcoin.js b/react/src/components/addcoin/addcoin.js index 56e9f57d3..43237f64c 100644 --- a/react/src/components/addcoin/addcoin.js +++ b/react/src/components/addcoin/addcoin.js @@ -128,7 +128,7 @@ class AddCoin extends React.Component { addCoinProps.display !== this.state.display) { this.setState(Object.assign({}, this.state, { display: addCoinProps.display, - modalClassName: addCoinProps.display ? 'show fade' : 'show fade', + modalClassName: 'show fade', })); setTimeout(() => { diff --git a/react/src/components/dashboard/claimInterestModal/claimInterestModal.js b/react/src/components/dashboard/claimInterestModal/claimInterestModal.js index 13cff7858..8fe21d3ea 100755 --- a/react/src/components/dashboard/claimInterestModal/claimInterestModal.js +++ b/react/src/components/dashboard/claimInterestModal/claimInterestModal.js @@ -35,7 +35,9 @@ class ClaimInterestModal extends React.Component { } componentWillMount() { - this.loadListUnspent(); + if (this.props.ActiveCoin.mode === 'native') { + this.loadListUnspent(); + } } loadListUnspent() { diff --git a/react/src/components/dashboard/claimInterestModal/claimInterestModal.render.js b/react/src/components/dashboard/claimInterestModal/claimInterestModal.render.js index 1d02e9731..a423385b9 100644 --- a/react/src/components/dashboard/claimInterestModal/claimInterestModal.render.js +++ b/react/src/components/dashboard/claimInterestModal/claimInterestModal.render.js @@ -15,7 +15,7 @@ export const _ClaimInterestTableRender = function() { className="btn btn-default btn-xs clipboard-edexaddr copy-string-btn" title={ translate('INDEX.COPY_TO_CLIPBOARD') } onClick={ () => this.copyTxId(_transactionsList[i].txid) }> - { translate('INDEX.COPY') } + { translate('INDEX.COPY') + ' TXID' } { _transactionsList[i].address } diff --git a/react/src/components/dashboard/coinTile/coinTileItem.js b/react/src/components/dashboard/coinTile/coinTileItem.js index e3d3e4493..cbd3c6419 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.js @@ -30,7 +30,7 @@ import Config from '../../../config'; import CoinTileItemRender from './coinTileItem.render'; -const SPV_DASHBOARD_UPDATE_TIMEOUT = 60000; +const SPV_DASHBOARD_UPDATE_TIMEOUT = 10000; const ACTIVE_HANDLE_TIMEOUT_COIND_NATIVE = 15000; const COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD = window.require('electron').remote.getCurrentWindow().appConfig.failedRPCAttemptsThreshold || 10; @@ -43,6 +43,28 @@ class CoinTileItem extends React.Component { this.autoSetActiveCoin = this.autoSetActiveCoin.bind(this); } + renderStopCoinButton() { + if (this.props.Main && + this.props.Main.coins && + this.props.Main.coins.native && + this.props.Main.coins.native.length) { + return true; + } + } + + renderRemoveCoinButton() { + if (this.props.Main && + this.props.Main.coins && + ((this.props.Main.coins.native && + this.props.Main.coins.native.length && + this.state.appConfig && + !this.state.appConfig.stopNativeDaemonsOnQuit) || + (this.props.Main.coins.spv && + this.props.Main.coins.spv.length))) { + return true; + } + } + autoSetActiveCoin() { const modes = [ 'native', @@ -97,8 +119,8 @@ class CoinTileItem extends React.Component { }); } - removeCoin(coin) { - shepherdRemoveCoin(coin) + removeCoin(coin, mode) { + shepherdRemoveCoin(coin, mode) .then((res) => { Store.dispatch( triggerToaster( @@ -189,9 +211,7 @@ class CoinTileItem extends React.Component { ) ); } - } - - if (mode === 'spv') { + } else if (mode === 'spv') { Store.dispatch(shepherdElectrumBalance(coin, this.props.Dashboard.electrumCoins[coin].pub)); Store.dispatch(shepherdElectrumTransactions(coin, this.props.Dashboard.electrumCoins[coin].pub)); } diff --git a/react/src/components/dashboard/coinTile/coinTileItem.render.js b/react/src/components/dashboard/coinTile/coinTileItem.render.js index f7b111708..08578f0e6 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.render.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.render.js @@ -24,29 +24,17 @@ const CoinTileItemRender = function() { - { item.mode === 'native' && - this.props.Main && - this.props.Main.coins && - this.props.Main.coins.native && - this.props.Main.coins.native.length && - this.props.Main.coins.native.length > 1 && + { this.renderStopCoinButton() && this.stopCoind(item.coin) } + onClick={ () => this.stopCoind(item.coin, item.mode) } title="Stop" className="icon fa-stop-circle coind-stop-icon"> } - { item.mode === 'native' && - this.props.Main && - this.props.Main.coins && - this.props.Main.coins.native && - this.props.Main.coins.native.length && - this.props.Main.coins.native.length > 1 && - this.state.appConfig && - !this.state.appConfig.stopNativeDaemonsOnQuit && + { this.renderRemoveCoinButton() && this.removeCoin(item.coin) } + onClick={ () => this.removeCoin(item.coin, item.mode) } title="Remove" - className="icon fa-plus-circle coind-remove-icon"> + className={ 'icon fa-plus-circle ' + (item.mode === 'spv' ? 'coind-remove-icon coind-remove-icon-spv' : 'coind-remove-icon') }> } { this.props.Dashboard && this.props.Dashboard.electrumCoins && diff --git a/react/src/components/dashboard/importKeyModal/importKeyModal.js b/react/src/components/dashboard/importKeyModal/importKeyModal.js index 5e130ce18..9e92e0519 100755 --- a/react/src/components/dashboard/importKeyModal/importKeyModal.js +++ b/react/src/components/dashboard/importKeyModal/importKeyModal.js @@ -77,11 +77,11 @@ class ImportKeyModal extends React.Component { this.setState({ trimPassphraseTimer: _trimPassphraseTimer, - [e.target.name]: newValue, + [e.target.name === 'wifkeysPassphraseTextarea' ? 'wifkeysPassphrase' : e.target.name]: newValue, }); } else { this.setState({ - [e.target.name]: e.target.value, + [e.target.name === 'wifkeysPassphraseTextarea' ? 'wifkeysPassphrase' : e.target.name]: e.target.value, }); } } @@ -174,7 +174,7 @@ class ImportKeyModal extends React.Component { rescan ? translate('INDEX.WALLET_RESCAN_FINISHED') : translate('INDEX.ADDRESS_IMPORTED'), translate('TOASTR.WALLET_NOTIFICATION'), 'success', - false + rescan ? false : true, ) ); } else { @@ -187,6 +187,19 @@ class ImportKeyModal extends React.Component { ); } }); + + this.state({ + passphraseWif: null, + passphraseAddress: null, + wifkeysPassphrase: null, + wifkeysPassphraseTextarea: null, + importWithRescan: this.state.importWithRescan ? false : this.state.importWithRescan, + }); + + // reset input vals + this.refs.wif + this.refs.wifkeysPassphrase.value = ''; + this.refs.wifkeysPassphraseTextarea.value = ''; } generateKeysFromPassphrase() { diff --git a/react/src/components/dashboard/importKeyModal/importKeyModal.render.js b/react/src/components/dashboard/importKeyModal/importKeyModal.render.js index 053d9ce56..a13065781 100644 --- a/react/src/components/dashboard/importKeyModal/importKeyModal.render.js +++ b/react/src/components/dashboard/importKeyModal/importKeyModal.render.js @@ -26,23 +26,25 @@ export const ImportKeyModalRender = function() { { translate('IMPORT_KEY.NOTICE') }: { translate('IMPORT_KEY.NOTICE_DESC') }.  { translate('IMPORT_KEY.KMD_RESCAN_WARNING_TIME') }.

-
{ translate('IMPORT_KEY.SHOW_ADDRESS_AND_WIF') }
- + { this.state.passphraseAddress && this.state.passphraseWif &&
@@ -118,6 +120,7 @@ export const ImportKeyModalRender = function() { type="text" className="form-control" name="wif" + ref="wif" onChange={ this.updateInput } value={ this.state.wif } />
diff --git a/react/src/components/dashboard/navbar/navbar.js b/react/src/components/dashboard/navbar/navbar.js index a5e9042ea..ec62a38bb 100755 --- a/react/src/components/dashboard/navbar/navbar.js +++ b/react/src/components/dashboard/navbar/navbar.js @@ -6,6 +6,10 @@ import { stopInterval, startInterval, displayImportKeyModal, + shepherdElectrumLock, + shepherdElectrumLogout, + getDexCoins, + activeHandle, } from '../../../actions/actionCreators'; import Store from '../../../store'; import Config from '../../../config'; @@ -23,6 +27,24 @@ class Navbar extends React.Component { this.openDropMenu = this.openDropMenu.bind(this); this.handleClickOutside = this.handleClickOutside.bind(this); this._checkAC = this._checkAC.bind(this); + this.spvLock = this.spvLock.bind(this); + this.spvLogout = this.spvLogout.bind(this); + } + + spvLock() { + shepherdElectrumLock() + .then((res) => { + Store.dispatch(getDexCoins()); + Store.dispatch(activeHandle()); + }); + } + + spvLogout() { + shepherdElectrumLogout() + .then((res) => { + Store.dispatch(getDexCoins()); + Store.dispatch(activeHandle()); + }); } componentWillMount() { @@ -106,6 +128,9 @@ const mapStateToProps = (state) => { Interval: { interval: state.Interval.interval, }, + Main: { + isLoggedIn: state.Main.isLoggedIn, + }, }; }; diff --git a/react/src/components/dashboard/navbar/navbar.render.js b/react/src/components/dashboard/navbar/navbar.render.js index fca2a46fa..14a7f7054 100644 --- a/react/src/components/dashboard/navbar/navbar.render.js +++ b/react/src/components/dashboard/navbar/navbar.render.js @@ -109,6 +109,26 @@ const NavbarRender = function() { { translate('ABOUT.ABOUT_AGAMA') } + { this.props.Main && + this.props.Main.isLoggedIn && + this.props.Main.spv && + this.props.Main.spv.length && +
  • + + Lock + +
  • + } + { this.props.Main && + this.props.Main.isLoggedIn && + this.props.Main.spv && + this.props.Main.spv.length && +
  • + + Logout + +
  • + } diff --git a/react/src/components/dashboard/settings/settings.exportKeysPanel.js b/react/src/components/dashboard/settings/settings.exportKeysPanel.js index e3c50be84..6fcada6a4 100644 --- a/react/src/components/dashboard/settings/settings.exportKeysPanel.js +++ b/react/src/components/dashboard/settings/settings.exportKeysPanel.js @@ -28,7 +28,12 @@ class ExportKeysPanel extends React.Component { props.Dashboard.activeSection !== 'settings') { this.setState(Object.assign({}, this.state, { keys: null, + wifkeysPassphrase: '', })); + + // reset input vals + this.refs.wifkeysPassphrase.value = ''; + this.refs.wifkeysPassphraseTextarea.value = ''; } } @@ -46,8 +51,12 @@ class ExportKeysPanel extends React.Component { } else { this.setState(Object.assign({}, this.state, { keys: keys.result, + wifkeysPassphrase: '', })); - console.warn(keys); + + // reset input vals + this.refs.wifkeysPassphrase.value = ''; + this.refs.wifkeysPassphraseTextarea.value = ''; } }) } @@ -116,11 +125,11 @@ class ExportKeysPanel extends React.Component { this.setState({ trimPassphraseTimer: _trimPassphraseTimer, - [e.target.name]: newValue, + [e.target.name === 'wifkeysPassphraseTextarea' ? 'wifkeysPassphrase' : e.target.name]: newValue, }); } else { this.setState({ - [e.target.name]: e.target.value, + [e.target.name === 'wifkeysPassphraseTextarea' ? 'wifkeysPassphrase' : e.target.name]: e.target.value, }); } } @@ -162,23 +171,25 @@ class ExportKeysPanel extends React.Component {
    -
    - +
    { this.state.keys &&
    - - - - - - { this.renderWifKeys() } +
    - { translate('SETTINGS.ADDRESS_LIST') } - - { translate('SETTINGS.WIF_KEY_LIST') } -
    + + + + + + { this.renderWifKeys() } +
    + { translate('SETTINGS.ADDRESS_LIST') } + + { translate('SETTINGS.WIF_KEY_LIST') } +
    diff --git a/react/src/components/dashboard/walletsMain/walletsMain.render.js b/react/src/components/dashboard/walletsMain/walletsMain.render.js index b14303a32..3e4a1a526 100644 --- a/react/src/components/dashboard/walletsMain/walletsMain.render.js +++ b/react/src/components/dashboard/walletsMain/walletsMain.render.js @@ -5,6 +5,7 @@ import SendCoin from '../sendCoin/sendCoin'; import WalletsProgress from '../walletsProgress/walletsProgress'; import WalletsData from '../walletsData/walletsData'; import ReceiveCoin from '../receiveCoin/receiveCoin'; +import { getCoinTitle } from '../../../util/coinHelper'; const WalletsMainRender = function() { return ( @@ -22,7 +23,7 @@ const WalletsMainRender = function() { - { this.props.ActiveCoin.coin } + { getCoinTitle(this.props.ActiveCoin.coin).name } diff --git a/react/src/components/login/login.js b/react/src/components/login/login.js index 55fc37ab5..8b655f8d9 100644 --- a/react/src/components/login/login.js +++ b/react/src/components/login/login.js @@ -8,7 +8,9 @@ import { startInterval, getDexCoins, triggerToaster, - toggleLoginSettingsModal + toggleLoginSettingsModal, + stopInterval, + dashboardChangeActiveCoin, } from '../../actions/actionCreators'; import Config from '../../config'; import Store from '../../store'; @@ -197,16 +199,19 @@ class Login extends React.Component { if (props.Login.pinList === 'no pins') { props.Login.pinList = []; } + if (props && props.Main && props.Main.isLoggedIn) { if (props.Main.total === 0) { this.setState({ activeLoginSection: 'activateCoin', + loginPassphrase: '', display: true, }); } else { this.setState({ + loginPassphrase: '', display: false, }); } @@ -215,21 +220,51 @@ class Login extends React.Component { if (props && props.Main && !props.Main.isLoggedIn) { + document.body.className = 'page-login layout-full page-dark'; + + if (props.Interval && + props.Interval.interval && + props.Interval.interval.sync) { + Store.dispatch(dashboardChangeActiveCoin()); + Store.dispatch( + stopInterval( + 'sync', + props.Interval.interval + ) + ); + } + this.setState({ display: true, activeLoginSection: this.state.activeLoginSection !== 'signup' ? 'login' : 'signup', }); + } + if (props.Main && + props.Main.total === 0) { document.body.className = 'page-login layout-full page-dark'; + + if (props.Interval && + props.Interval.interval && + props.Interval.interval.sync) { + Store.dispatch(dashboardChangeActiveCoin()); + Store.dispatch( + stopInterval( + 'sync', + props.Interval.interval + ) + ); + } } if (this.state.activeLoginSection !== 'signup' && props && props.Main && props.Main.isLoggedIn) { - this.setState({ - activeLoginSection: 'activateCoin', - }); + this.setState({ + loginPassphrase: '', + activeLoginSection: 'activateCoin', + }); } } @@ -264,7 +299,7 @@ class Login extends React.Component { this.setState({ trimPassphraseTimer: _trimPassphraseTimer, - [e.target.name]: newValue, + [e.target.name === 'loginPassphraseTextarea' ? 'loginPassphrase' : e.target.name]: newValue, loginPassPhraseSeedType: this.getLoginPassPhraseSeedType(newValue), }); } @@ -288,10 +323,14 @@ class Login extends React.Component { loginSeed() { // reset the login pass phrase values so that when the user logs out, the values are clear this.setState({ - loginPassphrase: null, + loginPassphrase: '', loginPassPhraseSeedType: null, }); + // reset login input vals + this.refs.loginPassphrase.value = ''; + this.refs.loginPassphraseTextarea.value = ''; + if (this.state.shouldEncryptSeed) { Store.dispatch(encryptPassphrase(this.state.loginPassphrase, this.state.encryptKey, this.state.pubKey)); } @@ -362,13 +401,6 @@ class Login extends React.Component { } execWalletCreate() { - /*Store.dispatch( - createNewWallet( - this.state.randomSeedConfirm, - this.props.Dashboard.activeHandle - ) - );*/ - Store.dispatch( shepherdElectrumAuth(this.state.randomSeedConfirm) ); diff --git a/react/src/components/login/login.render.js b/react/src/components/login/login.render.js index c3f97f250..3442a9e80 100644 --- a/react/src/components/login/login.render.js +++ b/react/src/components/login/login.render.js @@ -55,13 +55,17 @@ const LoginRender = function () { type="password" className={ !this.state.seedInputVisibility ? 'form-control' : 'hide' } name="loginPassphrase" + ref="loginPassphrase" onChange={ this.updateLoginPassPhraseInput } onKeyDown={ (event) => this.handleKeydown(event) } + autoComplete="off" value={ this.state.loginPassphrase || '' } /> diff --git a/react/src/components/overrides.scss b/react/src/components/overrides.scss index 8253a30cf..99a3facb8 100644 --- a/react/src/components/overrides.scss +++ b/react/src/components/overrides.scss @@ -375,6 +375,7 @@ select{ .header-easydex-section { img { width: 60px; + max-height: 60px; } } } @@ -511,4 +512,14 @@ select{ .coind-remove-icon { transform: rotate(45deg); top: 45px; +} + +.coind-remove-icon-spv { + top: 19px; +} + +.no-borders { + tbody > tr > td { + border: none; + } } \ No newline at end of file