Merge pull request #1835 from MetaMask/NewUI

Begin UI Refactor with Responsiveness & New Menus
feature/default_network_editable
kumavis 7 years ago committed by GitHub
commit 57abc58d62
  1. 17
      CHANGELOG.md
  2. 12
      app/home.html
  3. 3
      app/popup.html
  4. 3
      package.json
  5. 34
      test/integration/index.js
  6. 8
      test/integration/lib/first-time.js
  7. 115
      test/unit/responsive/components/dropdown-test.js
  8. 125
      ui/app/account-detail.js
  9. 91
      ui/app/accounts/account-list-item.js
  10. 164
      ui/app/accounts/index.js
  11. 4
      ui/app/actions.js
  12. 359
      ui/app/app.js
  13. 289
      ui/app/components/account-dropdowns.js
  14. 2
      ui/app/components/account-export.js
  15. 41
      ui/app/components/account-info-link.js
  16. 59
      ui/app/components/drop-menu-item.js
  17. 94
      ui/app/components/dropdown.js
  18. 7
      ui/app/components/editable-label.js
  19. 48
      ui/app/components/loading.js
  20. 130
      ui/app/components/menu-droppo.js
  21. 1
      ui/app/components/network.js
  22. 6
      ui/app/components/notice.js
  23. 10
      ui/app/components/shapeshift-form.js
  24. 1
      ui/app/components/tab-bar.js
  25. 42
      ui/app/components/token-list.js
  26. 18
      ui/app/components/transaction-list.js
  27. 48
      ui/app/conf-tx.js
  28. 53
      ui/app/css/index.css
  29. 41
      ui/app/css/lib.css
  30. 26
      ui/app/info.js
  31. 2
      ui/app/keychains/hd/create-vault-complete.js
  32. 7
      ui/app/keychains/hd/recover-seed/confirmation.js
  33. 6
      ui/app/unlock.js
  34. 1
      ui/lib/tx-helper.js

@ -2,6 +2,9 @@
## Current Master ## Current Master
- Replace account screen with an account drop-down menu.
- Replace confusing buttons with a new account-specific drop-down menu.
## 3.9.5 2017-8-04 ## 3.9.5 2017-8-04
- Improved phishing detection configuration update rate - Improved phishing detection configuration update rate
@ -86,7 +89,7 @@
## 3.7.8 2017-6-12 ## 3.7.8 2017-6-12
- Add a `ethereum:` prefix to the QR code address - Add an `ethereum:` prefix to the QR code address
- The default network on installation is now MainNet - The default network on installation is now MainNet
- Fix currency API URL from cryptonator. - Fix currency API URL from cryptonator.
- Update gasLimit params with every new block seen. - Update gasLimit params with every new block seen.
@ -242,7 +245,7 @@
- Add ability to import accounts in JSON file format (used by Mist, Geth, MyEtherWallet, and more!) - Add ability to import accounts in JSON file format (used by Mist, Geth, MyEtherWallet, and more!)
- Fix unapproved messages not being included in extension badge. - Fix unapproved messages not being included in extension badge.
- Fix rendering bug where the Confirm transaction view would lets you approve transactions when the account has insufficient balance. - Fix rendering bug where the Confirm transaction view would let you approve transactions when the account has insufficient balance.
## 3.1.2 2017-1-24 ## 3.1.2 2017-1-24
@ -265,8 +268,8 @@
## 3.0.0 2017-1-16 ## 3.0.0 2017-1-16
- Fix seed word account generation (https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.t4i1qmmsz). - Fix seed word account generation (https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.t4i1qmmsz).
- Fix Bug where you see a empty transaction flash by on the confirm transaction view. - Fix Bug where you see an empty transaction flash by on the confirm transaction view.
- Create visible difference in transaction history between a approved but not yet included in a block transaction and a transaction who has been confirmed. - Create visible difference in transaction history between an approved but not yet included in a block transaction and a transaction who has been confirmed.
- Fix memory leak in RPC Cache - Fix memory leak in RPC Cache
- Override RPC commands eth_syncing and web3_clientVersion - Override RPC commands eth_syncing and web3_clientVersion
- Remove certain non-essential permissions from certain builds. - Remove certain non-essential permissions from certain builds.
@ -321,7 +324,7 @@
- Fix bug where gas estimate would sometimes be very high. - Fix bug where gas estimate would sometimes be very high.
- Increased our gas estimate from 100k gas to 20% of estimate. - Increased our gas estimate from 100k gas to 20% of estimate.
- Fix github link on info page to point at current repository. - Fix GitHub link on info page to point at current repository.
## 2.13.6 2016-10-26 ## 2.13.6 2016-10-26
@ -397,7 +400,7 @@ popup notification opens up.
- Block negative values from transactions. - Block negative values from transactions.
- Fixed a memory leak. - Fixed a memory leak.
- MetaMask logo now renders as super lightweight SVG, improving compatibility and performance. - MetaMask logo now renders as super lightweight SVG, improving compatibility and performance.
- Now showing loading indication during vault unlocking, to clarify behavior for users who are experience slow unlocks. - Now showing loading indication during vault unlocking, to clarify behavior for users who are experiencing slow unlocks.
- Now only initially creates one wallet when restoring a vault, to reduce some users' confusion. - Now only initially creates one wallet when restoring a vault, to reduce some users' confusion.
## 2.10.2 2016-09-02 ## 2.10.2 2016-09-02
@ -429,7 +432,7 @@ popup notification opens up.
- Added info link on account screen that visits Etherscan. - Added info link on account screen that visits Etherscan.
- Fixed bug where a message signing request would be lost if the vault was locked. - Fixed bug where a message signing request would be lost if the vault was locked.
- Added shortcut to open MetaMask (Ctrl+Alt+M or Cmd+Opt/Alt+M) - Added shortcut to open MetaMask (Ctrl+Alt+M or Cmd+Opt/Alt+M)
- Prevent API calls in tests. - Prevent API calls in tests.
- Fixed bug where sign message confirmation would sometimes render blank. - Fixed bug where sign message confirmation would sometimes render blank.
## 2.9.0 2016-08-22 ## 2.9.0 2016-08-22

@ -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>

@ -2,9 +2,10 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no">
<title>MetaMask Plugin</title> <title>MetaMask Plugin</title>
</head> </head>
<body> <body style="width:357px; height:500px;">
<div id="app-content"></div> <div id="app-content"></div>
<script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script> <script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script>
</body> </body>

@ -91,7 +91,6 @@
"inject-css": "^0.1.1", "inject-css": "^0.1.1",
"jazzicon": "^1.2.0", "jazzicon": "^1.2.0",
"loglevel": "^1.4.1", "loglevel": "^1.4.1",
"menu-droppo": "^1.1.0",
"metamask-logo": "^2.1.2", "metamask-logo": "^2.1.2",
"mississippi": "^1.2.0", "mississippi": "^1.2.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
@ -109,7 +108,7 @@
"pumpify": "^1.3.4", "pumpify": "^1.3.4",
"qrcode-npm": "0.0.3", "qrcode-npm": "0.0.3",
"react": "^15.0.2", "react": "^15.0.2",
"react-addons-css-transition-group": "^15.0.2", "react-addons-css-transition-group": "^15.6.0",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-hyperscript": "^2.2.2", "react-hyperscript": "^2.2.2",
"react-markdown": "^2.3.0", "react-markdown": "^2.3.0",

@ -1,23 +1,19 @@
var fs = require('fs') const fs = require('fs')
var path = require('path') const path = require('path')
var browserify = require('browserify') const browserify = require('browserify')
var tests = fs.readdirSync(path.join(__dirname, 'lib')) const tests = fs.readdirSync(path.join(__dirname, 'lib'))
var bundlePath = path.join(__dirname, 'bundle.js') const bundlePath = path.join(__dirname, 'bundle.js')
var b = browserify() const b = browserify()
// Remove old bundle const writeStream = fs.createWriteStream(bundlePath)
try {
fs.unlinkSync(bundlePath)
var writeStream = fs.createWriteStream(bundlePath) tests.forEach(function (fileName) {
b.add(path.join(__dirname, 'lib', fileName))
tests.forEach(function (fileName) { })
b.add(path.join(__dirname, 'lib', fileName))
})
b.bundle().pipe(writeStream)
} catch (e) {
console.error('Integration build failure', e)
}
b.bundle()
.pipe(writeStream)
.on('error', (err) => {
throw err
})

@ -90,7 +90,13 @@ QUnit.test('render init screen', function (assert) {
return wait() return wait()
}).then(function (){ }).then(function (){
var qrButton = app.find('.fa.fa-qrcode')[0] var qrButton = app.find('.fa.fa-ellipsis-h')[0] // open account settings dropdown
qrButton.click()
return wait(1000)
}).then(function (){
var qrButton = app.find('.dropdown-menu-item')[1] // qr code item
qrButton.click() qrButton.click()
return wait(1000) return wait(1000)

@ -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);
});
});

@ -3,21 +3,17 @@ const extend = require('xtend')
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const connect = require('react-redux').connect const connect = require('react-redux').connect
const CopyButton = require('./components/copyButton')
const AccountInfoLink = require('./components/account-info-link')
const actions = require('./actions') const actions = require('./actions')
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const valuesFor = require('./util').valuesFor const valuesFor = require('./util').valuesFor
const Identicon = require('./components/identicon') const Identicon = require('./components/identicon')
const EthBalance = require('./components/eth-balance') const EthBalance = require('./components/eth-balance')
const TransactionList = require('./components/transaction-list') const TransactionList = require('./components/transaction-list')
const ExportAccountView = require('./components/account-export') const ExportAccountView = require('./components/account-export')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const EditableLabel = require('./components/editable-label') const EditableLabel = require('./components/editable-label')
const Tooltip = require('./components/tooltip')
const TabBar = require('./components/tab-bar') const TabBar = require('./components/tab-bar')
const TokenList = require('./components/token-list') const TokenList = require('./components/token-list')
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
module.exports = connect(mapStateToProps)(AccountDetailScreen) module.exports = connect(mapStateToProps)(AccountDetailScreen)
@ -54,12 +50,13 @@ AccountDetailScreen.prototype.render = function () {
return ( return (
h('.account-detail-section', [ h('.account-detail-section.full-flex-height', [
// identicon, label, balance, etc // identicon, label, balance, etc
h('.account-data-subsection', { h('.account-data-subsection', {
style: { style: {
margin: '0 20px', margin: '0 20px',
flex: '1 0 auto',
}, },
}, [ }, [
@ -84,6 +81,7 @@ AccountDetailScreen.prototype.render = function () {
style: { style: {
lineHeight: '10px', lineHeight: '10px',
marginLeft: '15px', marginLeft: '15px',
width: '100%',
}, },
}, [ }, [
h(EditableLabel, { h(EditableLabel, {
@ -98,7 +96,43 @@ AccountDetailScreen.prototype.render = function () {
// What is shown when not editing + edit text: // What is shown when not editing + edit text:
h('label.editing-label', [h('.edit-text', 'edit')]), 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,
enableAccountOptions: true,
},
),
]
),
]), ]),
h('.flex-row', { h('.flex-row', {
style: { style: {
@ -119,61 +153,10 @@ AccountDetailScreen.prototype.render = function () {
fontSize: '13px', fontSize: '13px',
fontFamily: 'Montserrat Light', fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision', textRendering: 'geometricPrecision',
marginTop: '10px',
marginBottom: '15px', marginBottom: '15px',
color: '#AEAEAE', color: '#AEAEAE',
}, },
}, checksumAddress), }, 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 // account ballence
@ -197,14 +180,11 @@ AccountDetailScreen.prototype.render = function () {
}, },
}), }),
h('.flex-grow'),
h('button', { h('button', {
onClick: () => props.dispatch(actions.buyEthView(selected)), onClick: () => props.dispatch(actions.buyEthView(selected)),
style: { style: { marginRight: '10px' },
marginBottom: '20px',
marginRight: '8px',
position: 'absolute',
left: '219px',
},
}, 'BUY'), }, 'BUY'),
h('button', { h('button', {
@ -219,14 +199,7 @@ AccountDetailScreen.prototype.render = function () {
]), ]),
// subview (tx history, pk export confirm, buy eth warning) // subview (tx history, pk export confirm, buy eth warning)
h(ReactCSSTransitionGroup, { this.subview(),
className: 'css-transition-group',
transitionName: 'main',
transitionEnterTimeout: 300,
transitionLeaveTimeout: 300,
}, [
this.subview(),
]),
]) ])
) )
@ -254,7 +227,7 @@ AccountDetailScreen.prototype.subview = function () {
AccountDetailScreen.prototype.tabSections = function () { AccountDetailScreen.prototype.tabSections = function () {
const { currentAccountTab } = this.props const { currentAccountTab } = this.props
return h('section.tabSection', [ return h('section.tabSection.full-flex-height.grow-tenx', [
h(TabBar, { h(TabBar, {
tabs: [ tabs: [
@ -305,7 +278,3 @@ AccountDetailScreen.prototype.transactionList = function () {
}, },
}) })
} }
AccountDetailScreen.prototype.requestAccountExport = function () {
this.props.dispatch(actions.requestExportAccount())
}

@ -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)
}

@ -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())
}

@ -709,7 +709,7 @@ function markAccountsFound () {
// //
// default rpc target refers to localhost:8545 in this instance. // default rpc target refers to localhost:8545 in this instance.
function setDefaultRpcTarget (rpcList) { function setDefaultRpcTarget () {
log.debug(`background.setDefaultRpcTarget`) log.debug(`background.setDefaultRpcTarget`)
return (dispatch) => { return (dispatch) => {
background.setDefaultRpc((err, result) => { background.setDefaultRpc((err, result) => {
@ -722,7 +722,7 @@ function setDefaultRpcTarget (rpcList) {
} }
function setRpcTarget (newRpc) { function setRpcTarget (newRpc) {
log.debug(`background.setRpcTarget`) log.debug(`background.setRpcTarget: ${newRpc}`)
return (dispatch) => { return (dispatch) => {
background.setCustomRpc(newRpc, (err, result) => { background.setCustomRpc(newRpc, (err, result) => {
if (err) { if (err) {

@ -3,14 +3,12 @@ const Component = require('react').Component
const connect = require('react-redux').connect const connect = require('react-redux').connect
const h = require('react-hyperscript') const h = require('react-hyperscript')
const actions = require('./actions') const actions = require('./actions')
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
// init // init
const InitializeMenuScreen = require('./first-time/init-menu') const InitializeMenuScreen = require('./first-time/init-menu')
const NewKeyChainScreen = require('./new-keychain') const NewKeyChainScreen = require('./new-keychain')
// unlock // unlock
const UnlockScreen = require('./unlock') const UnlockScreen = require('./unlock')
// accounts // accounts
const AccountsScreen = require('./accounts')
const AccountDetailScreen = require('./account-detail') const AccountDetailScreen = require('./account-detail')
const SendTransactionScreen = require('./send') const SendTransactionScreen = require('./send')
const ConfirmTxScreen = require('./conf-tx') const ConfirmTxScreen = require('./conf-tx')
@ -24,15 +22,15 @@ const Import = require('./accounts/import')
const InfoScreen = require('./info') const InfoScreen = require('./info')
const Loading = require('./components/loading') const Loading = require('./components/loading')
const SandwichExpando = require('sandwich-expando') const SandwichExpando = require('sandwich-expando')
const MenuDroppo = require('menu-droppo') const Dropdown = require('./components/dropdown').Dropdown
const DropMenuItem = require('./components/drop-menu-item') const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
const NetworkIndicator = require('./components/network') const NetworkIndicator = require('./components/network')
const Tooltip = require('./components/tooltip')
const BuyView = require('./components/buy-button-subview') const BuyView = require('./components/buy-button-subview')
const QrView = require('./components/qr-code') const QrView = require('./components/qr-code')
const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete') const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault') const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation') const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
module.exports = connect(mapStateToProps)(App) module.exports = connect(mapStateToProps)(App)
@ -40,6 +38,13 @@ inherits(App, Component)
function App () { Component.call(this) } function App () { Component.call(this) }
function mapStateToProps (state) { function mapStateToProps (state) {
const {
identities,
accounts,
address,
} = state.metamask
const selected = address || Object.keys(accounts)[0]
return { return {
// state from plugin // state from plugin
isLoading: state.appState.isLoading, isLoading: state.appState.isLoading,
@ -60,6 +65,10 @@ function mapStateToProps (state) {
lastUnreadNotice: state.metamask.lastUnreadNotice, lastUnreadNotice: state.metamask.lastUnreadNotice,
lostAccounts: state.metamask.lostAccounts, lostAccounts: state.metamask.lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [], frequentRpcList: state.metamask.frequentRpcList || [],
// state needed to get account dropdown temporarily rendering from app bar
identities,
selected,
} }
} }
@ -69,16 +78,16 @@ App.prototype.render = function () {
const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config' const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
const loadMessage = loadingMessage || isLoadingNetwork ? const loadMessage = loadingMessage || isLoadingNetwork ?
`Connecting to ${this.getNetworkName()}` : null `Connecting to ${this.getNetworkName()}` : null
log.debug('Main ui render function') log.debug('Main ui render function')
return ( return (
h('.flex-column.flex-grow.full-height', { h('.flex-column.full-height', {
style: { style: {
// Windows was showing a vertical scroll bar: // Windows was showing a vertical scroll bar:
overflow: 'hidden', overflow: 'hidden',
position: 'relative', position: 'relative',
alignItems: 'center',
}, },
}, [ }, [
@ -93,20 +102,12 @@ App.prototype.render = function () {
}), }),
// panel content // panel content
h('.app-primary.flex-grow' + (transForward ? '.from-right' : '.from-left'), { h('.app-primary' + (transForward ? '.from-right' : '.from-left'), {
style: { style: {
height: '380px', width: '100%',
width: '360px',
}, },
}, [ }, [
h(ReactCSSTransitionGroup, { this.renderPrimary(),
className: 'css-transition-group',
transitionName: 'main',
transitionEnterTimeout: 300,
transitionLeaveTimeout: 300,
}, [
this.renderPrimary(),
]),
]), ]),
]) ])
) )
@ -123,14 +124,16 @@ App.prototype.renderAppBar = function () {
return ( return (
h('div', [ h('.full-width', {
height: '38px',
}, [
h('.app-header.flex-row.flex-space-between', { h('.app-header.flex-row.flex-space-between', {
style: { style: {
alignItems: 'center', alignItems: 'center',
visibility: props.isUnlocked ? 'visible' : 'none', visibility: props.isUnlocked ? 'visible' : 'none',
background: props.isUnlocked ? 'white' : 'none', background: props.isUnlocked ? 'white' : 'none',
height: '36px', height: '38px',
position: 'relative', position: 'relative',
zIndex: 12, zIndex: 12,
}, },
@ -178,32 +181,26 @@ App.prototype.renderAppBar = function () {
}, },
}, [ }, [
// small accounts nav props.isUnlocked && h(AccountDropdowns, {
props.isUnlocked && h(Tooltip, { title: 'Switch Accounts' }, [ style: {},
h('img.cursor-pointer.color-orange', { enableAccountsSelector: true,
src: 'images/switch_acc.svg', identities: this.props.identities,
style: { selected: this.props.selected,
width: '23.5px', network: this.props.network,
marginRight: '8px', }, []),
},
onClick: (event) => {
event.stopPropagation()
this.props.dispatch(actions.showAccountsPage())
},
}),
]),
// hamburger // hamburger
props.isUnlocked && h(SandwichExpando, { props.isUnlocked && h(SandwichExpando, {
className: 'sandwich-expando',
width: 16, width: 16,
barHeight: 2, barHeight: 2,
padding: 0, padding: 0,
isOpen: state.isMainMenuOpen, isOpen: state.isMainMenuOpen,
color: 'rgb(247,146,30)', color: 'rgb(247,146,30)',
onClick: (event) => { onClick: () => {
event.preventDefault() this.setState({
event.stopPropagation() isMainMenuOpen: !state.isMainMenuOpen,
this.setState({ isMainMenuOpen: !state.isMainMenuOpen }) })
}, },
}), }),
]), ]),
@ -214,84 +211,141 @@ App.prototype.renderAppBar = function () {
App.prototype.renderNetworkDropdown = function () { App.prototype.renderNetworkDropdown = function () {
const props = this.props const props = this.props
const { provider: { type: providerType, rpcTarget: activeNetwork } } = props
const rpcList = props.frequentRpcList const rpcList = props.frequentRpcList
const state = this.state || {} const state = this.state || {}
const isOpen = state.isNetworkMenuOpen const isOpen = state.isNetworkMenuOpen
return h(MenuDroppo, { return h(Dropdown, {
useCssTransition: true,
isOpen, isOpen,
onClickOutside: (event) => { 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, zIndex: 11,
style: { style: {
position: 'absolute', position: 'absolute',
left: 0, left: '2px',
top: '36px', top: '36px',
}, },
innerStyle: { innerStyle: {
background: 'white', padding: '2px 16px 2px 0px',
boxShadow: '1px 1px 2px rgba(0,0,0,0.1)',
}, },
}, [ // DROP MENU ITEMS }, [
h('style', `
.drop-menu-item:hover { background:rgb(235, 235, 235); } h(
.drop-menu-item i { margin: 11px; } DropdownMenuItem,
`), {
key: 'main',
h(DropMenuItem, { closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
label: 'Main Ethereum Network', onClick: () => props.dispatch(actions.setProviderType('mainnet')),
closeMenu: () => this.setState({ isNetworkMenuOpen: false }), style: {
action: () => props.dispatch(actions.setProviderType('mainnet')), fontSize: '18px',
icon: h('.menu-icon.diamond'), },
activeNetworkRender: props.network, },
provider: props.provider, [
}), h('.menu-icon.diamond'),
'Main Ethereum Network',
h(DropMenuItem, { providerType === 'mainnet' ? h('.check', '✓') : null,
label: 'Ropsten Test Network', ]
closeMenu: () => this.setState({ isNetworkMenuOpen: false }), ),
action: () => props.dispatch(actions.setProviderType('ropsten')),
icon: h('.menu-icon.red-dot'), h(
activeNetworkRender: props.network, DropdownMenuItem,
provider: props.provider, {
}), key: 'ropsten',
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
h(DropMenuItem, { onClick: () => props.dispatch(actions.setProviderType('ropsten')),
label: 'Kovan Test Network', style: {
closeMenu: () => this.setState({ isNetworkMenuOpen: false}), fontSize: '18px',
action: () => props.dispatch(actions.setProviderType('kovan')), },
icon: h('.menu-icon.hollow-diamond'), },
activeNetworkRender: props.network, [
provider: props.provider, h('.menu-icon.red-dot'),
}), 'Ropsten Test Network',
providerType === 'ropsten' ? h('.check', '✓') : null,
h(DropMenuItem, { ]
label: 'Rinkeby Test Network', ),
closeMenu: () => this.setState({ isNetworkMenuOpen: false}),
action: () => props.dispatch(actions.setProviderType('rinkeby')), h(
icon: h('.menu-icon.golden-square'), DropdownMenuItem,
activeNetworkRender: props.network, {
provider: props.provider, key: 'kovan',
}), closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
onClick: () => props.dispatch(actions.setProviderType('kovan')),
h(DropMenuItem, { style: {
label: 'Localhost 8545', fontSize: '18px',
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('.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')),
style: {
fontSize: '18px',
},
},
[
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()),
style: {
fontSize: '18px',
},
},
[
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
'Localhost 8545',
activeNetwork === 'http://localhost:8545' ? h('.check', '✓') : null,
]
),
this.renderCustomOption(props.provider), this.renderCustomOption(props.provider),
this.renderCommonRpc(rpcList, props.provider), this.renderCommonRpc(rpcList, props.provider),
h(DropMenuItem, { h(
label: 'Custom RPC', DropdownMenuItem,
closeMenu: () => this.setState({ isNetworkMenuOpen: false }), {
action: () => this.props.dispatch(actions.showConfigPage()), closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
icon: h('i.fa.fa-question-circle.fa-lg'), onClick: () => this.props.dispatch(actions.showConfigPage()),
}), style: {
fontSize: '18px',
},
},
[
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
'Custom RPC',
activeNetwork === 'custom' ? h('.check', '✓') : null,
]
),
]) ])
} }
@ -300,54 +354,42 @@ App.prototype.renderDropdown = function () {
const state = this.state || {} const state = this.state || {}
const isOpen = state.isMainMenuOpen const isOpen = state.isMainMenuOpen
return h(MenuDroppo, { return h(Dropdown, {
useCssTransition: true,
isOpen: isOpen, isOpen: isOpen,
zIndex: 11, zIndex: 11,
onClickOutside: (event) => { onClickOutside: (event) => {
this.setState({ isMainMenuOpen: !isOpen }) const classList = event.target.classList
const parentClassList = event.target.parentElement.classList
const isToggleElement = classList.contains('sandwich-expando') ||
parentClassList.contains('sandwich-expando')
if (isOpen && !isToggleElement) {
this.setState({ isMainMenuOpen: false })
}
}, },
style: { style: {
position: 'absolute', position: 'absolute',
right: 0, right: '2px',
top: '36px', top: '38px',
},
innerStyle: {
background: 'white',
boxShadow: '1px 1px 2px rgba(0,0,0,0.1)',
}, },
}, [ // DROP MENU ITEMS innerStyle: {},
h('style', ` }, [
.drop-menu-item:hover { background:rgb(235, 235, 235); } h(DropdownMenuItem, {
.drop-menu-item i { margin: 11px; }
`),
h(DropMenuItem, {
label: 'Settings',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
action: () => this.props.dispatch(actions.showConfigPage()),
icon: h('i.fa.fa-gear.fa-lg'),
}),
h(DropMenuItem, {
label: 'Import Account',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
action: () => this.props.dispatch(actions.showImportPage()), onClick: () => { this.props.dispatch(actions.showConfigPage()) },
icon: h('i.fa.fa-arrow-circle-o-up.fa-lg'), }, 'Settings'),
}),
h(DropMenuItem, { h(DropdownMenuItem, {
label: 'Lock',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
action: () => this.props.dispatch(actions.lockMetamask()), onClick: () => { this.props.dispatch(actions.lockMetamask()) },
icon: h('i.fa.fa-lock.fa-lg'), }, 'Lock'),
}),
h(DropMenuItem, { h(DropdownMenuItem, {
label: 'Info/Help',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
action: () => this.props.dispatch(actions.showInfoPage()), onClick: () => { this.props.dispatch(actions.showInfoPage()) },
icon: h('i.fa.fa-question.fa-lg'), }, 'Info/Help'),
}),
]) ])
} }
@ -433,10 +475,6 @@ App.prototype.renderPrimary = function () {
// show current view // show current view
switch (props.currentView.name) { switch (props.currentView.name) {
case 'accounts':
log.debug('rendering accounts screen')
return h(AccountsScreen, {key: 'accounts'})
case 'accountDetail': case 'accountDetail':
log.debug('rendering account detail screen') log.debug('rendering account detail screen')
return h(AccountDetailScreen, {key: 'account-detail'}) return h(AccountDetailScreen, {key: 'account-detail'})
@ -525,6 +563,8 @@ App.prototype.toggleMetamaskActive = function () {
App.prototype.renderCustomOption = function (provider) { App.prototype.renderCustomOption = function (provider) {
const { rpcTarget, type } = provider const { rpcTarget, type } = provider
const props = this.props
if (type !== 'rpc') return null if (type !== 'rpc') return null
// Concatenate long URLs // Concatenate long URLs
@ -539,13 +579,19 @@ App.prototype.renderCustomOption = function (provider) {
return null return null
default: default:
return h(DropMenuItem, { return h(
label, DropdownMenuItem,
key: rpcTarget, {
closeMenu: () => this.setState({ isNetworkMenuOpen: false }), key: rpcTarget,
icon: h('i.fa.fa-question-circle.fa-lg'), onClick: () => props.dispatch(actions.setRpcTarget(rpcTarget)),
activeNetworkRender: 'custom', closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
}) },
[
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
label,
h('.check', '✓'),
]
)
} }
} }
@ -571,21 +617,26 @@ App.prototype.getNetworkName = function () {
} }
App.prototype.renderCommonRpc = function (rpcList, provider) { App.prototype.renderCommonRpc = function (rpcList, provider) {
const { rpcTarget } = provider
const props = this.props const props = this.props
const rpcTarget = provider.rpcTarget
return rpcList.map((rpc) => { return rpcList.map((rpc) => {
if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) { if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) {
return null return null
} else { } else {
return h(DropMenuItem, { return h(
label: rpc, DropdownMenuItem,
key: rpc, {
closeMenu: () => this.setState({ isNetworkMenuOpen: false }), key: `common${rpc}`,
action: () => props.dispatch(actions.setRpcTarget(rpc)), closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
icon: h('i.fa.fa-question-circle.fa-lg'), onClick: () => props.dispatch(actions.setRpcTarget(rpc)),
activeNetworkRender: rpc, },
}) [
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
rpc,
rpcTarget === rpc ? h('.check', '✓') : null,
]
)
} }
}) })
} }

@ -0,0 +1,289 @@
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 = 'accounts-selector'
this.optionsMenuToggleClassName = 'fa-ellipsis-h'
}
renderAccounts () {
const { identities, selected } = this.props
return Object.keys(identities).map((key, index) => {
const identity = identities[key]
const isSelected = identity.address === selected
return h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
this.props.actions.showAccountDetail(identity.address)
},
style: {
marginTop: index === 0 ? '5px' : '',
fontSize: '24px',
},
},
[
h(
Identicon,
{
address: identity.address,
diameter: 32,
style: {
marginLeft: '10px',
},
},
),
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, identity.name || ''),
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null),
]
)
})
}
renderAccountSelector () {
const { actions } = this.props
const { accountSelectorActive } = this.state
return h(
Dropdown,
{
useCssTransition: true, // Hardcoded because account selector is temporarily in app-header
style: {
marginLeft: '-238px',
marginTop: '38px',
minWidth: '180px',
overflowY: 'auto',
maxHeight: '300px',
width: '300px',
},
innerStyle: {
padding: '8px 25px',
},
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,
{
style: {
marginLeft: '10px',
},
diameter: 32,
},
),
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, 'Create Account'),
],
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => actions.showImportPage(),
},
[
h(
Identicon,
{
style: {
marginLeft: '10px',
},
diameter: 32,
},
),
h('span', {
style: {
marginLeft: '20px',
fontSize: '24px',
marginBottom: '5px',
},
}, 'Import Account'),
]
),
]
)
}
renderAccountOptions () {
const { actions } = this.props
const { optionsMenuActive } = this.state
return h(
Dropdown,
{
style: {
marginLeft: '-215px',
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, identities } = this.props
var identity = identities[selected]
actions.showQrView(selected, identity ? identity.name : '')
},
},
'Show QR Code',
),
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, enableAccountsSelector, enableAccountOptions } = this.props
const { optionsMenuActive, accountSelectorActive } = this.state
return h(
'span',
{
style: style,
},
[
enableAccountsSelector && h(
// 'i.fa.fa-angle-down',
'div.cursor-pointer.color-orange.accounts-selector',
{
style: {
// fontSize: '1.8em',
background: 'url(images/switch_acc.svg) white center center no-repeat',
height: '25px',
width: '25px',
transform: 'scale(0.75)',
marginRight: '3px',
},
onClick: (event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: !accountSelectorActive,
optionsMenuActive: false,
})
},
},
this.renderAccountSelector(),
),
enableAccountOptions && h(
'i.fa.fa-ellipsis-h',
{
style: {
marginRight: '0.5em',
fontSize: '1.8em',
},
onClick: (event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: false,
optionsMenuActive: !optionsMenuActive,
})
},
},
this.renderAccountOptions()
),
]
)
}
}
AccountDropdowns.defaultProps = {
enableAccountsSelector: false,
enableAccountOptions: false,
}
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()),
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
},
}
}
module.exports = {
AccountDropdowns: connect(null, mapDispatchToProps)(AccountDropdowns),
}

@ -100,7 +100,7 @@ ExportAccountView.prototype.render = function () {
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
overflow: 'hidden', overflow: 'hidden',
webkitUserSelect: 'text', webkitUserSelect: 'text',
width: '100%', maxWidth: '275px',
}, },
onClick: function (event) { onClick: function (event) {
copyToClipboard(ethUtil.stripHexPrefix(accountDetail.privateKey)) copyToClipboard(ethUtil.stripHexPrefix(accountDetail.privateKey))

@ -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 }) },
}),
]),
])
}

@ -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', '✓')
}
}

@ -0,0 +1,94 @@
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, useCssTransition } = 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,
{
useCssTransition,
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,
useCssTransition: false,
}
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, style } = this.props
return h(
'li.dropdown-menu-item',
{
onClick: () => {
onClick()
closeMenu()
},
style: Object.assign({
listStyle: 'none',
padding: '8px 0px 8px 0px',
fontSize: '18px',
fontStyle: 'normal',
fontFamily: 'Montserrat Regular',
cursor: 'pointer',
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
}, style),
},
children
)
}
}
DropdownMenuItem.propTypes = {
closeMenu: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
children: PropTypes.node,
}
module.exports = {
Dropdown,
DropdownMenuItem,
}

@ -30,7 +30,12 @@ EditableLabel.prototype.render = function () {
} else { } else {
return h('div.name-label', { return h('div.name-label', {
onClick: (event) => { 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) }, this.props.children)
} }

@ -1,7 +1,6 @@
const inherits = require('util').inherits const inherits = require('util').inherits
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
inherits(LoadingIndicator, Component) inherits(LoadingIndicator, Component)
@ -15,35 +14,28 @@ LoadingIndicator.prototype.render = function () {
const { isLoading, loadingMessage } = this.props const { isLoading, loadingMessage } = this.props
return ( return (
h(ReactCSSTransitionGroup, { isLoading ? h('.full-flex-height', {
className: 'css-transition-group', style: {
transitionName: 'loader', left: '0px',
transitionEnterTimeout: 150, zIndex: 10,
transitionLeaveTimeout: 150, 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', { h('br'),
style: {
zIndex: 10, showMessageIfAny(loadingMessage),
position: 'absolute', ]) : null
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,
])
) )
} }

@ -0,0 +1,130 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const findDOMNode = require('react-dom').findDOMNode
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
module.exports = MenuDroppoComponent
inherits(MenuDroppoComponent, Component)
function MenuDroppoComponent () {
Component.call(this)
}
MenuDroppoComponent.prototype.render = function () {
const speed = this.props.speed || '300ms'
const useCssTransition = this.props.useCssTransition
const zIndex = ('zIndex' in this.props) ? this.props.zIndex : 0
this.manageListeners()
let style = this.props.style || {}
if (!('position' in style)) {
style.position = 'fixed'
}
style.zIndex = zIndex
return (
h('.menu-droppo-container', {
style,
}, [
h('style', `
.menu-droppo-enter {
transition: transform ${speed} ease-in-out;
transform: translateY(-200%);
}
.menu-droppo-enter.menu-droppo-enter-active {
transition: transform ${speed} ease-in-out;
transform: translateY(0%);
}
.menu-droppo-leave {
transition: transform ${speed} ease-in-out;
transform: translateY(0%);
}
.menu-droppo-leave.menu-droppo-leave-active {
transition: transform ${speed} ease-in-out;
transform: translateY(-200%);
}
`),
useCssTransition
? h(ReactCSSTransitionGroup, {
className: 'css-transition-group',
transitionName: 'menu-droppo',
transitionEnterTimeout: parseInt(speed),
transitionLeaveTimeout: parseInt(speed),
}, this.renderPrimary())
: this.renderPrimary(),
])
)
}
MenuDroppoComponent.prototype.renderPrimary = function () {
const isOpen = this.props.isOpen
if (!isOpen) {
return null
}
const innerStyle = this.props.innerStyle || {}
return (
h('.menu-droppo', {
key: 'menu-droppo-drawer',
style: innerStyle,
},
[ this.props.children ])
)
}
MenuDroppoComponent.prototype.manageListeners = function () {
const isOpen = this.props.isOpen
const onClickOutside = this.props.onClickOutside
if (isOpen) {
this.outsideClickHandler = onClickOutside
} else if (isOpen) {
this.outsideClickHandler = null
}
}
MenuDroppoComponent.prototype.componentDidMount = function () {
if (this && document.body) {
this.globalClickHandler = this.globalClickOccurred.bind(this)
document.body.addEventListener('click', this.globalClickHandler)
var container = findDOMNode(this)
this.container = container
}
}
MenuDroppoComponent.prototype.componentWillUnmount = function () {
if (this && document.body) {
document.body.removeEventListener('click', this.globalClickHandler)
}
}
MenuDroppoComponent.prototype.globalClickOccurred = function (event) {
const target = event.target
const container = findDOMNode(this)
if (target !== container &&
!isDescendant(this.container, event.target) &&
this.outsideClickHandler) {
this.outsideClickHandler(event)
}
}
function isDescendant (parent, child) {
var node = child.parentNode
while (node !== null) {
if (node === parent) {
return true
}
node = node.parentNode
}
return false
}

@ -39,7 +39,6 @@ Network.prototype.render = function () {
}), }),
h('i.fa.fa-sort-desc'), h('i.fa.fa-sort-desc'),
]) ])
} else if (providerName === 'mainnet') { } else if (providerName === 'mainnet') {
hoverText = 'Main Ethereum Network' hoverText = 'Main Ethereum Network'
iconName = 'ethereum-network' iconName = 'ethereum-network'

@ -19,7 +19,11 @@ Notice.prototype.render = function () {
const disabled = state.disclaimerDisabled const disabled = state.disclaimerDisabled
return ( return (
h('.flex-column.flex-center.flex-grow', [ h('.flex-column.flex-center.flex-grow', {
style: {
width: '100%',
},
}, [
h('h3.flex-center.text-transform-uppercase.terms-header', { h('h3.flex-center.text-transform-uppercase.terms-header', {
style: { style: {
background: '#EBEBEB', background: '#EBEBEB',

@ -2,7 +2,6 @@ const PersistentForm = require('../../lib/persistent-form')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const connect = require('react-redux').connect const connect = require('react-redux').connect
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const actions = require('../actions') const actions = require('../actions')
const Qr = require('./qr-code') const Qr = require('./qr-code')
const isValidAddress = require('../util').isValidAddress const isValidAddress = require('../util').isValidAddress
@ -24,14 +23,7 @@ function ShapeshiftForm () {
} }
ShapeshiftForm.prototype.render = function () { ShapeshiftForm.prototype.render = function () {
return h(ReactCSSTransitionGroup, { return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain()
className: 'css-transition-group',
transitionName: 'main',
transitionEnterTimeout: 300,
transitionLeaveTimeout: 300,
}, [
this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain(),
])
} }
ShapeshiftForm.prototype.renderMain = function () { ShapeshiftForm.prototype.renderMain = function () {

@ -21,6 +21,7 @@ TabBar.prototype.render = function () {
background: '#EBEBEB', background: '#EBEBEB',
color: '#AEAEAE', color: '#AEAEAE',
paddingTop: '4px', paddingTop: '4px',
minHeight: '30px',
}, },
}, tabs.map((tab) => { }, tabs.map((tab) => {
const { key, content } = tab const { key, content } = tab

@ -47,10 +47,11 @@ TokenList.prototype.render = function () {
return h(TokenCell, tokenData) return h(TokenCell, tokenData)
}) })
return h('div', [ return h('.full-flex-height', [
h('ol', { this.renderTokenStatusBar(),
h('ol.full-flex-height.flex-column', {
style: { style: {
height: '260px',
overflowY: 'auto', overflowY: 'auto',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -63,6 +64,7 @@ TokenList.prototype.render = function () {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
padding: 10px; padding: 10px;
min-height: 50px;
} }
li.token-cell > h3 { li.token-cell > h3 {
@ -76,17 +78,37 @@ TokenList.prototype.render = function () {
`), `),
...tokenViews, ...tokenViews,
tokenViews.length ? null : this.message('No Tokens Found.'), h('.flex-grow'),
]), ]),
this.addTokenButtonElement(),
]) ])
} }
TokenList.prototype.addTokenButtonElement = function () { TokenList.prototype.renderTokenStatusBar = function () {
return h('div', [ const { tokens } = this.state
h('div.footer.hover-white.pointer', {
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', key: 'reveal-account-bar',
onClick: () => { onClick: (event) => {
event.preventDefault()
this.props.addToken() this.props.addToken()
}, },
style: { style: {
@ -97,7 +119,7 @@ TokenList.prototype.addTokenButtonElement = function () {
alignItems: 'center', alignItems: 'center',
}, },
}, [ }, [
h('i.fa.fa-plus.fa-lg'), 'ADD TOKEN',
]), ]),
]) ])
} }

@ -24,7 +24,11 @@ TransactionList.prototype.render = function () {
return ( return (
h('section.transaction-list', [ h('section.transaction-list.full-flex-height', {
style: {
justifyContent: 'center',
},
}, [
h('style', ` h('style', `
.transaction-list .transaction-list-item:not(:last-of-type) { .transaction-list .transaction-list-item:not(:last-of-type) {
@ -39,7 +43,7 @@ TransactionList.prototype.render = function () {
h('.tx-list', { h('.tx-list', {
style: { style: {
overflowY: 'auto', overflowY: 'auto',
height: '300px', height: '100%',
padding: '0 20px', padding: '0 20px',
textAlign: 'center', textAlign: 'center',
}, },
@ -64,13 +68,17 @@ TransactionList.prototype.render = function () {
}, },
}) })
}) })
: h('.flex-center', { : h('.flex-center.full-flex-height', {
style: { style: {
flexDirection: 'column', flexDirection: 'column',
height: '100%', justifyContent: 'center',
}, },
}, [ }, [
'No transaction history.', h('p', {
style: {
marginTop: '50px',
},
}, 'No transaction history.'),
]), ]),
]), ]),
]) ])

@ -1,6 +1,5 @@
const inherits = require('util').inherits const inherits = require('util').inherits
const Component = require('react').Component const Component = require('react').Component
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const connect = require('react-redux').connect const connect = require('react-redux').connect
const actions = require('./actions') const actions = require('./actions')
@ -92,34 +91,25 @@ ConfirmTxScreen.prototype.render = function () {
warningIfExists(props.warning), warningIfExists(props.warning),
h(ReactCSSTransitionGroup, { currentTxView({
className: 'css-transition-group', // Properties
transitionName: 'main', txData: txData,
transitionEnterTimeout: 300, key: txData.id,
transitionLeaveTimeout: 300, selectedAddress: props.selectedAddress,
}, [ accounts: props.accounts,
identities: props.identities,
currentTxView({ conversionRate,
// Properties currentCurrency,
txData: txData, blockGasLimit,
key: txData.id, // Actions
selectedAddress: props.selectedAddress, buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress),
accounts: props.accounts, sendTransaction: this.sendTransaction.bind(this),
identities: props.identities, cancelTransaction: this.cancelTransaction.bind(this, txData),
conversionRate, signMessage: this.signMessage.bind(this, txData),
currentCurrency, signPersonalMessage: this.signPersonalMessage.bind(this, txData),
blockGasLimit, cancelMessage: this.cancelMessage.bind(this, txData),
// Actions cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
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),
}),
]),
]) ])
) )
} }

@ -19,17 +19,52 @@ html, body {
font-weight: 300; font-weight: 300;
line-height: 1.4em; line-height: 1.4em;
background: #F7F7F7; background: #F7F7F7;
margin: 0;
padding: 0;
}
html {
min-height: 500px;
}
.app-root {
overflow: hidden;
position: relative
}
.app-primary {
display: flex;
} }
input:focus, textarea:focus { input:focus, textarea:focus {
outline: none; 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 { #app-content {
overflow-x: hidden; overflow-x: hidden;
min-width: 357px; min-width: 357px;
width: 360px; height: 100%;
height: 500px; display: flex;
flex-direction: column;
} }
button, input[type="submit"] { button, input[type="submit"] {
@ -130,10 +165,6 @@ h2.page-subtitle {
margin: 12px; margin: 12px;
} }
.app-primary {
}
.app-footer { .app-footer {
padding-bottom: 10px; padding-bottom: 10px;
align-items: center; align-items: center;
@ -170,7 +201,7 @@ textarea.twelve-word-phrase {
} }
.check { .check {
margin-left: 7px; margin-left: 12px;
color: #F7861C; color: #F7861C;
flex: 1 0 auto; flex: 1 0 auto;
display: flex; display: flex;
@ -403,8 +434,16 @@ input.large-input {
/* account detail screen */ /* account detail screen */
.account-detail-section { .account-detail-section {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
flex-direction: inherit;
}
.grow-tenx {
flex-grow: 10;
} }
.name-label{ .name-label{
} }

@ -232,12 +232,21 @@ hr.horizontal-line {
align-items: center; align-items: center;
} }
.tabSection {
min-width: 350px;
}
.menu-icon { .menu-icon {
display: inline-block; display: inline-block;
height: 9px; height: 12px;
min-width: 9px; min-width: 12px;
margin: 13px; margin: 13px;
} }
i.fa.fa-question-circle.fa-lg.menu-icon {
font-size: 18px;
}
.ether-icon { .ether-icon {
background: rgb(0, 163, 68); background: rgb(0, 163, 68);
border-radius: 20px; border-radius: 20px;
@ -266,3 +275,31 @@ hr.horizontal-line {
margin-top: 20px; margin-top: 20px;
color: red; color: red;
} }
/*
Hacky breakpoint fix for account + tab sections
Resolves issue from @frankiebee in
https://github.com/MetaMask/metamask-extension/pull/1835
Please remove this when integrating new designs
*/
@media screen and (min-width: 575px) and (max-width: 800px) {
.account-data-subsection {
flex: 0 0 auto !important; // reset flex
margin-left: 10px !important; // create additional horizontal space
margin-right: 10px !important;
width: 40%;
}
.tabSection {
flex: 0 0 auto !important;
margin-left: 10px !important;
margin-right: 10px !important;
min-width: 285px;
width: 49%;
}
.name-label {
width: 80%;
}
}

@ -20,7 +20,11 @@ InfoScreen.prototype.render = function () {
const version = global.platform.getVersion() const version = global.platform.getVersion()
return ( return (
h('.flex-column.flex-grow', [ h('.flex-column.flex-grow', {
style: {
maxWidth: '400px',
},
}, [
// subtitle and nav // subtitle and nav
h('.section-title.flex-row.flex-center', [ h('.section-title.flex-row.flex-center', [
@ -103,12 +107,7 @@ InfoScreen.prototype.render = function () {
target: '_blank', target: '_blank',
}, 'Visit our Support Center'), }, '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('div', [
h('a', { h('a', {
href: 'https://metamask.io/', href: 'https://metamask.io/',
@ -126,6 +125,7 @@ InfoScreen.prototype.render = function () {
h('div.info', 'Visit our web site'), h('div.info', 'Visit our web site'),
]), ]),
]), ]),
h('div.fa.fa-slack', [ h('div.fa.fa-slack', [
h('a.info', { h('a.info', {
href: 'http://slack.metamask.io', href: 'http://slack.metamask.io',
@ -133,11 +133,13 @@ InfoScreen.prototype.render = function () {
}, 'Join the conversation on Slack'), }, 'Join the conversation on Slack'),
]), ]),
h('div.fa.fa-twitter', [ h('div', [
h('a.info', { h('.fa.fa-twitter', [
href: 'https://twitter.com/metamask_io', h('a.info', {
target: '_blank', href: 'https://twitter.com/metamask_io',
}, 'Follow us on Twitter'), target: '_blank',
}, 'Follow us on Twitter'),
]),
]), ]),
h('div.fa.fa-envelope', [ h('div.fa.fa-envelope', [

@ -47,8 +47,6 @@ CreateVaultCompleteScreen.prototype.render = function () {
h('div', { h('div', {
style: { style: {
width: '360px',
height: '78px',
fontSize: '1em', fontSize: '1em',
marginTop: '10px', marginTop: '10px',
textAlign: 'center', textAlign: 'center',

@ -23,7 +23,9 @@ RevealSeedConfirmation.prototype.render = function () {
return ( 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', { h('h3.flex-center.text-transform-uppercase', {
style: { style: {
@ -61,7 +63,7 @@ RevealSeedConfirmation.prototype.render = function () {
}, },
}), }),
h('.flex-row.flex-space-between', { h('.flex-row.flex-start', {
style: { style: {
marginTop: 30, marginTop: 30,
width: '50%', width: '50%',
@ -74,6 +76,7 @@ RevealSeedConfirmation.prototype.render = function () {
// submit // submit
h('button.primary', { h('button.primary', {
style: { marginLeft: '10px' },
onClick: this.revealSeedWords.bind(this), onClick: this.revealSeedWords.bind(this),
}, 'OK'), }, 'OK'),

@ -26,7 +26,11 @@ UnlockScreen.prototype.render = function () {
const state = this.props const state = this.props
const warning = state.warning const warning = state.warning
return ( return (
h('.flex-column', [ h('.flex-column', {
style: {
width: 'inherit',
},
}, [
h('.unlock-screen.flex-column.flex-center.flex-grow', [ h('.unlock-screen.flex-column.flex-center.flex-grow', [
h(Mascot, { h(Mascot, {

@ -18,4 +18,3 @@ module.exports = function (unapprovedTxs, unapprovedMsgs, personalMsgs, network)
return allValues return allValues
} }

Loading…
Cancel
Save