diff --git a/CHANGELOG.md b/CHANGELOG.md index 47cd69897..f6295cf7b 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 diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index 9edb99e28..c93eef4fd 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) @@ -82,12 +86,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', [ @@ -98,72 +103,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 000000000..f79a533ba --- /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 99a325e35..90f2955e6 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 d958b6730..309351956 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 @@ -133,6 +136,7 @@ function reduceApp(state, action) { return extend(appState, { currentView: {}, accountDetail: { + subview: 'transactions', accountExport: 'none', privateKey: '', }, @@ -146,6 +150,7 @@ function reduceApp(state, action) { context: action.value || account, }, accountDetail: { + subview: 'transactions', accountExport: 'none', privateKey: '', }, @@ -159,6 +164,7 @@ function reduceApp(state, action) { context: action.value, }, accountDetail: { + subview: 'transactions', accountExport: 'none', privateKey: '', }, @@ -220,6 +226,9 @@ function reduceApp(state, action) { name: 'accountDetail', context: state.metamask.selectedAddress, }, + accountDetail: { + subview: 'transactions', + }, }) } @@ -287,7 +296,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', }, }) @@ -295,6 +310,7 @@ function reduceApp(state, action) { case actions.EXPORT_ACCOUNT: return extend(appState, { accountDetail: { + subview: 'export', accountExport: 'completed', }, }) @@ -302,6 +318,7 @@ function reduceApp(state, action) { case actions.SHOW_PRIVATE_KEY: return extend(appState, { accountDetail: { + subview: 'export', accountExport: 'completed', privateKey: action.value, },