diff options
Diffstat (limited to 'ui/app')
-rw-r--r-- | ui/app/account-detail.js | 120 | ||||
-rw-r--r-- | ui/app/accounts/account-list-item.js | 91 | ||||
-rw-r--r-- | ui/app/accounts/index.js | 164 | ||||
-rw-r--r-- | ui/app/add-token.js | 2 | ||||
-rw-r--r-- | ui/app/app.js | 272 | ||||
-rw-r--r-- | ui/app/components/account-dropdowns.js | 227 | ||||
-rw-r--r-- | ui/app/components/account-info-link.js | 41 | ||||
-rw-r--r-- | ui/app/components/drop-menu-item.js | 59 | ||||
-rw-r--r-- | ui/app/components/dropdown.js | 89 | ||||
-rw-r--r-- | ui/app/components/editable-label.js | 7 | ||||
-rw-r--r-- | ui/app/components/pending-tx.js | 2 | ||||
-rw-r--r-- | ui/app/components/transaction-list.js | 8 | ||||
-rw-r--r-- | ui/app/css/index.css | 15 | ||||
-rw-r--r-- | ui/app/css/lib.css | 4 | ||||
-rw-r--r-- | ui/app/info.js | 10 | ||||
-rw-r--r-- | ui/app/keychains/hd/create-vault-complete.js | 2 |
16 files changed, 537 insertions, 576 deletions
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js index bed05a7fb..18c867153 100644 --- a/ui/app/account-detail.js +++ b/ui/app/account-detail.js @@ -3,21 +3,18 @@ const extend = require('xtend') const Component = require('react').Component const h = require('react-hyperscript') const connect = require('react-redux').connect -const CopyButton = require('./components/copyButton') -const AccountInfoLink = require('./components/account-info-link') const actions = require('./actions') const ReactCSSTransitionGroup = require('react-addons-css-transition-group') const valuesFor = require('./util').valuesFor - const Identicon = require('./components/identicon') const EthBalance = require('./components/eth-balance') const TransactionList = require('./components/transaction-list') const ExportAccountView = require('./components/account-export') const ethUtil = require('ethereumjs-util') const EditableLabel = require('./components/editable-label') -const Tooltip = require('./components/tooltip') const TabBar = require('./components/tab-bar') const TokenList = require('./components/token-list') +const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns module.exports = connect(mapStateToProps)(AccountDetailScreen) @@ -54,12 +51,18 @@ AccountDetailScreen.prototype.render = function () { return ( - h('.account-detail-section', [ + h('.account-detail-section', { + style: { + height: '100%', + maxWidth: '850px', + }, + }, [ // identicon, label, balance, etc h('.account-data-subsection', { style: { margin: '0 20px', + flex: '1 0 auto', }, }, [ @@ -84,6 +87,7 @@ AccountDetailScreen.prototype.render = function () { style: { lineHeight: '10px', marginLeft: '15px', + width: '100%', }, }, [ h(EditableLabel, { @@ -98,7 +102,42 @@ AccountDetailScreen.prototype.render = function () { // What is shown when not editing + edit text: h('label.editing-label', [h('.edit-text', 'edit')]), - h('h2.font-medium.color-forest', {name: 'edit'}, identity && identity.name), + h( + 'div', + { + style: { + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + }, + }, + [ + h( + 'h2.font-medium.color-forest', + { + name: 'edit', + style: { + }, + }, + [ + identity && identity.name, + ] + ), + h( + AccountDropdowns, + { + style: { + marginRight: '8px', + marginLeft: 'auto', + cursor: 'pointer', + }, + selected, + network, + identities: props.identities, + }, + ), + ] + ), ]), h('.flex-row', { style: { @@ -124,56 +163,6 @@ AccountDetailScreen.prototype.render = function () { color: '#AEAEAE', }, }, checksumAddress), - - // copy and export - - h('.flex-row', { - style: { - justifyContent: 'flex-end', - }, - }, [ - - h(AccountInfoLink, { selected, network }), - - h(CopyButton, { - value: checksumAddress, - }), - - h(Tooltip, { - title: 'QR Code', - }, [ - h('i.fa.fa-qrcode.pointer.pop-hover', { - onClick: () => props.dispatch(actions.showQrView(selected, identity ? identity.name : '')), - style: { - fontSize: '18px', - position: 'relative', - color: 'rgb(247, 134, 28)', - top: '5px', - marginLeft: '3px', - marginRight: '3px', - }, - }), - ]), - - h(Tooltip, { - title: 'Export Private Key', - }, [ - h('div', { - style: { - display: 'flex', - alignItems: 'center', - }, - }, [ - h('img.cursor-pointer.color-orange', { - src: 'images/key-32.png', - onClick: () => this.requestAccountExport(selected), - style: { - height: '19px', - }, - }), - ]), - ]), - ]), ]), // account ballence @@ -197,14 +186,11 @@ AccountDetailScreen.prototype.render = function () { }, }), + h('.flex-grow'), + h('button', { onClick: () => props.dispatch(actions.buyEthView(selected)), - style: { - marginBottom: '20px', - marginRight: '8px', - position: 'absolute', - left: '219px', - }, + style: { marginRight: '10px' }, }, 'BUY'), h('button', { @@ -254,7 +240,11 @@ AccountDetailScreen.prototype.subview = function () { AccountDetailScreen.prototype.tabSections = function () { const { currentAccountTab } = this.props - return h('section.tabSection', [ + return h('section.tabSection', { + style: { + height: '100%', + }, + }, [ h(TabBar, { tabs: [ @@ -305,7 +295,3 @@ AccountDetailScreen.prototype.transactionList = function () { }, }) } - -AccountDetailScreen.prototype.requestAccountExport = function () { - this.props.dispatch(actions.requestExportAccount()) -} diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js deleted file mode 100644 index 10a0b6cc7..000000000 --- a/ui/app/accounts/account-list-item.js +++ /dev/null @@ -1,91 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits -const ethUtil = require('ethereumjs-util') - -const EthBalance = require('../components/eth-balance') -const CopyButton = require('../components/copyButton') -const Identicon = require('../components/identicon') - -module.exports = AccountListItem - -inherits(AccountListItem, Component) -function AccountListItem () { - Component.call(this) -} - -AccountListItem.prototype.render = function () { - const { identity, selectedAddress, accounts, onShowDetail, - conversionRate, currentCurrency } = this.props - - const checksumAddress = identity && identity.address && ethUtil.toChecksumAddress(identity.address) - const isSelected = selectedAddress === identity.address - const account = accounts[identity.address] - const selectedClass = isSelected ? '.selected' : '' - - return ( - h(`.accounts-list-option.flex-row.flex-space-between.pointer.hover-white${selectedClass}`, { - key: `account-panel-${identity.address}`, - onClick: (event) => onShowDetail(identity.address, event), - }, [ - - h('.identicon-wrapper.flex-column.flex-center.select-none', [ - this.pendingOrNot(), - this.indicateIfLoose(), - h(Identicon, { - address: identity.address, - imageify: true, - }), - ]), - - // account address, balance - h('.identity-data.flex-column.flex-justify-center.flex-grow.select-none', { - style: { - width: '200px', - }, - }, [ - h('span', identity.name), - h('span.font-small', { - style: { - overflow: 'hidden', - textOverflow: 'ellipsis', - }, - }, checksumAddress), - h(EthBalance, { - value: account && account.balance, - currentCurrency, - conversionRate, - style: { - lineHeight: '7px', - marginTop: '10px', - }, - }), - ]), - - // copy button - h('.identity-copy.flex-column', { - style: { - margin: '0 20px', - }, - }, [ - h(CopyButton, { - value: checksumAddress, - }), - ]), - ]) - ) -} - -AccountListItem.prototype.indicateIfLoose = function () { - try { // Sometimes keyrings aren't loaded yet: - const type = this.props.keyring.type - const isLoose = type !== 'HD Key Tree' - return isLoose ? h('.keyring-label', 'LOOSE') : null - } catch (e) { return } -} - -AccountListItem.prototype.pendingOrNot = function () { - const pending = this.props.pending - if (pending.length === 0) return null - return h('.pending-dot', pending.length) -} diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js deleted file mode 100644 index ac2615cd7..000000000 --- a/ui/app/accounts/index.js +++ /dev/null @@ -1,164 +0,0 @@ -const inherits = require('util').inherits -const Component = require('react').Component -const h = require('react-hyperscript') -const connect = require('react-redux').connect -const actions = require('../actions') -const valuesFor = require('../util').valuesFor -const findDOMNode = require('react-dom').findDOMNode -const AccountListItem = require('./account-list-item') - -module.exports = connect(mapStateToProps)(AccountsScreen) - -function mapStateToProps (state) { - const pendingTxs = valuesFor(state.metamask.unapprovedTxs) - .filter(txMeta => txMeta.metamaskNetworkId === state.metamask.network) - const pendingMsgs = valuesFor(state.metamask.unapprovedMsgs) - const pending = pendingTxs.concat(pendingMsgs) - - return { - accounts: state.metamask.accounts, - identities: state.metamask.identities, - unapprovedTxs: state.metamask.unapprovedTxs, - selectedAddress: state.metamask.selectedAddress, - scrollToBottom: state.appState.scrollToBottom, - pending, - keyrings: state.metamask.keyrings, - conversionRate: state.metamask.conversionRate, - currentCurrency: state.metamask.currentCurrency, - } -} - -inherits(AccountsScreen, Component) -function AccountsScreen () { - Component.call(this) -} - -AccountsScreen.prototype.render = function () { - const props = this.props - const { keyrings, conversionRate, currentCurrency } = props - const identityList = valuesFor(props.identities) - const unapprovedTxList = valuesFor(props.unapprovedTxs) - - return ( - - h('.accounts-section.flex-grow', [ - - // subtitle and nav - h('.section-title.flex-center', [ - h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { - onClick: this.goHome.bind(this), - }), - h('h2.page-subtitle', 'Select Account'), - ]), - - h('hr.horizontal-line'), - - // identity selection - h('section.identity-section', { - style: { - height: '418px', - overflowY: 'auto', - overflowX: 'hidden', - }, - }, - [ - identityList.map((identity) => { - const pending = this.props.pending.filter((txOrMsg) => { - if ('txParams' in txOrMsg) { - return txOrMsg.txParams.from === identity.address - } else if ('msgParams' in txOrMsg) { - return txOrMsg.msgParams.from === identity.address - } else { - return false - } - }) - - const simpleAddress = identity.address.substring(2).toLowerCase() - const keyring = keyrings.find((kr) => { - return kr.accounts.includes(simpleAddress) || - kr.accounts.includes(identity.address) - }) - - return h(AccountListItem, { - key: `acct-panel-${identity.address}`, - identity, - selectedAddress: this.props.selectedAddress, - conversionRate, - currentCurrency, - accounts: this.props.accounts, - onShowDetail: this.onShowDetail.bind(this), - pending, - keyring, - }) - }), - - h('hr.horizontal-line'), - h('div.footer.hover-white.pointer', { - key: 'reveal-account-bar', - onClick: () => { - this.addNewAccount() - }, - style: { - display: 'flex', - height: '40px', - padding: '10px', - justifyContent: 'center', - alignItems: 'center', - }, - }, [ - h('i.fa.fa-plus.fa-lg', {key: ''}), - ]), - h('hr.horizontal-line'), - ]), - - unapprovedTxList.length ? ( - - h('.unconftx-link.flex-row.flex-center', { - onClick: this.navigateToConfTx.bind(this), - }, [ - h('span', 'Unconfirmed Txs'), - h('i.fa.fa-arrow-right.fa-lg'), - ]) - - ) : ( - null - ), - ]) - ) -} - -// If a new account was revealed, scroll to the bottom -AccountsScreen.prototype.componentDidUpdate = function () { - const scrollToBottom = this.props.scrollToBottom - - if (scrollToBottom) { - var container = findDOMNode(this) - var scrollable = container.querySelector('.identity-section') - scrollable.scrollTop = scrollable.scrollHeight - } -} - -AccountsScreen.prototype.navigateToConfTx = function () { - event.stopPropagation() - this.props.dispatch(actions.showConfTxPage()) -} - -AccountsScreen.prototype.onShowDetail = function (address, event) { - event.stopPropagation() - this.props.dispatch(actions.showAccountDetail(address)) -} - -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/add-token.js b/ui/app/add-token.js index 15ef7a852..b303b5c0d 100644 --- a/ui/app/add-token.js +++ b/ui/app/add-token.js @@ -86,7 +86,7 @@ AddTokenScreen.prototype.render = function () { h('div', [ h('span', { style: { fontWeight: 'bold', paddingRight: '10px'}, - }, 'Token Symbol'), + }, 'Token Sybmol'), ]), h('div', { style: {display: 'flex'} }, [ diff --git a/ui/app/app.js b/ui/app/app.js index 1a63002e1..d1a20f079 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -10,7 +10,6 @@ const NewKeyChainScreen = require('./new-keychain') // unlock const UnlockScreen = require('./unlock') // accounts -const AccountsScreen = require('./accounts') const AccountDetailScreen = require('./account-detail') const SendTransactionScreen = require('./send') const ConfirmTxScreen = require('./conf-tx') @@ -24,10 +23,9 @@ const Import = require('./accounts/import') const InfoScreen = require('./info') const Loading = require('./components/loading') const SandwichExpando = require('sandwich-expando') -const MenuDroppo = require('menu-droppo') -const DropMenuItem = require('./components/drop-menu-item') +const Dropdown = require('./components/dropdown').Dropdown +const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem const NetworkIndicator = require('./components/network') -const Tooltip = require('./components/tooltip') const BuyView = require('./components/buy-button-subview') const QrView = require('./components/qr-code') const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete') @@ -79,6 +77,8 @@ App.prototype.render = function () { // Windows was showing a vertical scroll bar: overflow: 'hidden', position: 'relative', + height: '100%', + alignItems: 'center', }, }, [ @@ -95,8 +95,8 @@ App.prototype.render = function () { // panel content h('.app-primary.flex-grow' + (transForward ? '.from-right' : '.from-left'), { style: { - height: '380px', - width: '360px', + height: '100%', + maxWidth: '850px', }, }, [ h(ReactCSSTransitionGroup, { @@ -123,14 +123,18 @@ App.prototype.renderAppBar = function () { return ( - h('div', [ + h('div', { + style: { + width: '100%' + }, + }, [ h('.app-header.flex-row.flex-space-between', { style: { alignItems: 'center', visibility: props.isUnlocked ? 'visible' : 'none', background: props.isUnlocked ? 'white' : 'none', - height: '36px', + height: '38px', position: 'relative', zIndex: 12, }, @@ -178,21 +182,6 @@ App.prototype.renderAppBar = function () { }, }, [ - // small accounts nav - props.isUnlocked && h(Tooltip, { title: 'Switch Accounts' }, [ - h('img.cursor-pointer.color-orange', { - src: 'images/switch_acc.svg', - style: { - width: '23.5px', - marginRight: '8px', - }, - onClick: (event) => { - event.stopPropagation() - this.props.dispatch(actions.showAccountsPage()) - }, - }), - ]), - // hamburger props.isUnlocked && h(SandwichExpando, { width: 16, @@ -214,11 +203,12 @@ App.prototype.renderAppBar = function () { App.prototype.renderNetworkDropdown = function () { const props = this.props + const { provider: { type: providerType, rpcTarget: activeNetwork } } = props const rpcList = props.frequentRpcList const state = this.state || {} const isOpen = state.isNetworkMenuOpen - return h(MenuDroppo, { + return h(Dropdown, { isOpen, onClickOutside: (event) => { this.setState({ isNetworkMenuOpen: !isOpen }) @@ -226,72 +216,92 @@ App.prototype.renderNetworkDropdown = function () { zIndex: 11, style: { position: 'absolute', - left: 0, + left: '2px', top: '36px', }, - innerStyle: { - background: 'white', - boxShadow: '1px 1px 2px rgba(0,0,0,0.1)', - }, - }, [ // DROP MENU ITEMS - h('style', ` - .drop-menu-item:hover { background:rgb(235, 235, 235); } - .drop-menu-item i { margin: 11px; } - `), - - h(DropMenuItem, { - label: 'Main Ethereum Network', - closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => props.dispatch(actions.setProviderType('mainnet')), - icon: h('.menu-icon.diamond'), - activeNetworkRender: props.network, - provider: props.provider, - }), - - h(DropMenuItem, { - label: 'Ropsten Test Network', - closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => props.dispatch(actions.setProviderType('ropsten')), - icon: h('.menu-icon.red-dot'), - activeNetworkRender: props.network, - provider: props.provider, - }), - - h(DropMenuItem, { - label: 'Kovan Test Network', - closeMenu: () => this.setState({ isNetworkMenuOpen: false}), - action: () => props.dispatch(actions.setProviderType('kovan')), - icon: h('.menu-icon.hollow-diamond'), - activeNetworkRender: props.network, - provider: props.provider, - }), - - h(DropMenuItem, { - label: 'Rinkeby Test Network', - closeMenu: () => this.setState({ isNetworkMenuOpen: false}), - action: () => props.dispatch(actions.setProviderType('rinkeby')), - icon: h('.menu-icon.golden-square'), - activeNetworkRender: props.network, - provider: props.provider, - }), - - h(DropMenuItem, { - label: 'Localhost 8545', - closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => props.dispatch(actions.setDefaultRpcTarget(rpcList)), - icon: h('i.fa.fa-question-circle.fa-lg'), - activeNetworkRender: props.provider.rpcTarget, - }), + innerStyle: {}, + }, [ + + h( + DropdownMenuItem, + { + closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }), + onClick: () => props.dispatch(actions.setProviderType('mainnet')), + }, + [ + h('.menu-icon.diamond'), + 'Main Ethereum Network', + providerType === 'mainnet' ? h('.check', '✓') : null, + ] + ), + + h( + DropdownMenuItem, + { + closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }), + onClick: () => props.dispatch(actions.setProviderType('ropsten')), + }, + [ + h('.menu-icon.red-dot'), + 'Ropsten Test Network', + providerType === 'ropsten' ? h('.check', '✓') : null, + ] + ), + + h( + DropdownMenuItem, + { + closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }), + onClick: () => props.dispatch(actions.setProviderType('kovan')), + }, + [ + h('.menu-icon.hollow-diamond'), + 'Kovan Test Network', + providerType === 'kovan' ? h('.check', '✓') : null, + ] + ), + + h( + DropdownMenuItem, + { + closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }), + onClick: () => props.dispatch(actions.setProviderType('rinkeby')), + }, + [ + h('.menu-icon.golden-square'), + 'Rinkeby Test Network', + providerType === 'rinkeby' ? h('.check', '✓') : null, + ] + ), + + h( + DropdownMenuItem, + { + closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }), + onClick: () => props.dispatch(actions.setDefaultRpcTarget(rpcList)), + }, + [ + h('i.fa.fa-question-circle.fa-lg.menu-icon'), + 'Localhost 8545', + activeNetwork === 'http://localhost:8545' ? h('.check', '✓') : null, + ] + ), this.renderCustomOption(props.provider), this.renderCommonRpc(rpcList, props.provider), - h(DropMenuItem, { - label: 'Custom RPC', - closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => this.props.dispatch(actions.showConfigPage()), - icon: h('i.fa.fa-question-circle.fa-lg'), - }), + h( + DropdownMenuItem, + { + closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }), + onClick: () => this.props.dispatch(actions.showConfigPage()), + }, + [ + h('i.fa.fa-question-circle.fa-lg.menu-icon'), + 'Custom RPC', + activeNetwork === 'custom' ? h('.check', '✓') : null, + ] + ), ]) } @@ -300,7 +310,7 @@ App.prototype.renderDropdown = function () { const state = this.state || {} const isOpen = state.isMainMenuOpen - return h(MenuDroppo, { + return h(Dropdown, { isOpen: isOpen, zIndex: 11, onClickOutside: (event) => { @@ -308,46 +318,30 @@ App.prototype.renderDropdown = function () { }, style: { position: 'absolute', - right: 0, - top: '36px', + right: '2px', + top: '38px', }, - innerStyle: { - background: 'white', - boxShadow: '1px 1px 2px rgba(0,0,0,0.1)', - }, - }, [ // DROP MENU ITEMS - h('style', ` - .drop-menu-item:hover { background:rgb(235, 235, 235); } - .drop-menu-item i { margin: 11px; } - `), - - h(DropMenuItem, { - label: 'Settings', + innerStyle: {}, + }, [ + h(DropdownMenuItem, { closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), - action: () => this.props.dispatch(actions.showConfigPage()), - icon: h('i.fa.fa-gear.fa-lg'), - }), + onClick: () => { this.props.dispatch(actions.showConfigPage()) }, + }, 'Settings'), - h(DropMenuItem, { - label: 'Import Account', + h(DropdownMenuItem, { closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), - action: () => this.props.dispatch(actions.showImportPage()), - icon: h('i.fa.fa-arrow-circle-o-up.fa-lg'), - }), + onClick: () => { this.props.dispatch(actions.showImportPage()) }, + }, 'Import Account'), - h(DropMenuItem, { - label: 'Lock', + h(DropdownMenuItem, { closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), - action: () => this.props.dispatch(actions.lockMetamask()), - icon: h('i.fa.fa-lock.fa-lg'), - }), + onClick: () => { this.props.dispatch(actions.lockMetamask()) }, + }, 'Lock'), - h(DropMenuItem, { - label: 'Info/Help', + h(DropdownMenuItem, { closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), - action: () => this.props.dispatch(actions.showInfoPage()), - icon: h('i.fa.fa-question.fa-lg'), - }), + onClick: () => { this.props.dispatch(actions.showInfoPage()) }, + }, 'Info/Help'), ]) } @@ -433,10 +427,6 @@ App.prototype.renderPrimary = function () { // show current view switch (props.currentView.name) { - case 'accounts': - log.debug('rendering accounts screen') - return h(AccountsScreen, {key: 'accounts'}) - case 'accountDetail': log.debug('rendering account detail screen') return h(AccountDetailScreen, {key: 'account-detail'}) @@ -539,13 +529,18 @@ App.prototype.renderCustomOption = function (provider) { return null default: - return h(DropMenuItem, { - label, - key: rpcTarget, - closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - icon: h('i.fa.fa-question-circle.fa-lg'), - activeNetworkRender: 'custom', - }) + return h( + DropdownMenuItem, + { + key: rpcTarget, + closeMenu: () => this.setState({ isNetworkMenuOpen: false }), + }, + [ + h('i.fa.fa-question-circle.fa-lg.menu-icon'), + label, + h('.check', '✓'), + ] + ) } } @@ -578,14 +573,19 @@ App.prototype.renderCommonRpc = function (rpcList, provider) { if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) { return null } else { - return h(DropMenuItem, { - label: rpc, - key: rpc, - closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => props.dispatch(actions.setRpcTarget(rpc)), - icon: h('i.fa.fa-question-circle.fa-lg'), - activeNetworkRender: rpc, - }) + return h( + DropdownMenuItem, + { + key: rpc, + closeMenu: () => this.setState({ isNetworkMenuOpen: false }), + action: () => props.dispatch(actions.setRpcTarget(rpc)), + }, + [ + h('i.fa.fa-question-circle.fa-lg.menu-icon'), + rpc, + h('.check', '✓'), + ] + ) } }) } diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js new file mode 100644 index 000000000..d1d319477 --- /dev/null +++ b/ui/app/components/account-dropdowns.js @@ -0,0 +1,227 @@ +const Component = require('react').Component +const PropTypes = require('react').PropTypes +const h = require('react-hyperscript') +const actions = require('../actions') +const genAccountLink = require('../../lib/account-link.js') +const connect = require('react-redux').connect +const Dropdown = require('./dropdown').Dropdown +const DropdownMenuItem = require('./dropdown').DropdownMenuItem +const Identicon = require('./identicon') +const ethUtil = require('ethereumjs-util') +const copyToClipboard = require('copy-to-clipboard') + +class AccountDropdowns extends Component { + constructor (props) { + super(props) + this.state = { + accountSelectorActive: false, + optionsMenuActive: false, + } + } + + renderAccounts () { + const { identities, selected } = this.props + + return Object.keys(identities).map((key) => { + const identity = identities[key] + const isSelected = identity.address === selected + + return h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + this.props.actions.showAccountDetail(identity.address) + }, + }, + [ + h( + Identicon, + { + address: identity.address, + diameter: 16, + }, + ), + h('span', { style: { marginLeft: '10px' } }, identity.name || ''), + h('span', { style: { marginLeft: '10px' } }, isSelected ? h('.check', '✓') : null), + ] + ) + }) + } + + renderAccountSelector () { + const { actions } = this.props + const { accountSelectorActive } = this.state + + return h( + Dropdown, + { + style: { + marginLeft: '-125px', + minWidth: '180px', + }, + isOpen: accountSelectorActive, + onClickOutside: () => { this.setState({ accountSelectorActive: false }) }, + }, + [ + ...this.renderAccounts(), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => actions.addNewAccount(), + }, + [ + h( + Identicon, + { + diameter: 16, + }, + ), + h('span', { style: { marginLeft: '10px' } }, 'Create Account'), + ], + ), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => actions.showImportPage(), + }, + [ + h( + Identicon, + { + diameter: 16, + }, + ), + h('span', { style: { marginLeft: '10px' } }, 'Import Account'), + ] + ), + ] + ) + } + + renderAccountOptions () { + const { actions } = this.props + const { optionsMenuActive } = this.state + + return h( + Dropdown, + { + style: { + marginLeft: '-162px', + minWidth: '180px', + }, + isOpen: optionsMenuActive, + onClickOutside: () => { this.setState({ optionsMenuActive: false }) }, + }, + [ + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => actions.showConfigPage(), + }, + 'Account Settings', + ), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + const { selected, network } = this.props + const url = genAccountLink(selected, network) + global.platform.openWindow({ url }) + }, + }, + 'View account on Etherscan', + ), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + const { selected } = this.props + const checkSumAddress = selected && ethUtil.toChecksumAddress(selected) + copyToClipboard(checkSumAddress) + }, + }, + 'Copy Address to clipboard', + ), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + actions.requestAccountExport() + }, + }, + 'Export Private Key', + ), + ] + ) + } + + render () { + const { style } = this.props + const { optionsMenuActive, accountSelectorActive } = this.state + + return h( + 'span', + { + style: style, + }, + [ + h( + 'i.fa.fa-angle-down', + { + style: {}, + onClick: (event) => { + event.stopPropagation() + this.setState({ + accountSelectorActive: !accountSelectorActive, + optionsMenuActive: false, + }) + }, + }, + this.renderAccountSelector(), + ), + h( + 'i.fa.fa-ellipsis-h', + { + style: { 'marginLeft': '10px'}, + onClick: (event) => { + event.stopPropagation() + this.setState({ + accountSelectorActive: false, + optionsMenuActive: !optionsMenuActive, + }) + }, + }, + this.renderAccountOptions() + ), + ] + ) + } +} + +AccountDropdowns.propTypes = { + identities: PropTypes.objectOf(PropTypes.object), + selected: PropTypes.string, +} + +const mapDispatchToProps = (dispatch) => { + return { + actions: { + showConfigPage: () => dispatch(actions.showConfigPage()), + requestAccountExport: () => dispatch(actions.requestExportAccount()), + showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)), + addNewAccount: () => dispatch(actions.addNewAccount()), + showImportPage: () => dispatch(actions.showImportPage()), + }, + } +} + +module.exports = { + AccountDropdowns: connect(null, mapDispatchToProps)(AccountDropdowns), +} diff --git a/ui/app/components/account-info-link.js b/ui/app/components/account-info-link.js deleted file mode 100644 index 6526ab502..000000000 --- a/ui/app/components/account-info-link.js +++ /dev/null @@ -1,41 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits -const Tooltip = require('./tooltip') -const genAccountLink = require('../../lib/account-link') - -module.exports = AccountInfoLink - -inherits(AccountInfoLink, Component) -function AccountInfoLink () { - Component.call(this) -} - -AccountInfoLink.prototype.render = function () { - const { selected, network } = this.props - const title = 'View account on Etherscan' - const url = genAccountLink(selected, network) - - if (!url) { - return null - } - - return h('.account-info-link', { - style: { - display: 'flex', - alignItems: 'center', - }, - }, [ - - h(Tooltip, { - title, - }, [ - h('i.fa.fa-info-circle.cursor-pointer.color-orange', { - style: { - margin: '5px', - }, - onClick () { global.platform.openWindow({ url }) }, - }), - ]), - ]) -} diff --git a/ui/app/components/drop-menu-item.js b/ui/app/components/drop-menu-item.js deleted file mode 100644 index e42948209..000000000 --- a/ui/app/components/drop-menu-item.js +++ /dev/null @@ -1,59 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits - -module.exports = DropMenuItem - -inherits(DropMenuItem, Component) -function DropMenuItem () { - Component.call(this) -} - -DropMenuItem.prototype.render = function () { - return h('li.drop-menu-item', { - onClick: () => { - this.props.closeMenu() - this.props.action() - }, - style: { - listStyle: 'none', - padding: '6px 16px 6px 5px', - fontFamily: 'Montserrat Regular', - color: 'rgb(125, 128, 130)', - cursor: 'pointer', - display: 'flex', - justifyContent: 'flex-start', - }, - }, [ - this.props.icon, - this.props.label, - this.activeNetworkRender(), - ]) -} - -DropMenuItem.prototype.activeNetworkRender = function () { - const activeNetwork = this.props.activeNetworkRender - const { provider } = this.props - const providerType = provider ? provider.type : null - if (activeNetwork === undefined) return - - switch (this.props.label) { - case 'Main Ethereum Network': - if (providerType === 'mainnet') return h('.check', '✓') - break - case 'Ropsten Test Network': - if (providerType === 'ropsten') return h('.check', '✓') - break - case 'Kovan Test Network': - if (providerType === 'kovan') return h('.check', '✓') - break - case 'Rinkeby Test Network': - if (providerType === 'rinkeby') return h('.check', '✓') - break - case 'Localhost 8545': - if (activeNetwork === 'http://localhost:8545') return h('.check', '✓') - break - default: - if (activeNetwork === 'custom') return h('.check', '✓') - } -} diff --git a/ui/app/components/dropdown.js b/ui/app/components/dropdown.js new file mode 100644 index 000000000..e77b4c40c --- /dev/null +++ b/ui/app/components/dropdown.js @@ -0,0 +1,89 @@ +const Component = require('react').Component +const PropTypes = require('react').PropTypes +const h = require('react-hyperscript') +const MenuDroppo = require('menu-droppo') + +const noop = () => {} + +class Dropdown extends Component { + render () { + const { isOpen, onClickOutside, style, children } = this.props + + return h( + MenuDroppo, + { + isOpen, + zIndex: 11, + onClickOutside, + style, + innerStyle: { + borderRadius: '4px', + padding: '8px 16px', + background: 'rgba(0, 0, 0, 0.8)', + boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', + }, + }, + [ + h( + 'style', + ` + li.dropdown-menu-item:hover { color:rgb(225, 225, 225); } + li.dropdown-menu-item { color: rgb(185, 185, 185); } + ` + ), + ...children, + ] + ) + } +} + +Dropdown.defaultProps = { + isOpen: false, + onClick: noop, +} + +Dropdown.propTypes = { + isOpen: PropTypes.bool.isRequired, + onClick: PropTypes.func.isRequired, + children: PropTypes.node, + style: PropTypes.object.isRequired, +} + +class DropdownMenuItem extends Component { + render () { + const { onClick, closeMenu, children } = this.props + + return h( + 'li.dropdown-menu-item', + { + onClick: () => { + onClick() + closeMenu() + }, + style: { + listStyle: 'none', + padding: '8px 0px 8px 0px', + fontSize: '12px', + fontStyle: 'normal', + fontFamily: 'Montserrat Regular', + cursor: 'pointer', + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + }, + }, + children + ) + } +} + +DropdownMenuItem.propTypes = { + closeMenu: PropTypes.func.isRequired, + onClick: PropTypes.func.isRequired, + children: PropTypes.node, +} + +module.exports = { + Dropdown, + DropdownMenuItem, +} diff --git a/ui/app/components/editable-label.js b/ui/app/components/editable-label.js index 41936f5e0..167be7eaf 100644 --- a/ui/app/components/editable-label.js +++ b/ui/app/components/editable-label.js @@ -30,7 +30,12 @@ EditableLabel.prototype.render = function () { } else { return h('div.name-label', { onClick: (event) => { - this.setState({ isEditingLabel: true }) + const nameAttribute = event.target.getAttribute('name') + // checks for class to handle smaller CTA above the account name + const classAttribute = event.target.getAttribute('class') + if (nameAttribute === 'edit' || classAttribute === 'edit-text') { + this.setState({ isEditingLabel: true }) + } }, }, this.props.children) } diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index 5324ccd64..d7d602f31 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -15,7 +15,7 @@ const addressSummary = util.addressSummary const nameForAddress = require('../../lib/contract-namer') const BNInput = require('./bn-as-decimal-input') -const MIN_GAS_PRICE_GWEI_BN = new BN(1) +const MIN_GAS_PRICE_GWEI_BN = new BN(2) const GWEI_FACTOR = new BN(1e9) const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR) const MIN_GAS_LIMIT_BN = new BN(21000) diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js index 3b4ba741e..ae6aaec8c 100644 --- a/ui/app/components/transaction-list.js +++ b/ui/app/components/transaction-list.js @@ -24,7 +24,11 @@ TransactionList.prototype.render = function () { return ( - h('section.transaction-list', [ + h('section.transaction-list', { + style: { + height: '100%', + }, + }, [ h('style', ` .transaction-list .transaction-list-item:not(:last-of-type) { @@ -39,7 +43,7 @@ TransactionList.prototype.render = function () { h('.tx-list', { style: { overflowY: 'auto', - height: '300px', + height: '100%', padding: '0 20px', textAlign: 'center', }, diff --git a/ui/app/css/index.css b/ui/app/css/index.css index 808aafb4c..2ae92bbd6 100644 --- a/ui/app/css/index.css +++ b/ui/app/css/index.css @@ -19,6 +19,15 @@ html, body { font-weight: 300; line-height: 1.4em; background: #F7F7F7; + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} + +.css-transition-group { + flex: 1; + height: 100%; } input:focus, textarea:focus { @@ -28,8 +37,7 @@ input:focus, textarea:focus { #app-content { overflow-x: hidden; min-width: 357px; - width: 360px; - height: 500px; + height: 100%; } button, input[type="submit"] { @@ -403,7 +411,8 @@ input.large-input { /* account detail screen */ .account-detail-section { - + display: flex; + flex-wrap: wrap; } .name-label{ diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css index 910a24ee2..b0ca958a2 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -232,6 +232,10 @@ hr.horizontal-line { align-items: center; } +.tabSection { + min-width: 350px; +} + .menu-icon { display: inline-block; height: 9px; diff --git a/ui/app/info.js b/ui/app/info.js index cb2e41f5b..e8470de97 100644 --- a/ui/app/info.js +++ b/ui/app/info.js @@ -97,17 +97,11 @@ InfoScreen.prototype.render = function () { paddingLeft: '30px', }}, [ - h('div.fa.fa-support', [ - h('a.info', { - href: 'http://metamask.consensyssupport.happyfox.com', - target: '_blank', - }, 'Visit our Support Center'), - ]), h('div.fa.fa-github', [ h('a.info', { - href: 'https://github.com/MetaMask/metamask-extension/issues/new', + href: 'https://github.com/MetaMask/faq', target: '_blank', - }, 'Found a bug? Report it!'), + }, 'Need Help? Read our FAQ!'), ]), h('div', [ h('a', { diff --git a/ui/app/keychains/hd/create-vault-complete.js b/ui/app/keychains/hd/create-vault-complete.js index a318a9b50..c32751fff 100644 --- a/ui/app/keychains/hd/create-vault-complete.js +++ b/ui/app/keychains/hd/create-vault-complete.js @@ -47,8 +47,6 @@ CreateVaultCompleteScreen.prototype.render = function () { h('div', { style: { - width: '360px', - height: '78px', fontSize: '1em', marginTop: '10px', textAlign: 'center', |