diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c4f78d121..8e119d3e4 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -339,6 +339,9 @@ "followTwitter": { "message": "Follow us on Twitter" }, + "forgetDevice": { + "message": "Forget this device" + }, "from": { "message": "From" }, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index cd6fdcc37..b8b7c38e4 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -357,8 +357,12 @@ module.exports = class MetamaskController extends EventEmitter { removeAccount: nodeify(this.removeAccount, this), importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this), - // trezor + // hardware wallets connectHardware: nodeify(this.connectHardware, this), + forgetDevice: nodeify(this.forgetDevice, this), + checkHardwareStatus: nodeify(this.checkHardwareStatus, this), + + // TREZOR unlockTrezorAccount: nodeify(this.unlockTrezorAccount, this), // vault management @@ -561,6 +565,37 @@ module.exports = class MetamaskController extends EventEmitter { } } + async checkHardwareStatus (deviceName) { + + switch (deviceName) { + case 'trezor': + const keyringController = this.keyringController + const keyring = await keyringController.getKeyringsByType( + 'Trezor Hardware' + )[0] + if (!keyring) { + return false + } + return keyring.isUnlocked() + } + } + + async forgetDevice (deviceName) { + + switch (deviceName) { + case 'trezor': + const keyringController = this.keyringController + const keyring = await keyringController.getKeyringsByType( + 'Trezor Hardware' + )[0] + if (!keyring) { + return false + } + keyring.forgetDevice() + return true + } + } + /** * Imports an account from a trezor device. * diff --git a/ui/app/actions.js b/ui/app/actions.js index 3bdd548d3..9330a864b 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -79,6 +79,8 @@ var actions = { importNewAccount, addNewAccount, connectHardware, + checkHardwareStatus, + forgetDevice, unlockTrezorAccount, NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN', navigateToNewAccountScreen, @@ -622,6 +624,48 @@ function addNewAccount () { } } +function checkHardwareStatus (deviceName) { + log.debug(`background.checkHardwareStatus`, deviceName) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.checkHardwareStatus(deviceName, (err, unlocked) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve(unlocked) + }) + }) + } +} + +function forgetDevice (deviceName) { + log.debug(`background.forgetDevice`, deviceName) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.forgetDevice(deviceName, (err, response) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve() + }) + }) + } +} + function connectHardware (deviceName, page) { log.debug(`background.connectHardware`, deviceName, page) return (dispatch, getState) => { diff --git a/ui/app/components/pages/create-account/connect-hardware/account-list.js b/ui/app/components/pages/create-account/connect-hardware/account-list.js index 170d8f0b3..3bd6a00a7 100644 --- a/ui/app/components/pages/create-account/connect-hardware/account-list.js +++ b/ui/app/components/pages/create-account/connect-hardware/account-list.js @@ -90,11 +90,20 @@ class AccountList extends Component { ]) } + renderForgetDevice () { + return h('div.hw-forget-device-container', {}, [ + h('a', { + onClick: this.props.onForgetDevice.bind(this), + }, this.context.t('forgetDevice')), + ]) + } + render () { return h('div', {}, [ this.renderAccounts(), this.renderPagination(), this.renderButtons(), + this.renderForgetDevice(), ]) } @@ -104,6 +113,7 @@ class AccountList extends Component { AccountList.propTypes = { accounts: PropTypes.array.isRequired, onAccountChange: PropTypes.func.isRequired, + onForgetDevice: PropTypes.func.isRequired, getPage: PropTypes.func.isRequired, network: PropTypes.string, selectedAccount: PropTypes.string, diff --git a/ui/app/components/pages/create-account/connect-hardware/index.js b/ui/app/components/pages/create-account/connect-hardware/index.js index 126102235..9aef36cfb 100644 --- a/ui/app/components/pages/create-account/connect-hardware/index.js +++ b/ui/app/components/pages/create-account/connect-hardware/index.js @@ -13,13 +13,19 @@ class ConnectHardwareForm extends Component { super(props) this.state = { error: null, - response: null, btnText: context.t('connectToTrezor'), selectedAccount: null, accounts: [], } } + async componentDidMount () { + const unlocked = await this.props.checkHardwareStatus('trezor') + if (unlocked) { + this.getPage(0) + } + } + connectToTrezor = () => { if (this.state.accounts.length) { return null @@ -70,6 +76,20 @@ class ConnectHardwareForm extends Component { }) } + onForgetDevice = () => { + this.props.forgetDevice('trezor') + .then(_ => { + this.setState({ + error: null, + btnText: this.context.t('connectToTrezor'), + selectedAccount: null, + accounts: [], + }) + }).catch(e => { + this.setState({ error: e.toString() }) + }) + } + onUnlockAccount = () => { if (this.state.selectedAccount === null) { @@ -110,6 +130,7 @@ class ConnectHardwareForm extends Component { getPage: this.getPage, history: this.props.history, onUnlockAccount: this.onUnlockAccount, + onForgetDevice: this.onForgetDevice, onCancel: this.onCancel, }) } @@ -127,6 +148,8 @@ ConnectHardwareForm.propTypes = { showImportPage: PropTypes.func, showConnectPage: PropTypes.func, connectHardware: PropTypes.func, + checkHardwareStatus: PropTypes.func, + forgetDevice: PropTypes.func, unlockTrezorAccount: PropTypes.func, numberOfExistingAccounts: PropTypes.number, history: PropTypes.object, @@ -154,6 +177,12 @@ const mapDispatchToProps = dispatch => { connectHardware: (deviceName, page) => { return dispatch(actions.connectHardware(deviceName, page)) }, + checkHardwareStatus: (deviceName) => { + return dispatch(actions.checkHardwareStatus(deviceName)) + }, + forgetDevice: (deviceName) => { + return dispatch(actions.forgetDevice(deviceName)) + }, unlockTrezorAccount: index => { return dispatch(actions.unlockTrezorAccount(index)) }, diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss index 66eb47378..e11c10dfe 100644 --- a/ui/app/css/itcss/components/new-account.scss +++ b/ui/app/css/itcss/components/new-account.scss @@ -270,6 +270,17 @@ } } +.hw-forget-device-container { + display: flex; + flex-flow: column; + align-items: center; + padding: 30px 30px 0; + + a { + color: #2f9ae0; + cursor: pointer; + } +} .new-account-create-form { display: flex;