diff options
Diffstat (limited to 'ui/app')
-rw-r--r-- | ui/app/accounts/import/index.js | 91 | ||||
-rw-r--r-- | ui/app/accounts/import/json.js | 27 | ||||
-rw-r--r-- | ui/app/accounts/import/private-key.js | 69 | ||||
-rw-r--r-- | ui/app/accounts/import/seed.js | 30 | ||||
-rw-r--r-- | ui/app/accounts/index.js | 10 | ||||
-rw-r--r-- | ui/app/actions.js | 26 | ||||
-rw-r--r-- | ui/app/app.js | 11 | ||||
-rw-r--r-- | ui/app/components/buy-button-subview.js | 82 | ||||
-rw-r--r-- | ui/app/components/tab-bar.js | 35 | ||||
-rw-r--r-- | ui/app/css/lib.css | 8 | ||||
-rw-r--r-- | ui/app/info.js | 2 | ||||
-rw-r--r-- | ui/app/reducers/app.js | 17 | ||||
-rw-r--r-- | ui/app/unlock.js | 2 |
13 files changed, 370 insertions, 40 deletions
diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js new file mode 100644 index 000000000..18a6b985c --- /dev/null +++ b/ui/app/accounts/import/index.js @@ -0,0 +1,91 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect +import Select from 'react-select' + +// Subviews +const JsonImportView = require('./json.js') +const SeedImportView = require('./seed.js') +const PrivateKeyImportView = require('./private-key.js') + +const menuItems = [ + 'Private Key', +] + +module.exports = connect(mapStateToProps)(AccountImportSubview) + +function mapStateToProps (state) { + return { + menuItems, + } +} + +inherits(AccountImportSubview, Component) +function AccountImportSubview () { + Component.call(this) +} + +AccountImportSubview.prototype.render = function () { + const props = this.props + const state = this.state || {} + const { menuItems } = props + const { type } = state + + return ( + h('div', { + style: { + }, + }, [ + h('div', { + style: { + padding: '10px', + color: 'rgb(174, 174, 174)', + }, + }, [ + + h('h3', { style: { padding: '3px' } }, 'SELECT TYPE'), + + h('style', ` + .has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label { + color: rgb(174,174,174); + } + `), + + h(Select, { + name: 'import-type-select', + clearable: false, + value: type || menuItems[0], + options: menuItems.map((type) => { + return { + value: type, + label: type, + } + }), + onChange: (opt) => { + this.setState({ type: opt.value }) + }, + }), + ]), + + this.renderImportView(), + ]) + ) +} + +AccountImportSubview.prototype.renderImportView = function() { + const props = this.props + const state = this.state || {} + const { type } = state + const { menuItems } = props + const current = type || menuItems[0] + + switch (current) { + case 'HD Key Tree': + return h(SeedImportView) + case 'Private Key': + return h(PrivateKeyImportView) + default: + return h(JsonImportView) + } +} diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js new file mode 100644 index 000000000..22cf95cfd --- /dev/null +++ b/ui/app/accounts/import/json.js @@ -0,0 +1,27 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect + +module.exports = connect(mapStateToProps)(JsonImportSubview) + +function mapStateToProps (state) { + return {} +} + +inherits(JsonImportSubview, Component) +function JsonImportSubview () { + Component.call(this) +} + +JsonImportSubview.prototype.render = function () { + return ( + h('div', { + style: { + }, + }, [ + `Upload your json file here!`, + ]) + ) +} + diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js new file mode 100644 index 000000000..6b988a76b --- /dev/null +++ b/ui/app/accounts/import/private-key.js @@ -0,0 +1,69 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect +const type = 'Simple Key Pair' +const actions = require('../../actions') + +module.exports = connect(mapStateToProps)(PrivateKeyImportView) + +function mapStateToProps (state) { + return { + error: state.appState.warning, + } +} + +inherits(PrivateKeyImportView, Component) +function PrivateKeyImportView () { + Component.call(this) +} + +PrivateKeyImportView.prototype.render = function () { + const { error } = this.props + + return ( + h('div', { + style: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: '5px 15px 0px 15px', + }, + }, [ + h('span', 'Paste your private key string here'), + + h('input.large-input.letter-spacey', { + type: 'password', + id: 'private-key-box', + onKeyPress: this.createKeyringOnEnter.bind(this), + style: { + width: 260, + marginTop: 12, + }, + }), + + h('button.primary', { + onClick: this.createNewKeychain.bind(this), + style: { + margin: 12, + }, + }, 'Import'), + + error ? h('span.warning', error) : null, + ]) + ) +} + +PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) { + if (event.key === 'Enter') { + event.preventDefault() + this.createNewKeychain() + } +} + +PrivateKeyImportView.prototype.createNewKeychain = function () { + const input = document.getElementById('private-key-box') + const privateKey = input.value + this.props.dispatch(actions.addNewKeyring(type, [ privateKey ])) +} + diff --git a/ui/app/accounts/import/seed.js b/ui/app/accounts/import/seed.js new file mode 100644 index 000000000..b4a7c0afa --- /dev/null +++ b/ui/app/accounts/import/seed.js @@ -0,0 +1,30 @@ +const inherits = require('util').inherits +const Component = require('react').Component +const h = require('react-hyperscript') +const connect = require('react-redux').connect + +module.exports = connect(mapStateToProps)(SeedImportSubview) + +function mapStateToProps (state) { + return {} +} + +inherits(SeedImportSubview, Component) +function SeedImportSubview () { + Component.call(this) +} + +SeedImportSubview.prototype.render = function () { + return ( + h('div', { + style: { + }, + }, [ + `Paste your seed phrase here!`, + h('textarea'), + h('br'), + h('button', 'Submit'), + ]) + ) +} + diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js index edb15eafe..e6f376735 100644 --- a/ui/app/accounts/index.js +++ b/ui/app/accounts/index.js @@ -73,7 +73,8 @@ AccountsScreen.prototype.render = function () { const simpleAddress = identity.address.substring(2).toLowerCase() const keyring = keyrings.find((kr) => { - return kr.accounts.includes(simpleAddress) + return kr.accounts.includes(simpleAddress) || + kr.accounts.includes(identity.address) }) return h(AccountListItem, { @@ -154,6 +155,13 @@ AccountsScreen.prototype.addNewAccount = function () { this.props.dispatch(actions.addNewAccount(0)) } +/* An optional view proposed in this design: + * https://consensys.quip.com/zZVrAysM5znY +AccountsScreen.prototype.addNewAccount = function () { + this.props.dispatch(actions.navigateToNewAccountScreen()) +} +*/ + AccountsScreen.prototype.goHome = function () { this.props.dispatch(actions.goHome()) } diff --git a/ui/app/actions.js b/ui/app/actions.js index 5a3968f82..7934a329a 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -32,16 +32,20 @@ var actions = { SHOW_INIT_MENU: 'SHOW_INIT_MENU', SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED', SHOW_INFO_PAGE: 'SHOW_INFO_PAGE', + SHOW_IMPORT_PAGE: 'SHOW_IMPORT_PAGE', unlockMetamask: unlockMetamask, unlockFailed: unlockFailed, showCreateVault: showCreateVault, showRestoreVault: showRestoreVault, showInitializeMenu: showInitializeMenu, + showImportPage, createNewVaultAndKeychain: createNewVaultAndKeychain, createNewVaultAndRestore: createNewVaultAndRestore, createNewVaultInProgress: createNewVaultInProgress, addNewKeyring, addNewAccount, + NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN', + navigateToNewAccountScreen, showNewVaultSeed: showNewVaultSeed, showInfoPage: showInfoPage, // seed recovery actions @@ -249,7 +253,21 @@ function requestRevealSeed (password) { } function addNewKeyring (type, opts) { - return callBackgroundThenUpdate(background.addNewKeyring, type, opts) + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + background.addNewKeyring(type, opts, (err, newState) => { + dispatch(actions.hideLoadingIndication()) + if (err) return dispatch(actions.displayWarning(err.message)) + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.showAccountsPage()) + }) + } +} + +function navigateToNewAccountScreen() { + return { + type: this.NEW_ACCOUNT_SCREEN, + } } function addNewAccount (ringNumber = 0) { @@ -376,6 +394,12 @@ function showInitializeMenu () { } } +function showImportPage () { + return { + type: actions.SHOW_IMPORT_PAGE, + } +} + function agreeToDisclaimer () { return (dispatch) => { dispatch(this.showLoadingIndication()) diff --git a/ui/app/app.js b/ui/app/app.js index 9efe95874..0e04c334c 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -20,6 +20,7 @@ const NoticeScreen = require('./components/notice') const generateLostAccountsNotice = require('../lib/lost-accounts-notice') // other views const ConfigScreen = require('./config') +const Import = require('./accounts/import') const InfoScreen = require('./info') const LoadingIndicator = require('./components/loading') const SandwichExpando = require('sandwich-expando') @@ -305,6 +306,13 @@ App.prototype.renderDropdown = function () { }), h(DropMenuItem, { + label: 'Import Account', + closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), + action: () => this.props.dispatch(actions.showImportPage()), + icon: h('i.fa.fa-arrow-circle-o-up.fa-lg'), + }), + + h(DropMenuItem, { label: 'Lock', closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), action: () => this.props.dispatch(actions.lockMetamask()), @@ -411,6 +419,9 @@ App.prototype.renderPrimary = function () { case 'config': return h(ConfigScreen, {key: 'config'}) + case 'import-menu': + return h(Import, {key: 'import-menu'}) + case 'reveal-seed-conf': return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'}) diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js index 35eda647e..afda5bf59 100644 --- a/ui/app/components/buy-button-subview.js +++ b/ui/app/components/buy-button-subview.js @@ -7,6 +7,7 @@ const CoinbaseForm = require('./coinbase-form') const ShapeshiftForm = require('./shapeshift-form') const extension = require('../../../app/scripts/lib/extension') const Loading = require('./loading') +const TabBar = require('./tab-bar') module.exports = connect(mapStateToProps)(BuyButtonSubview) @@ -29,7 +30,6 @@ function BuyButtonSubview () { BuyButtonSubview.prototype.render = function () { const props = this.props - const currentForm = props.buyView.formView const isLoading = props.isSubLoading return ( @@ -53,43 +53,53 @@ BuyButtonSubview.prototype.render = function () { h(Loading, { isLoading }), - h('h3.flex-row.text-transform-uppercase', { - style: { - background: '#EBEBEB', - color: '#AEAEAE', - paddingTop: '4px', - justifyContent: 'space-around', + h(TabBar, { + tabs: [ + { + content: [ + 'Coinbase', + h('a', { + onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'), + }, [ + h('i.fa.fa-question-circle', { + style: { + margin: '0px 5px', + }, + }), + ]), + ], + key: 'coinbase', + }, + { + content: [ + 'Shapeshift', + h('a', { + href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md', + onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'), + }, [ + h('i.fa.fa-question-circle', { + style: { + margin: '0px 5px', + }, + }), + ]), + ], + key: 'shapeshift', + }, + ], + defaultTab: 'coinbase', + tabSelected: (key) => { + switch (key) { + case 'coinbase': + props.dispatch(actions.coinBaseSubview()) + break + case 'shapeshift': + props.dispatch(actions.shapeShiftSubview(props.provider.type)) + break + } }, - }, [ - h(currentForm.coinbase ? '.activeForm' : '.inactiveForm.pointer', { - onClick: () => props.dispatch(actions.coinBaseSubview()), - }, 'Coinbase'), - h('a', { - onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'), - }, [ - h('i.fa.fa-question-circle', { - style: { - position: 'relative', - right: '33px', - }, - }), - ]), - h(currentForm.shapeshift ? '.activeForm' : '.inactiveForm.pointer', { - onClick: () => props.dispatch(actions.shapeShiftSubview(props.provider.type)), - }, 'Shapeshift'), + }), - h('a', { - href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md', - onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'), - }, [ - h('i.fa.fa-question-circle', { - style: { - position: 'relative', - right: '28px', - }, - }), - ]), - ]), this.formVersionSubview(), ]) ) diff --git a/ui/app/components/tab-bar.js b/ui/app/components/tab-bar.js new file mode 100644 index 000000000..65078e0a4 --- /dev/null +++ b/ui/app/components/tab-bar.js @@ -0,0 +1,35 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits + +module.exports = TabBar + +inherits(TabBar, Component) +function TabBar () { + Component.call(this) +} + +TabBar.prototype.render = function () { + const props = this.props + const state = this.state || {} + const { tabs = [], defaultTab, tabSelected } = props + const { subview = defaultTab } = state + + return ( + h('.flex-row.space-around.text-transform-uppercase', { + style: { + background: '#EBEBEB', + color: '#AEAEAE', + paddingTop: '4px', + }, + }, tabs.map((tab) => { + const { key, content } = tab + return h(subview === key ? '.activeForm' : '.inactiveForm.pointer', { + onClick: () => { + this.setState({ subview: key }) + tabSelected(key) + }, + }, content) + })) + ) +} diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css index abbf8667e..a8df1d115 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -23,6 +23,14 @@ flex-direction: column; } +.space-between { + justify-content: space-between; +} + +.space-around { + justify-content: space-around; +} + .flex-column-bottom { display: flex; flex-direction: column-reverse; diff --git a/ui/app/info.js b/ui/app/info.js index cc753b2ea..e79580be4 100644 --- a/ui/app/info.js +++ b/ui/app/info.js @@ -110,7 +110,7 @@ InfoScreen.prototype.render = function () { onClick (event) { this.navigateTo(event.target.href) }, }, [ h('img.icon-size', { - src: manifest.icons[128], + src: manifest.icons['128'], style: { filter: 'grayscale(100%)', /* IE6-9 */ WebkitFilter: 'grayscale(100%)', /* Microsoft Edge and Firefox 35+ */ diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index dc7344b3e..ae91272cc 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -99,6 +99,14 @@ function reduceApp (state, action) { transForward: action.value, }) + case actions.SHOW_IMPORT_PAGE: + return extend(appState, { + currentView: { + name: 'import-menu', + }, + transForward: true, + }) + case actions.SHOW_INFO_PAGE: return extend(appState, { currentView: { @@ -128,6 +136,15 @@ function reduceApp (state, action) { isLoading: false, }) + case actions.NEW_ACCOUNT_SCREEN: + return extend(appState, { + currentView: { + name: 'new-account', + context: appState.currentView.context, + }, + transForward: true, + }) + case actions.SHOW_SEND_PAGE: return extend(appState, { currentView: { diff --git a/ui/app/unlock.js b/ui/app/unlock.js index 19f5eaec2..1aee3c5d0 100644 --- a/ui/app/unlock.js +++ b/ui/app/unlock.js @@ -26,7 +26,7 @@ UnlockScreen.prototype.render = function () { const state = this.props const warning = state.warning return ( - h('.flex-column.hey-im-here', [ + h('.flex-column', [ h('.unlock-screen.flex-column.flex-center.flex-grow', [ h(Mascot, { |