From f4d58ebc705287788c9eaee6025269dd78b051a1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 4 May 2016 18:08:31 -0700 Subject: [PATCH 1/5] Move account export to subview Account detail view now has an animated transitioning `subview` section that allows us to show extra details within it. Clicking `export` now slide replaces the transaction list with the export UI. Added cancel/done/submit buttons to the Export UI. Done submits like Enter did, the other two transition back to the transaction list. For some reason when first unlocking, the selected account is being instantly replaced with the accounts list, so I need to fix that before merging this into master. --- ui/app/account-detail.js | 106 ++++++++++---------------- ui/app/components/account-export.js | 87 +++++++++++++++++++++ ui/app/components/transaction-list.js | 2 +- ui/app/reducers/app.js | 17 +++++ 4 files changed, 145 insertions(+), 67 deletions(-) create mode 100644 ui/app/components/account-export.js diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index a876b275fdf2..208ecc63ad1e 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -1,11 +1,15 @@ const inherits = require('util').inherits +const extend = require('xtend') const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect const copyToClipboard = require('copy-to-clipboard') const actions = require('./actions') +const ReactCSSTransitionGroup = require('react-addons-css-transition-group') + const AccountPanel = require('./components/account-panel') const transactionList = require('./components/transaction-list') +const ExportAccountView = require('./components/account-export') module.exports = connect(mapStateToProps)(AccountDetailScreen) @@ -78,12 +82,13 @@ AccountDetailScreen.prototype.render = function() { }, 'EXPORT'), ]), - transactionList(transactions - .filter(tx => tx.txParams.from === state.address) - .filter(tx => tx.txParams.metamaskNetworkId === state.networkVersion) - .sort((a, b) => b.time - a.time), state.networkVersion), - this.exportedAccount(accountDetail), - + h(ReactCSSTransitionGroup, { + transitionName: "main", + transitionEnterTimeout: 300, + transitionLeaveTimeout: 300, + }, [ + this.subview(), + ]), // transaction table /* h('section.flex-column', [ @@ -94,72 +99,41 @@ AccountDetailScreen.prototype.render = function() { ) } +AccountDetailScreen.prototype.subview = function() { + var subview + try { + subview = this.props.accountDetail.subview + } catch (e) { + subview = null + } + + switch (subview) { + case 'transactions': + return this.transactionList() + case 'export': + var state = extend({key: 'export'}, this.props) + return h(ExportAccountView, state) + default: + return this.transactionList() + } +} + +AccountDetailScreen.prototype.transactionList = function() { + var state = this.props + var transactions = state.transactions + + return transactionList(transactions + .filter(tx => tx.txParams.from === state.address) + .filter(tx => tx.txParams.metamaskNetworkId === state.networkVersion) + .sort((a, b) => b.time - a.time), state.networkVersion) +} + AccountDetailScreen.prototype.navigateToAccounts = function(event){ event.stopPropagation() this.props.dispatch(actions.showAccountsPage()) } -AccountDetailScreen.prototype.exportAccount = function(address) { - this.props.dispatch(actions.exportAccount(address)) -} - AccountDetailScreen.prototype.requestAccountExport = function() { this.props.dispatch(actions.requestExportAccount()) } -AccountDetailScreen.prototype.exportedAccount = function(accountDetail) { - if (!accountDetail) return - var accountExport = accountDetail.accountExport - - var notExporting = accountExport === 'none' - var exportRequested = accountExport === 'requested' - var accountExported = accountExport === 'completed' - - if (notExporting) return - - if (exportRequested) { - var warning = `Exporting your private key is very dangerous, - and you should only do it if you know what you're doing.` - var confirmation = `If you're absolutely sure, type "I understand" below and - hit Enter.` - return h('div', {}, [ - h('p.error', warning), - h('p', confirmation), - h('input#exportAccount', { - onKeyPress: this.onExportKeyPress.bind(this), - }) - ]) - } - - if (accountExported) { - return h('div.privateKey', { - - }, [ - h('label', 'Your private key (click to copy):'), - h('p.error.cursor-pointer', { - style: { - textOverflow: 'ellipsis', - overflow: 'hidden', - webkitUserSelect: 'text', - width: '100%', - }, - onClick: function(event) { - copyToClipboard(accountDetail.privateKey) - } - }, accountDetail.privateKey), - ]) - } -} - -AccountDetailScreen.prototype.onExportKeyPress = function(event) { - if (event.key !== 'Enter') return - event.preventDefault() - - var input = document.getElementById('exportAccount') - if (input.value === 'I understand') { - this.props.dispatch(actions.exportAccount(this.props.address)) - } else { - input.value = '' - input.placeholder = 'Please retype "I understand" exactly.' - } -} diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js new file mode 100644 index 000000000000..f79a533ba6ac --- /dev/null +++ b/ui/app/components/account-export.js @@ -0,0 +1,87 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const actions = require('../actions') + +module.exports = ExportAccountView + + +inherits(ExportAccountView, Component) +function ExportAccountView() { + Component.call(this) +} + +ExportAccountView.prototype.render = function() { + console.log("EXPORT VIEW") + console.dir(this.props) + var state = this.props + var accountDetail = state.accountDetail + + if (!accountDetail) return h('div') + var accountExport = accountDetail.accountExport + + var notExporting = accountExport === 'none' + var exportRequested = accountExport === 'requested' + var accountExported = accountExport === 'completed' + + if (notExporting) return h('div') + + if (exportRequested) { + var warning = `Exporting your private key is very dangerous, + and you should only do it if you know what you're doing.` + var confirmation = `If you're absolutely sure, type "I understand" below and + submit.` + return h('div', { key: 'exporting' }, [ + h('p.error', warning), + h('p', confirmation), + h('input#exportAccount', { + onKeyPress: this.onExportKeyPress.bind(this), + }), + h('button', { + onClick: () => this.onExportKeyPress({ key: 'Enter', preventDefault: () => {} }), + }, 'Submit'), + h('button', { + onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)) + }, 'Cancel'), + ]) + } + + if (accountExported) { + return h('div.privateKey', { + + }, [ + h('label', 'Your private key (click to copy):'), + h('p.error.cursor-pointer', { + style: { + textOverflow: 'ellipsis', + overflow: 'hidden', + webkitUserSelect: 'text', + width: '100%', + }, + onClick: function(event) { + copyToClipboard(accountDetail.privateKey) + } + }, accountDetail.privateKey), + h('button', { + onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)) + }, 'Done'), + ]) + } +} + +ExportAccountView.prototype.onExportKeyPress = function(event) { + if (event.key !== 'Enter') return + event.preventDefault() + + var input = document.getElementById('exportAccount') + if (input.value === 'I understand') { + this.props.dispatch(actions.exportAccount(this.props.address)) + } else { + input.value = '' + input.placeholder = 'Please retype "I understand" exactly.' + } +} + +ExportAccountView.prototype.exportAccount = function(address) { + this.props.dispatch(actions.exportAccount(address)) +} diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js index 99a325e35ffd..90f2955e6285 100644 --- a/ui/app/components/transaction-list.js +++ b/ui/app/components/transaction-list.js @@ -4,7 +4,7 @@ const addressSummary = require('../util').addressSummary const explorerLink = require('../../lib/explorer-link') module.exports = function(transactions, network) { - return h('details', [ + return h('details', { key: 'transaction-list' }, [ h('summary', [ h('div.font-small', {style: {display: 'inline'}}, 'Transactions'), diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 14f92a8c5d79..35ddf6759796 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -23,6 +23,9 @@ function reduceApp(state, action) { var appState = extend({ currentView: seedWords ? seedConfView : defaultView, + accountDetail: { + subview: 'transactions', + }, currentDomain: 'example.com', transForward: true, // Used to render transition direction isLoading: false, // Used to display loading indicator @@ -131,6 +134,7 @@ function reduceApp(state, action) { return extend(appState, { currentView: {}, accountDetail: { + subview: 'transactions', accountExport: 'none', privateKey: '', }, @@ -144,6 +148,7 @@ function reduceApp(state, action) { context: action.value || account, }, accountDetail: { + subview: 'transactions', accountExport: 'none', privateKey: '', }, @@ -157,6 +162,7 @@ function reduceApp(state, action) { context: action.value, }, accountDetail: { + subview: 'transactions', accountExport: 'none', privateKey: '', }, @@ -218,6 +224,9 @@ function reduceApp(state, action) { name: 'accountDetail', context: state.metamask.selectedAddress, }, + accountDetail: { + subview: 'transactions', + }, }) } @@ -285,7 +294,13 @@ function reduceApp(state, action) { case actions.REQUEST_ACCOUNT_EXPORT: return extend(appState, { + transForward: true, + currentView: { + name: 'accountDetail', + context: appState.currentView.context, + }, accountDetail: { + subview: 'export', accountExport: 'requested', }, }) @@ -293,6 +308,7 @@ function reduceApp(state, action) { case actions.EXPORT_ACCOUNT: return extend(appState, { accountDetail: { + subview: 'export', accountExport: 'completed', }, }) @@ -300,6 +316,7 @@ function reduceApp(state, action) { case actions.SHOW_PRIVATE_KEY: return extend(appState, { accountDetail: { + subview: 'export', accountExport: 'completed', privateKey: action.value, }, From 37dc13ce3bf0c40b755d33b8ba63c4662a0d3415 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 4 May 2016 18:18:58 -0700 Subject: [PATCH 2/5] Bump changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47cd69897edc..f6295cf7b889 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Current Master - Add support for calls to `eth.sign`. +- Moved account exporting within transitioning subview on the account detail view. +- Added buttons to the account export process. ## 1.7.0 2016-04-29 From d929f8053202dabb866f5aea3fc0238593c474ce Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 4 May 2016 20:37:39 -0700 Subject: [PATCH 3/5] Fix routing bug Where sometimes the account list was shown when first unlocking, after momentarily showing the detail panel. --- ui/app/actions.js | 1 - ui/app/reducers/app.js | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 072139e1aac6..9a8ba76d12fd 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -108,7 +108,6 @@ function tryUnlockMetamask(password) { return (dispatch) => { dispatch(this.unlockInProgress()) _accountManager.submitPassword(password, (err, selectedAccount) => { - dispatch(this.hideLoadingIndication()) if (err) { dispatch(this.unlockFailed()) } else { diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 14f92a8c5d79..d958b6730f74 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -109,7 +109,9 @@ function reduceApp(state, action) { case actions.UNLOCK_METAMASK: return extend(appState, { currentView: {}, + detailView: {}, transForward: true, + isLoading: false, warning: null, }) From 93db6cacb5bd05fd5f8b786733f13231a2c3fb0c Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 4 May 2016 20:41:29 -0700 Subject: [PATCH 4/5] Fix button width transition bug Fixes #160 --- ui/app/account-detail.js | 6 +++++- ui/app/app.js | 3 ++- ui/app/css/transitions.css | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index a876b275fdf2..9edb99e2846e 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -35,7 +35,11 @@ AccountDetailScreen.prototype.render = function() { return ( - h('.account-detail-section.flex-column.flex-grow', [ + h('.account-detail-section.flex-column.flex-grow', { + style: { + width: '330px', + }, + }, [ // subtitle and nav h('.section-title.flex-row.flex-center', [ diff --git a/ui/app/app.js b/ui/app/app.js index 94c72a3c8b5e..a4ce40881351 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -65,7 +65,7 @@ App.prototype.render = function() { h('.flex-column.flex-grow.full-height', { style: { // Windows was showing a vertical scroll bar: - overflowY: 'hidden', + overflow: 'hidden', } }, [ @@ -82,6 +82,7 @@ App.prototype.render = function() { h('.app-primary.flex-grow' + (transForward ? '.from-right' : '.from-left'), { style: { height: '380px', + width: '360px', } }, [ h(ReactCSSTransitionGroup, { diff --git a/ui/app/css/transitions.css b/ui/app/css/transitions.css index 3d0bf46a16ee..e2225a98da7e 100644 --- a/ui/app/css/transitions.css +++ b/ui/app/css/transitions.css @@ -19,6 +19,7 @@ position: absolute; width: 100%; transition: transform 300ms ease-in-out; + overflow-x: hidden; } /* final positions */ From cad9a5e6e93008db5ec87e221c0889ee26f63b5b Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 4 May 2016 20:44:47 -0700 Subject: [PATCH 5/5] Bump changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6295cf7b889..c3c51936ef11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Add support for calls to `eth.sign`. - Moved account exporting within transitioning subview on the account detail view. - Added buttons to the account export process. +- Improved visual appearance of account detail transition where button heights would change. ## 1.7.0 2016-04-29