diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index b314745f5..f6250dc16 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -85,6 +85,30 @@ class PreferencesController { this.store.updateState({ identities }) } + /** + * Removes an address from state + * + * @param {string} address A hex address + * @returns {string} the address that was removed + */ + removeAddress (address) { + const identities = this.store.getState().identities + if (!identities[address]) { + throw new Error(`${address} can't be deleted cause it was not found`) + } + delete identities[address] + this.store.updateState({ identities }) + + // If the selected account is no longer valid, + // select an arbitrary other account: + if (address === this.getSelectedAddress()) { + const selected = Object.keys(identities)[0] + this.setSelectedAddress(selected) + } + return address + } + + /** * Adds addresses to the identities object without removing identities * diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index bec02c3ed..e8f0eba90 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -354,6 +354,7 @@ module.exports = class MetamaskController extends EventEmitter { verifySeedPhrase: nodeify(this.verifySeedPhrase, this), clearSeedWordCache: this.clearSeedWordCache.bind(this), resetAccount: nodeify(this.resetAccount, this), + removeAccount: nodeify(this.removeAccount, this), importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this), // trezor @@ -587,7 +588,8 @@ module.exports = class MetamaskController extends EventEmitter { } }) - this.preferencesController.setAccountLabel(trezorAccount, `TREZOR #${index + 1}`) + this.preferencesController.setAccountLabel(trezorAccount, `TREZOR #${parseInt(index, 10) + 1}`) + this.preferencesController.setSelectedAddress(trezorAccount) const { identities } = this.preferencesController.store.getState() return { ...keyState, identities } } @@ -705,6 +707,18 @@ module.exports = class MetamaskController extends EventEmitter { return selectedAddress } + /** + * Removes a "Loose" account from state. + * + * @param {string[]} address A hex address + * + */ + async removeAccount (address) { + this.preferencesController.removeAddress(address) + return address + } + + /** * Imports an account with the specified import strategy. * These are defined in app/scripts/account-import-strategies diff --git a/ui/app/actions.js b/ui/app/actions.js index f04de8fe8..3bdd548d3 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -83,6 +83,7 @@ var actions = { NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN', navigateToNewAccountScreen, resetAccount, + removeAccount, showNewVaultSeed: showNewVaultSeed, showInfoPage: showInfoPage, CLOSE_WELCOME_SCREEN: 'CLOSE_WELCOME_SCREEN', @@ -535,6 +536,26 @@ function resetAccount () { } } +function removeAccount (address) { + return dispatch => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + background.removeAccount(address, (err, account) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + log.info('Account removed: ' + account) + dispatch(actions.showAccountsPage()) + resolve() + }) + }) + } +} + function addNewKeyring (type, opts) { return (dispatch) => { dispatch(actions.showLoadingIndication()) diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js index b561ea186..73450c1bd 100644 --- a/ui/app/components/account-menu/index.js +++ b/ui/app/components/account-menu/index.js @@ -68,7 +68,7 @@ function mapDispatchToProps (dispatch) { dispatch(actions.hideSidebar()) dispatch(actions.toggleAccountMenu()) }, - showForgetAccountConfirmationModal: (address) => { + showRemoveAccountConfirmationModal: (address) => { return dispatch(actions.showModal({ name: 'CONFIRM_FORGET_ACCOUNT', address })) }, } @@ -156,7 +156,8 @@ AccountMenu.prototype.renderAccounts = function () { } = this.props const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), []) - return accountOrder.map((address) => { + return accountOrder.filter(address => !!identities[address]).map((address) => { + const identity = identities[address] const isSelected = identity.address === selectedAddress @@ -191,25 +192,24 @@ AccountMenu.prototype.renderAccounts = function () { ]), this.renderKeyringType(keyring), - this.renderForgetAccount(keyring, identity.address), + this.renderRemoveAccount(keyring, identity.address), ], ) }) } -AccountMenu.prototype.renderForgetAccount = function (keyring, address) { +AccountMenu.prototype.renderRemoveAccount = function (keyring, address) { // Any account that's not form the HD wallet can be forgotten const type = keyring.type - const isForgetable = type !== 'HD Key Tree' - return isForgetable ? h('a.forget-account-icon', { onClick: (e) => this.forgetAccount(e, address) }, '') : null + const isRemovable = type !== 'HD Key Tree' + return isRemovable ? h('a.forget-account-icon', { onClick: (e) => this.removeAccount(e, address) }, '') : null } -AccountMenu.prototype.forgetAccount = function (e, address) { +AccountMenu.prototype.removeAccount = function (e, address) { e.preventDefault() e.stopPropagation() - const { showForgetAccountConfirmationModal } = this.props - console.log('should forget address: ', address) - showForgetAccountConfirmationModal(address) + const { showRemoveAccountConfirmationModal } = this.props + showRemoveAccountConfirmationModal(address) } AccountMenu.prototype.renderKeyringType = function (keyring) { diff --git a/ui/app/components/modals/confirm-remove-account/confirm-remove-account.container.js b/ui/app/components/modals/confirm-remove-account/confirm-remove-account.container.js index 9a612f2f6..fcb149b3f 100644 --- a/ui/app/components/modals/confirm-remove-account/confirm-remove-account.container.js +++ b/ui/app/components/modals/confirm-remove-account/confirm-remove-account.container.js @@ -3,6 +3,12 @@ import ConfirmRemoveAccount from './confirm-remove-account.component' const { hideModal, removeAccount } = require('../../../actions') +const mapStateToProps = state => { + return { + address: state.appState.modal.modalState.props.address, + } +} + const mapDispatchToProps = dispatch => { return { hideModal: () => dispatch(hideModal()), @@ -10,4 +16,4 @@ const mapDispatchToProps = dispatch => { } } -export default connect(null, mapDispatchToProps)(ConfirmRemoveAccount) +export default connect(mapStateToProps, mapDispatchToProps)(ConfirmRemoveAccount) diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 758cfa4a2..9ace56661 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -1,5 +1,4 @@ -const React = require('react') -const Component = React.Component +const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect @@ -385,7 +384,7 @@ Modal.prototype.render = function () { backdropStyle: BACKDROPSTYLE, closeOnClick: !disableBackdropClick, }, - React.cloneElement(children, {...this.props.modalState.props}, null), + children, ) }