aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Serrano <kevin.serrano@consensys.net>2017-08-04 04:10:46 +0800
committerKevin Serrano <kevin.serrano@consensys.net>2017-08-04 04:10:46 +0800
commit81982d01c05b37b80bfb08f1f65a742555acbe0f (patch)
tree0220c6d61fd1c0b9a448ee226eecf98c717fac05
parent15bcbb4941efc66630e9abf1ff27370e759f753d (diff)
parent4115c25d8f2e186a575de7904a91b3717da5e800 (diff)
downloadtangerine-wallet-browser-81982d01c05b37b80bfb08f1f65a742555acbe0f.tar
tangerine-wallet-browser-81982d01c05b37b80bfb08f1f65a742555acbe0f.tar.gz
tangerine-wallet-browser-81982d01c05b37b80bfb08f1f65a742555acbe0f.tar.bz2
tangerine-wallet-browser-81982d01c05b37b80bfb08f1f65a742555acbe0f.tar.lz
tangerine-wallet-browser-81982d01c05b37b80bfb08f1f65a742555acbe0f.tar.xz
tangerine-wallet-browser-81982d01c05b37b80bfb08f1f65a742555acbe0f.tar.zst
tangerine-wallet-browser-81982d01c05b37b80bfb08f1f65a742555acbe0f.zip
Merge branch 'NewUI' of github.com:MetaMask/metamask-plugin into NewUI
-rw-r--r--CHANGELOG.md2
-rw-r--r--app/home.html12
-rw-r--r--app/popup.html5
-rw-r--r--package.json3
-rw-r--r--test/unit/responsive/components/dropdown-test.js115
-rw-r--r--ui/app/account-detail.js123
-rw-r--r--ui/app/accounts/account-list-item.js91
-rw-r--r--ui/app/accounts/index.js164
-rw-r--r--ui/app/actions.js4
-rw-r--r--ui/app/app.js309
-rw-r--r--ui/app/components/account-dropdowns.js235
-rw-r--r--ui/app/components/account-info-link.js41
-rw-r--r--ui/app/components/drop-menu-item.js59
-rw-r--r--ui/app/components/dropdown.js92
-rw-r--r--ui/app/components/editable-label.js7
-rw-r--r--ui/app/components/loading.js48
-rw-r--r--ui/app/components/network.js1
-rw-r--r--ui/app/components/shapeshift-form.js10
-rw-r--r--ui/app/components/tab-bar.js1
-rw-r--r--ui/app/components/token-list.js42
-rw-r--r--ui/app/components/transaction-list.js18
-rw-r--r--ui/app/conf-tx.js48
-rw-r--r--ui/app/css/index.css53
-rw-r--r--ui/app/css/lib.css5
-rw-r--r--ui/app/info.js26
-rw-r--r--ui/app/keychains/hd/create-vault-complete.js2
-rw-r--r--ui/app/keychains/hd/recover-seed/confirmation.js7
-rw-r--r--ui/lib/tx-helper.js1
28 files changed, 829 insertions, 695 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66c95a0c3..600df2955 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## Current Master
+- Replace account scren with an account drop-down menu.
+- Replace confusing buttons with an new account-specific drop-down menu.
- Continuously update blacklist for known phishing sites in background.
- Automatically detect suspicious URLs too similar to common phishing targets, and blacklist them.
diff --git a/app/home.html b/app/home.html
new file mode 100644
index 000000000..cfb4b00a0
--- /dev/null
+++ b/app/home.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no">
+ <title>MetaMask Plugin</title>
+ </head>
+ <body>
+ <div id="app-content"></div>
+ <script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script>
+ </body>
+</html>
diff --git a/app/popup.html b/app/popup.html
index 6d85a9811..d09b09315 100644
--- a/app/popup.html
+++ b/app/popup.html
@@ -2,10 +2,11 @@
<html>
<head>
<meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no">
<title>MetaMask Plugin</title>
</head>
- <body>
+ <body style="width:357px; height:500px;">
<div id="app-content"></div>
<script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/package.json b/package.json
index a086af29d..7cd6c074a 100644
--- a/package.json
+++ b/package.json
@@ -91,7 +91,7 @@
"inject-css": "^0.1.1",
"jazzicon": "^1.2.0",
"loglevel": "^1.4.1",
- "menu-droppo": "^1.1.0",
+ "menu-droppo": "2.0.1",
"metamask-logo": "^2.1.2",
"mississippi": "^1.2.0",
"mkdirp": "^0.5.1",
@@ -109,7 +109,6 @@
"pumpify": "^1.3.4",
"qrcode-npm": "0.0.3",
"react": "^15.0.2",
- "react-addons-css-transition-group": "^15.0.2",
"react-dom": "^15.5.4",
"react-hyperscript": "^2.2.2",
"react-markdown": "^2.3.0",
diff --git a/test/unit/responsive/components/dropdown-test.js b/test/unit/responsive/components/dropdown-test.js
new file mode 100644
index 000000000..3ad2c390e
--- /dev/null
+++ b/test/unit/responsive/components/dropdown-test.js
@@ -0,0 +1,115 @@
+var assert = require('assert');
+
+const additions = require('react-testutils-additions');
+const h = require('react-hyperscript');
+const ReactTestUtils = require('react-addons-test-utils');
+const sinon = require('sinon');
+const path = require('path');
+const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdown.js')).Dropdown;
+const DropdownMenuItem = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdown.js')).DropdownMenuItem;
+
+describe('Dropdown components', function () {
+ let onClickOutside;
+ let closeMenu;
+ let onClick;
+
+ let dropdownComponentProps;
+ const renderer = ReactTestUtils.createRenderer()
+ beforeEach(function () {
+ onClickOutside = sinon.spy();
+ closeMenu = sinon.spy();
+ onClick = sinon.spy();
+
+ dropdownComponentProps = {
+ isOpen: true,
+ zIndex: 11,
+ onClickOutside,
+ style: {
+ position: 'absolute',
+ right: 0,
+ top: '36px',
+ },
+ innerStyle: {},
+ }
+ });
+
+ it('can render two items', function () {
+ const dropdownComponent = h(
+ Dropdown,
+ dropdownComponentProps,
+ [
+ h('style', `
+ .drop-menu-item:hover { background:rgb(235, 235, 235); }
+ .drop-menu-item i { margin: 11px; }
+ `),
+ h(DropdownMenuItem, {
+ closeMenu,
+ onClick,
+ }, 'Item 1'),
+ h(DropdownMenuItem, {
+ closeMenu,
+ onClick,
+ }, 'Item 2'),
+ ]
+ )
+
+ const component = additions.renderIntoDocument(dropdownComponent);
+ renderer.render(dropdownComponent);
+ const items = additions.find(component, 'li');
+ assert.equal(items.length, 2);
+ });
+
+ it('closes when item clicked', function() {
+ const dropdownComponent = h(
+ Dropdown,
+ dropdownComponentProps,
+ [
+ h('style', `
+ .drop-menu-item:hover { background:rgb(235, 235, 235); }
+ .drop-menu-item i { margin: 11px; }
+ `),
+ h(DropdownMenuItem, {
+ closeMenu,
+ onClick,
+ }, 'Item 1'),
+ h(DropdownMenuItem, {
+ closeMenu,
+ onClick,
+ }, 'Item 2'),
+ ]
+ )
+ const component = additions.renderIntoDocument(dropdownComponent);
+ renderer.render(dropdownComponent);
+ const items = additions.find(component, 'li');
+ const node = items[0];
+ ReactTestUtils.Simulate.click(node);
+ assert.equal(closeMenu.calledOnce, true);
+ });
+
+ it('invokes click handler when item clicked', function() {
+ const dropdownComponent = h(
+ Dropdown,
+ dropdownComponentProps,
+ [
+ h('style', `
+ .drop-menu-item:hover { background:rgb(235, 235, 235); }
+ .drop-menu-item i { margin: 11px; }
+ `),
+ h(DropdownMenuItem, {
+ closeMenu,
+ onClick,
+ }, 'Item 1'),
+ h(DropdownMenuItem, {
+ closeMenu,
+ onClick,
+ }, 'Item 2'),
+ ]
+ )
+ const component = additions.renderIntoDocument(dropdownComponent);
+ renderer.render(dropdownComponent);
+ const items = additions.find(component, 'li');
+ const node = items[0];
+ ReactTestUtils.Simulate.click(node);
+ assert.equal(onClick.calledOnce, true);
+ });
+});
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
index bed05a7fb..24561c32e 100644
--- a/ui/app/account-detail.js
+++ b/ui/app/account-detail.js
@@ -3,21 +3,17 @@ 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 +50,13 @@ AccountDetailScreen.prototype.render = function () {
return (
- h('.account-detail-section', [
+ h('.account-detail-section.full-flex-height', [
- // identicon, label, balance, etc
+ // identicon, label, balance, etc
h('.account-data-subsection', {
style: {
margin: '0 20px',
+ flex: '1 0 auto',
},
}, [
@@ -84,6 +81,7 @@ AccountDetailScreen.prototype.render = function () {
style: {
lineHeight: '10px',
marginLeft: '15px',
+ width: '100%',
},
}, [
h(EditableLabel, {
@@ -98,7 +96,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 +157,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 +180,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', {
@@ -219,14 +199,7 @@ AccountDetailScreen.prototype.render = function () {
]),
// subview (tx history, pk export confirm, buy eth warning)
- h(ReactCSSTransitionGroup, {
- className: 'css-transition-group',
- transitionName: 'main',
- transitionEnterTimeout: 300,
- transitionLeaveTimeout: 300,
- }, [
- this.subview(),
- ]),
+ this.subview(),
])
)
@@ -254,7 +227,7 @@ AccountDetailScreen.prototype.subview = function () {
AccountDetailScreen.prototype.tabSections = function () {
const { currentAccountTab } = this.props
- return h('section.tabSection', [
+ return h('section.tabSection.full-flex-height.grow-tenx', [
h(TabBar, {
tabs: [
@@ -305,7 +278,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/actions.js b/ui/app/actions.js
index d99291e46..0a9d347aa 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -706,7 +706,7 @@ function markAccountsFound () {
//
// default rpc target refers to localhost:8545 in this instance.
-function setDefaultRpcTarget (rpcList) {
+function setDefaultRpcTarget () {
log.debug(`background.setDefaultRpcTarget`)
return (dispatch) => {
background.setDefaultRpc((err, result) => {
@@ -719,7 +719,7 @@ function setDefaultRpcTarget (rpcList) {
}
function setRpcTarget (newRpc) {
- log.debug(`background.setRpcTarget`)
+ log.debug(`background.setRpcTarget: ${newRpc}`)
return (dispatch) => {
background.setCustomRpc(newRpc, (err, result) => {
if (err) {
diff --git a/ui/app/app.js b/ui/app/app.js
index 1a63002e1..297a2f621 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -3,14 +3,12 @@ const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('./actions')
-const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
// init
const InitializeMenuScreen = require('./first-time/init-menu')
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 +22,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')
@@ -69,16 +66,16 @@ App.prototype.render = function () {
const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
const loadMessage = loadingMessage || isLoadingNetwork ?
`Connecting to ${this.getNetworkName()}` : null
-
log.debug('Main ui render function')
return (
- h('.flex-column.flex-grow.full-height', {
+ h('.flex-column.full-height', {
style: {
// Windows was showing a vertical scroll bar:
overflow: 'hidden',
position: 'relative',
+ alignItems: 'center',
},
}, [
@@ -93,20 +90,12 @@ App.prototype.render = function () {
}),
// panel content
- h('.app-primary.flex-grow' + (transForward ? '.from-right' : '.from-left'), {
+ h('.app-primary' + (transForward ? '.from-right' : '.from-left'), {
style: {
- height: '380px',
- width: '360px',
+ maxWidth: '850px',
},
}, [
- h(ReactCSSTransitionGroup, {
- className: 'css-transition-group',
- transitionName: 'main',
- transitionEnterTimeout: 300,
- transitionLeaveTimeout: 300,
- }, [
- this.renderPrimary(),
- ]),
+ this.renderPrimary(),
]),
])
)
@@ -123,14 +112,16 @@ App.prototype.renderAppBar = function () {
return (
- h('div', [
+ h('.full-width', {
+ height: '38px',
+ }, [
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 +169,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,84 +190,122 @@ 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 })
+ const { classList } = event.target
+ const isNotToggleElement = [
+ classList.contains('menu-icon'),
+ classList.contains('network-name'),
+ classList.contains('network-indicator'),
+ ].filter(bool => bool).length === 0
+ // classes from three constituent nodes of the toggle element
+
+ if (isNotToggleElement) {
+ this.setState({ isNetworkMenuOpen: false })
+ }
},
zIndex: 11,
style: {
position: 'absolute',
- left: 0,
+ left: '2px',
top: '36px',
},
innerStyle: {
- background: 'white',
- boxShadow: '1px 1px 2px rgba(0,0,0,0.1)',
+ padding: '2px 16px 2px 0px',
},
- }, [ // 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,
- }),
+ }, [
+
+ h(
+ DropdownMenuItem,
+ {
+ key: 'main',
+ 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,
+ {
+ key: 'ropsten',
+ 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,
+ {
+ key: 'kovan',
+ 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,
+ {
+ key: 'rinkeby',
+ 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,
+ {
+ key: 'default',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => props.dispatch(actions.setDefaultRpcTarget()),
+ },
+ [
+ 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,54 +314,37 @@ App.prototype.renderDropdown = function () {
const state = this.state || {}
const isOpen = state.isMainMenuOpen
- return h(MenuDroppo, {
+ return h(Dropdown, {
isOpen: isOpen,
zIndex: 11,
onClickOutside: (event) => {
- this.setState({ isMainMenuOpen: !isOpen })
+ const { classList } = event.target
+ const isNotToggleElement = !classList.contains('sandwich-expando')
+ if (isNotToggleElement) {
+ this.setState({ isMainMenuOpen: false })
+ }
},
style: {
position: 'absolute',
- right: 0,
- top: '36px',
- },
- innerStyle: {
- background: 'white',
- boxShadow: '1px 1px 2px rgba(0,0,0,0.1)',
+ right: '2px',
+ top: '38px',
},
- }, [ // 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.lockMetamask()) },
+ }, 'Lock'),
- 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'),
- }),
-
- h(DropMenuItem, {
- label: 'Info/Help',
- 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 +430,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'})
@@ -525,6 +518,8 @@ App.prototype.toggleMetamaskActive = function () {
App.prototype.renderCustomOption = function (provider) {
const { rpcTarget, type } = provider
+ const props = this.props
+
if (type !== 'rpc') return null
// Concatenate long URLs
@@ -539,13 +534,19 @@ 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,
+ onClick: () => props.dispatch(actions.setRpcTarget(rpcTarget)),
+ closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
+ },
+ [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ label,
+ h('.check', '✓'),
+ ]
+ )
}
}
@@ -571,21 +572,27 @@ App.prototype.getNetworkName = function () {
}
App.prototype.renderCommonRpc = function (rpcList, provider) {
- const { rpcTarget } = provider
const props = this.props
+ const rpcTarget = provider.rpcTarget
return rpcList.map((rpc) => {
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: `common${rpc}`,
+ closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
+ onClick: () => props.dispatch(actions.setRpcTarget(rpc)),
+ },
+ [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ rpc,
+ rpcTarget === rpc ? h('.check', '✓') : null,
+ ]
+ )
}
})
}
diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js
new file mode 100644
index 000000000..4ef9a5c14
--- /dev/null
+++ b/ui/app/components/account-dropdowns.js
@@ -0,0 +1,235 @@
+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,
+ }
+ this.accountSelectorToggleClassName = 'fa-angle-down'
+ this.optionsMenuToggleClassName = 'fa-ellipsis-h'
+ }
+
+ 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',
+ overflowY: 'auto',
+ maxHeight: '300px',
+ },
+ isOpen: accountSelectorActive,
+ onClickOutside: (event) => {
+ const { classList } = event.target
+ const isNotToggleElement = !classList.contains(this.accountSelectorToggleClassName)
+ if (accountSelectorActive && isNotToggleElement) {
+ 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: () => {
+ const { classList } = event.target
+ const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName)
+ if (optionsMenuActive && isNotToggleElement) {
+ this.setState({ optionsMenuActive: false })
+ }
+ },
+ },
+ [
+ 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..759800fd6
--- /dev/null
+++ b/ui/app/components/dropdown.js
@@ -0,0 +1,92 @@
+const Component = require('react').Component
+const PropTypes = require('react').PropTypes
+const h = require('react-hyperscript')
+const MenuDroppo = require('menu-droppo')
+const extend = require('xtend')
+
+const noop = () => {}
+
+class Dropdown extends Component {
+ render () {
+ const { isOpen, onClickOutside, style, innerStyle, children } = this.props
+
+ const innerStyleDefaults = extend({
+ borderRadius: '4px',
+ padding: '8px 16px',
+ background: 'rgba(0, 0, 0, 0.8)',
+ boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px',
+ }, innerStyle)
+
+ return h(
+ MenuDroppo,
+ {
+ isOpen,
+ zIndex: 11,
+ onClickOutside,
+ style,
+ innerStyle: innerStyleDefaults,
+ },
+ [
+ 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/loading.js b/ui/app/components/loading.js
index 87d6f5d20..163792584 100644
--- a/ui/app/components/loading.js
+++ b/ui/app/components/loading.js
@@ -1,7 +1,6 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
inherits(LoadingIndicator, Component)
@@ -15,35 +14,28 @@ LoadingIndicator.prototype.render = function () {
const { isLoading, loadingMessage } = this.props
return (
- h(ReactCSSTransitionGroup, {
- className: 'css-transition-group',
- transitionName: 'loader',
- transitionEnterTimeout: 150,
- transitionLeaveTimeout: 150,
+ isLoading ? h('.full-flex-height', {
+ style: {
+ left: '0px',
+ zIndex: 10,
+ position: 'absolute',
+ flexDirection: 'column',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ height: '100%',
+ width: '100%',
+ background: 'rgba(255, 255, 255, 0.8)',
+ },
}, [
+ h('img', {
+ src: 'images/loading.svg',
+ }),
- isLoading ? h('div', {
- style: {
- zIndex: 10,
- position: 'absolute',
- flexDirection: 'column',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- height: '100%',
- width: '100%',
- background: 'rgba(255, 255, 255, 0.8)',
- },
- }, [
- h('img', {
- src: 'images/loading.svg',
- }),
-
- h('br'),
-
- showMessageIfAny(loadingMessage),
- ]) : null,
- ])
+ h('br'),
+
+ showMessageIfAny(loadingMessage),
+ ]) : null
)
}
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index d5d3e18cd..698a0bbb9 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -39,7 +39,6 @@ Network.prototype.render = function () {
}),
h('i.fa.fa-sort-desc'),
])
-
} else if (providerName === 'mainnet') {
hoverText = 'Main Ethereum Network'
iconName = 'ethereum-network'
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index e0a720426..76a265d63 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -2,7 +2,6 @@ const PersistentForm = require('../../lib/persistent-form')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
-const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const actions = require('../actions')
const Qr = require('./qr-code')
const isValidAddress = require('../util').isValidAddress
@@ -24,14 +23,7 @@ function ShapeshiftForm () {
}
ShapeshiftForm.prototype.render = function () {
- return h(ReactCSSTransitionGroup, {
- className: 'css-transition-group',
- transitionName: 'main',
- transitionEnterTimeout: 300,
- transitionLeaveTimeout: 300,
- }, [
- this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain(),
- ])
+ return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain()
}
ShapeshiftForm.prototype.renderMain = function () {
diff --git a/ui/app/components/tab-bar.js b/ui/app/components/tab-bar.js
index 6295e7dd9..bef444a48 100644
--- a/ui/app/components/tab-bar.js
+++ b/ui/app/components/tab-bar.js
@@ -21,6 +21,7 @@ TabBar.prototype.render = function () {
background: '#EBEBEB',
color: '#AEAEAE',
paddingTop: '4px',
+ minHeight: '30px',
},
}, tabs.map((tab) => {
const { key, content } = tab
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index 20cfa897e..5ea31ae8d 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -47,10 +47,11 @@ TokenList.prototype.render = function () {
return h(TokenCell, tokenData)
})
- return h('div', [
- h('ol', {
+ return h('.full-flex-height', [
+ this.renderTokenStatusBar(),
+
+ h('ol.full-flex-height.flex-column', {
style: {
- height: '260px',
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',
@@ -63,6 +64,7 @@ TokenList.prototype.render = function () {
flex-direction: row;
align-items: center;
padding: 10px;
+ min-height: 50px;
}
li.token-cell > h3 {
@@ -76,17 +78,37 @@ TokenList.prototype.render = function () {
`),
...tokenViews,
- tokenViews.length ? null : this.message('No Tokens Found.'),
+ h('.flex-grow'),
]),
- this.addTokenButtonElement(),
])
}
-TokenList.prototype.addTokenButtonElement = function () {
- return h('div', [
- h('div.footer.hover-white.pointer', {
+TokenList.prototype.renderTokenStatusBar = function () {
+ const { tokens } = this.state
+
+ let msg
+ if (tokens.length === 1) {
+ msg = `You own 1 token`
+ } else if (tokens.length === 1) {
+ msg = `You own ${tokens.length} tokens`
+ } else {
+ msg = `No tokens found`
+ }
+
+ return h('div', {
+ style: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ minHeight: '70px',
+ padding: '10px',
+ },
+ }, [
+ h('span', msg),
+ h('button', {
key: 'reveal-account-bar',
- onClick: () => {
+ onClick: (event) => {
+ event.preventDefault()
this.props.addToken()
},
style: {
@@ -97,7 +119,7 @@ TokenList.prototype.addTokenButtonElement = function () {
alignItems: 'center',
},
}, [
- h('i.fa.fa-plus.fa-lg'),
+ 'ADD TOKEN',
]),
])
}
diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js
index 3b4ba741e..69b72614c 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.full-flex-height', {
+ style: {
+ justifyContent: 'center',
+ },
+ }, [
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',
},
@@ -64,13 +68,17 @@ TransactionList.prototype.render = function () {
},
})
})
- : h('.flex-center', {
+ : h('.flex-center.full-flex-height', {
style: {
flexDirection: 'column',
- height: '100%',
+ justifyContent: 'center',
},
}, [
- 'No transaction history.',
+ h('p', {
+ style: {
+ marginTop: '50px',
+ },
+ }, 'No transaction history.'),
]),
]),
])
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index 747d3ce2b..34727ff78 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -1,6 +1,5 @@
const inherits = require('util').inherits
const Component = require('react').Component
-const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('./actions')
@@ -92,34 +91,25 @@ ConfirmTxScreen.prototype.render = function () {
warningIfExists(props.warning),
- h(ReactCSSTransitionGroup, {
- className: 'css-transition-group',
- transitionName: 'main',
- transitionEnterTimeout: 300,
- transitionLeaveTimeout: 300,
- }, [
-
- currentTxView({
- // Properties
- txData: txData,
- key: txData.id,
- selectedAddress: props.selectedAddress,
- accounts: props.accounts,
- identities: props.identities,
- conversionRate,
- currentCurrency,
- blockGasLimit,
- // Actions
- buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress),
- sendTransaction: this.sendTransaction.bind(this),
- cancelTransaction: this.cancelTransaction.bind(this, txData),
- signMessage: this.signMessage.bind(this, txData),
- signPersonalMessage: this.signPersonalMessage.bind(this, txData),
- cancelMessage: this.cancelMessage.bind(this, txData),
- cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
- }),
-
- ]),
+ currentTxView({
+ // Properties
+ txData: txData,
+ key: txData.id,
+ selectedAddress: props.selectedAddress,
+ accounts: props.accounts,
+ identities: props.identities,
+ conversionRate,
+ currentCurrency,
+ blockGasLimit,
+ // Actions
+ buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress),
+ sendTransaction: this.sendTransaction.bind(this),
+ cancelTransaction: this.cancelTransaction.bind(this, txData),
+ signMessage: this.signMessage.bind(this, txData),
+ signPersonalMessage: this.signPersonalMessage.bind(this, txData),
+ cancelMessage: this.cancelMessage.bind(this, txData),
+ cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
+ }),
])
)
}
diff --git a/ui/app/css/index.css b/ui/app/css/index.css
index 808aafb4c..05bdb33af 100644
--- a/ui/app/css/index.css
+++ b/ui/app/css/index.css
@@ -19,17 +19,54 @@ html, body {
font-weight: 300;
line-height: 1.4em;
background: #F7F7F7;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ min-height: 500px;
+}
+
+.app-root {
+ overflow: hidden;
+ position: relative
+}
+
+.app-primary {
+ display: flex;
}
input:focus, textarea:focus {
outline: none;
}
+.full-size {
+ height: 100%;
+ width: 100%;
+}
+
+.full-width {
+ width: 100%;
+}
+
+.full-height {
+ height: 100%;
+}
+
+.full-flex-height {
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+}
+
#app-content {
overflow-x: hidden;
min-width: 357px;
- width: 360px;
- height: 500px;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
}
button, input[type="submit"] {
@@ -130,10 +167,6 @@ h2.page-subtitle {
margin: 12px;
}
-.app-primary {
-
-}
-
.app-footer {
padding-bottom: 10px;
align-items: center;
@@ -403,8 +436,16 @@ input.large-input {
/* account detail screen */
.account-detail-section {
+ display: flex;
+ flex-wrap: wrap;
+ overflow-y: auto;
+ flex-direction: inherit;
+}
+.grow-tenx {
+ flex-grow: 10;
}
+
.name-label{
}
diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css
index 910a24ee2..98570859a 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;
@@ -266,3 +270,4 @@ hr.horizontal-line {
margin-top: 20px;
color: red;
}
+
diff --git a/ui/app/info.js b/ui/app/info.js
index cb2e41f5b..899841c83 100644
--- a/ui/app/info.js
+++ b/ui/app/info.js
@@ -20,7 +20,11 @@ InfoScreen.prototype.render = function () {
const version = global.platform.getVersion()
return (
- h('.flex-column.flex-grow', [
+ h('.flex-column.flex-grow', {
+ style: {
+ maxWidth: '400px',
+ },
+ }, [
// subtitle and nav
h('.section-title.flex-row.flex-center', [
@@ -103,12 +107,7 @@ InfoScreen.prototype.render = function () {
target: '_blank',
}, 'Visit our Support Center'),
]),
- h('div.fa.fa-github', [
- h('a.info', {
- href: 'https://github.com/MetaMask/metamask-extension/issues/new',
- target: '_blank',
- }, 'Found a bug? Report it!'),
- ]),
+
h('div', [
h('a', {
href: 'https://metamask.io/',
@@ -126,6 +125,7 @@ InfoScreen.prototype.render = function () {
h('div.info', 'Visit our web site'),
]),
]),
+
h('div.fa.fa-slack', [
h('a.info', {
href: 'http://slack.metamask.io',
@@ -133,11 +133,13 @@ InfoScreen.prototype.render = function () {
}, 'Join the conversation on Slack'),
]),
- h('div.fa.fa-twitter', [
- h('a.info', {
- href: 'https://twitter.com/metamask_io',
- target: '_blank',
- }, 'Follow us on Twitter'),
+ h('div', [
+ h('.fa.fa-twitter', [
+ h('a.info', {
+ href: 'https://twitter.com/metamask_io',
+ target: '_blank',
+ }, 'Follow us on Twitter'),
+ ]),
]),
h('div.fa.fa-envelope', [
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',
diff --git a/ui/app/keychains/hd/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js
index 4ccbec9fc..4335186a5 100644
--- a/ui/app/keychains/hd/recover-seed/confirmation.js
+++ b/ui/app/keychains/hd/recover-seed/confirmation.js
@@ -23,7 +23,9 @@ RevealSeedConfirmation.prototype.render = function () {
return (
- h('.initialize-screen.flex-column.flex-center.flex-grow', [
+ h('.initialize-screen.flex-column.flex-center.flex-grow', {
+ style: { maxWidth: '420px' },
+ }, [
h('h3.flex-center.text-transform-uppercase', {
style: {
@@ -61,7 +63,7 @@ RevealSeedConfirmation.prototype.render = function () {
},
}),
- h('.flex-row.flex-space-between', {
+ h('.flex-row.flex-start', {
style: {
marginTop: 30,
width: '50%',
@@ -74,6 +76,7 @@ RevealSeedConfirmation.prototype.render = function () {
// submit
h('button.primary', {
+ style: { marginLeft: '10px' },
onClick: this.revealSeedWords.bind(this),
}, 'OK'),
diff --git a/ui/lib/tx-helper.js b/ui/lib/tx-helper.js
index afc62e7b6..5def23e51 100644
--- a/ui/lib/tx-helper.js
+++ b/ui/lib/tx-helper.js
@@ -18,4 +18,3 @@ module.exports = function (unapprovedTxs, unapprovedMsgs, personalMsgs, network)
return allValues
}
-