Merge branch 'NewUI-flat' into NewUI-flat-disable-send-next-in-error

feature/default_network_editable
Daniel Tsui 7 years ago committed by GitHub
commit 09445ef692
  1. 14
      app/images/check-white.svg
  2. 5
      ui/app/add-token.js
  3. 25
      ui/app/app.js
  4. 14
      ui/app/components/account-menu/index.js
  5. 144
      ui/app/components/customize-gas-modal/index.js
  6. 1
      ui/app/components/modals/export-private-key-modal.js
  7. 7
      ui/app/components/network.js
  8. 2
      ui/app/components/pending-tx/confirm-send-ether.js
  9. 36
      ui/app/components/send/currency-display.js
  10. 20
      ui/app/components/send/send-constants.js
  11. 39
      ui/app/components/send/send-utils.js
  12. 8
      ui/app/components/send/send-v2-container.js
  13. 7
      ui/app/components/tx-view.js
  14. 99
      ui/app/components/wallet-view.js
  15. 13
      ui/app/css/itcss/components/account-menu.scss
  16. 13
      ui/app/css/itcss/components/add-token.scss
  17. 6
      ui/app/css/itcss/components/confirm.scss
  18. 10
      ui/app/css/itcss/components/currency-display.scss
  19. 1
      ui/app/css/itcss/components/modal.scss
  20. 15
      ui/app/css/itcss/components/network.scss
  21. 89
      ui/app/css/itcss/components/newui-sections.scss
  22. 23
      ui/app/css/itcss/components/send.scss
  23. 7
      ui/app/css/itcss/components/settings.scss
  24. 6
      ui/app/css/itcss/components/token-list.scss
  25. 1
      ui/app/css/itcss/tools/utilities.scss
  26. 23
      ui/app/selectors.js
  27. 33
      ui/app/send-v2.js
  28. 2
      ui/app/settings.js
  29. 14
      yarn.lock

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="13px" viewBox="0 0 16 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 47 (45396) - http://www.bohemiancoding.com/sketch -->
<title>check-white</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="MetaMascara-v2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="account-dropdown-top-bar-IXD" transform="translate(-17.000000, -80.000000)" fill-rule="nonzero" fill="#FFFFFF">
<g id="Group-11" transform="translate(18.000000, 74.000000)">
<polygon id="check-white" points="4.2 15.5712828 0.714212839 12.0143571 -0.714212839 13.4142143 4.2 18.4287172 14.7142128 7.69992858 13.2857872 6.30007142"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 872 B

@ -350,7 +350,10 @@ AddTokenScreen.prototype.render = function () {
h('div.add-token__footers', [ h('div.add-token__footers', [
h('div.add-token__add-custom', { h('div.add-token__add-custom', {
onClick: () => this.setState({ isCollapsed: !isCollapsed }), onClick: () => this.setState({ isCollapsed: !isCollapsed }),
}, 'Add custom token'), }, [
'Add custom token',
h(`i.fa.fa-angle-${isCollapsed ? 'down' : 'up'}`),
]),
this.renderCustomForm(), this.renderCustomForm(),
]), ]),
]), ]),

@ -203,6 +203,16 @@ App.prototype.renderSidebar = function () {
} }
App.prototype.renderAppBar = function () { App.prototype.renderAppBar = function () {
const {
isUnlocked,
network,
provider,
networkDropdownOpen,
showNetworkDropdown,
hideNetworkDropdown,
currentView,
} = this.props
if (window.METAMASK_UI_TYPE === 'notification') { if (window.METAMASK_UI_TYPE === 'notification') {
return null return null
} }
@ -243,22 +253,21 @@ App.prototype.renderAppBar = function () {
}, [ }, [
// Network Indicator // Network Indicator
h(NetworkIndicator, { h(NetworkIndicator, {
network: this.props.network, network,
provider: this.props.provider, provider,
disabled: currentView.name === 'confTx',
onClick: (event) => { onClick: (event) => {
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
if (this.props.networkDropdownOpen === false) { return networkDropdownOpen === false
this.props.showNetworkDropdown() ? showNetworkDropdown()
} else { : hideNetworkDropdown()
this.props.hideNetworkDropdown()
}
}, },
}), }),
]), ]),
h('div.account-menu__icon', { onClick: this.props.toggleAccountMenu }, [ isUnlocked && h('div.account-menu__icon', { onClick: this.props.toggleAccountMenu }, [
h(Identicon, { h(Identicon, {
address: this.props.selectedAddress, address: this.props.selectedAddress,
diameter: 32, diameter: 32,

@ -63,10 +63,11 @@ AccountMenu.prototype.render = function () {
h(CloseArea, { onClick: toggleAccountMenu }), h(CloseArea, { onClick: toggleAccountMenu }),
h(Item, { h(Item, {
className: 'account-menu__header', className: 'account-menu__header',
onClick: lockMetamask,
}, [ }, [
'My Accounts', 'My Accounts',
h('button.account-menu__logout-button', 'Log out'), h('button.account-menu__logout-button', {
onClick: lockMetamask,
}, 'Log out'),
]), ]),
h(Divider), h(Divider),
h('div.account-menu__accounts', this.renderAccounts()), h('div.account-menu__accounts', this.renderAccounts()),
@ -98,15 +99,14 @@ AccountMenu.prototype.renderAccounts = function () {
const { const {
identities, identities,
accounts, accounts,
selected, selectedAddress,
keyrings, keyrings,
showAccountDetail, showAccountDetail,
} = this.props } = this.props
console.log({ accounts })
return Object.keys(identities).map((key, index) => { return Object.keys(identities).map((key, index) => {
const identity = identities[key] const identity = identities[key]
const isSelected = identity.address === selected const isSelected = identity.address === selectedAddress
const balanceValue = accounts[key] ? accounts[key].balance : '' const balanceValue = accounts[key] ? accounts[key].balance : ''
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...' const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...'
@ -122,7 +122,7 @@ AccountMenu.prototype.renderAccounts = function () {
{ onClick: () => showAccountDetail(identity.address) }, { onClick: () => showAccountDetail(identity.address) },
[ [
h('div.account-menu__check-mark', [ h('div.account-menu__check-mark', [
isSelected ? h('i.fa.fa-check') : null, isSelected ? h('div.account-menu__check-mark-icon') : null,
]), ]),
h( h(
@ -148,6 +148,6 @@ AccountMenu.prototype.indicateIfLoose = function (keyring) {
try { // Sometimes keyrings aren't loaded yet: try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type const type = keyring.type
const isLoose = type !== 'HD Key Tree' const isLoose = type !== 'HD Key Tree'
return isLoose ? h('.keyring-label', 'LOOSE') : null return isLoose ? h('.keyring-label', 'IMPORTED') : null
} catch (e) { return } } catch (e) { return }
} }

@ -6,23 +6,46 @@ const actions = require('../../actions')
const GasModalCard = require('./gas-modal-card') const GasModalCard = require('./gas-modal-card')
const { const {
MIN_GAS_PRICE, MIN_GAS_PRICE_DEC,
MIN_GAS_LIMIT, MIN_GAS_LIMIT_DEC,
MIN_GAS_PRICE_GWEI,
} = require('../send/send-constants') } = require('../send/send-constants')
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util') const {
isBalanceSufficient,
} = require('../send/send-utils')
const {
conversionUtil,
multiplyCurrencies,
conversionGreaterThan,
} = require('../../conversion-util')
const { const {
getGasPrice, getGasPrice,
getGasLimit, getGasLimit,
conversionRateSelector, conversionRateSelector,
getSendAmount,
getSelectedToken,
getSendFrom,
getCurrentAccountWithSendEtherInfo,
getSelectedTokenToFiatRate,
} = require('../../selectors') } = require('../../selectors')
function mapStateToProps (state) { function mapStateToProps (state) {
const selectedToken = getSelectedToken(state)
const currentAccount = getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state)
const conversionRate = conversionRateSelector(state)
return { return {
gasPrice: getGasPrice(state), gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state), gasLimit: getGasLimit(state),
conversionRate: conversionRateSelector(state), conversionRate,
amount: getSendAmount(state),
balance: currentAccount.balance,
primaryCurrency: selectedToken && selectedToken.symbol,
selectedToken,
amountConversionRate: selectedToken ? getSelectedTokenToFiatRate(state) : conversionRate,
} }
} }
@ -39,15 +62,26 @@ inherits(CustomizeGasModal, Component)
function CustomizeGasModal (props) { function CustomizeGasModal (props) {
Component.call(this) Component.call(this)
const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
})
this.state = { this.state = {
gasPrice: props.gasPrice || MIN_GAS_PRICE, gasPrice,
gasLimit: props.gasLimit || MIN_GAS_LIMIT, gasLimit,
gasTotal,
error: null,
} }
} }
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal) module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal)
CustomizeGasModal.prototype.save = function (gasPrice, gasLimit) { CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
const { const {
updateGasPrice, updateGasPrice,
updateGasLimit, updateGasLimit,
@ -55,41 +89,101 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit) {
updateGasTotal updateGasTotal
} = this.props } = this.props
const newGasTotal = multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
})
updateGasPrice(gasPrice) updateGasPrice(gasPrice)
updateGasLimit(gasLimit) updateGasLimit(gasLimit)
updateGasTotal(newGasTotal) updateGasTotal(gasTotal)
hideModal() hideModal()
} }
CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
const {
amount,
balance,
primaryCurrency,
selectedToken,
amountConversionRate,
conversionRate,
} = this.props
let error = null
const balanceIsSufficient = isBalanceSufficient({
amount,
gasTotal,
balance,
primaryCurrency,
selectedToken,
amountConversionRate,
conversionRate,
})
if (!balanceIsSufficient) {
error = 'Insufficient balance for current gas total'
}
const gasLimitTooLow = gasLimit && conversionGreaterThan(
{
value: MIN_GAS_LIMIT_DEC,
fromNumericBase: 'dec',
conversionRate,
},
{
value: gasLimit,
fromNumericBase: 'hex',
},
)
if (gasLimitTooLow) {
error = 'Gas limit must be at least 21000'
}
this.setState({ error })
return error
}
CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) { CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) {
const convertedGasLimit = conversionUtil(newGasLimit, { const { gasPrice } = this.state
const gasLimit = conversionUtil(newGasLimit, {
fromNumericBase: 'dec', fromNumericBase: 'dec',
toNumericBase: 'hex', toNumericBase: 'hex',
}) })
this.setState({ gasLimit: convertedGasLimit }) const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
})
this.validate({ gasTotal, gasLimit })
this.setState({ gasTotal, gasLimit })
} }
CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) { CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
const convertedGasPrice = conversionUtil(newGasPrice, { const { gasLimit } = this.state
const gasPrice = conversionUtil(newGasPrice, {
fromNumericBase: 'dec', fromNumericBase: 'dec',
toNumericBase: 'hex', toNumericBase: 'hex',
fromDenomination: 'GWEI', fromDenomination: 'GWEI',
toDenomination: 'WEI', toDenomination: 'WEI',
}) })
this.setState({ gasPrice: convertedGasPrice }) const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
})
this.validate({ gasTotal })
this.setState({ gasTotal, gasPrice })
} }
CustomizeGasModal.prototype.render = function () { CustomizeGasModal.prototype.render = function () {
const { hideModal, conversionRate } = this.props const { hideModal, conversionRate } = this.props
const { gasPrice, gasLimit } = this.state const { gasPrice, gasLimit, gasTotal, error } = this.state
const convertedGasPrice = conversionUtil(gasPrice, { const convertedGasPrice = conversionUtil(gasPrice, {
fromNumericBase: 'hex', fromNumericBase: 'hex',
@ -120,7 +214,7 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, { h(GasModalCard, {
value: convertedGasPrice, value: convertedGasPrice,
min: MIN_GAS_PRICE, min: MIN_GAS_PRICE_GWEI,
// max: 1000, // max: 1000,
step: 1, step: 1,
onChange: value => this.convertAndSetGasPrice(value), onChange: value => this.convertAndSetGasPrice(value),
@ -130,7 +224,7 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, { h(GasModalCard, {
value: convertedGasLimit, value: convertedGasLimit,
min: MIN_GAS_LIMIT, min: 1,
// max: 100000, // max: 100000,
step: 1, step: 1,
onChange: value => this.convertAndSetGasLimit(value), onChange: value => this.convertAndSetGasLimit(value),
@ -141,6 +235,10 @@ CustomizeGasModal.prototype.render = function () {
]), ]),
h('div.send-v2__customize-gas__footer', {}, [ h('div.send-v2__customize-gas__footer', {}, [
error && h('div.send-v2__customize-gas__error-message', [
error,
]),
h('div.send-v2__customize-gas__revert', { h('div.send-v2__customize-gas__revert', {
onClick: () => console.log('Revert'), onClick: () => console.log('Revert'),
@ -151,8 +249,8 @@ CustomizeGasModal.prototype.render = function () {
onClick: this.props.hideModal, onClick: this.props.hideModal,
}, ['CANCEL']), }, ['CANCEL']),
h('div.send-v2__customize-gas__save', { h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, {
onClick: () => this.save(gasPrice, gasLimit), onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal),
}, ['SAVE']), }, ['SAVE']),
]) ])

@ -66,7 +66,6 @@ ExportPrivateKeyModal.prototype.renderPasswordInput = function (privateKey) {
}) })
: h('input.private-key-password-input', { : h('input.private-key-password-input', {
type: 'password', type: 'password',
placeholder: 'Type password',
onChange: event => this.setState({ password: event.target.value }), onChange: event => this.setState({ password: event.target.value }),
}) })
} }

@ -64,13 +64,18 @@ Network.prototype.render = function () {
return ( return (
h('div.network-component.pointer', { h('div.network-component.pointer', {
className: classnames('network-component pointer', { className: classnames('network-component pointer', {
'network-component--disabled': this.props.disabled,
'ethereum-network': providerName === 'mainnet', 'ethereum-network': providerName === 'mainnet',
'ropsten-test-network': providerName === 'ropsten' || parseInt(networkNumber) === 3, 'ropsten-test-network': providerName === 'ropsten' || parseInt(networkNumber) === 3,
'kovan-test-network': providerName === 'kovan', 'kovan-test-network': providerName === 'kovan',
'rinkeby-test-network': providerName === 'rinkeby', 'rinkeby-test-network': providerName === 'rinkeby',
}), }),
title: hoverText, title: hoverText,
onClick: (event) => this.props.onClick(event), onClick: (event) => {
if (!this.props.disabled) {
this.props.onClick(event)
}
},
}, [ }, [
(function () { (function () {
switch (iconName) { switch (iconName) {

@ -50,7 +50,7 @@ ConfirmSendEther.prototype.getAmount = function () {
const { conversionRate, currentCurrency } = this.props const { conversionRate, currentCurrency } = this.props
const txMeta = this.gatherTxMeta() const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {} const txParams = txMeta.txParams || {}
console.log(`conversionRate, currentCurrency`, conversionRate, currentCurrency);
const FIAT = conversionUtil(txParams.value, { const FIAT = conversionUtil(txParams.value, {
fromNumericBase: 'hex', fromNumericBase: 'hex',
toNumericBase: 'dec', toNumericBase: 'dec',

@ -20,14 +20,6 @@ function isValidInput (text) {
return re.test(text) return re.test(text)
} }
function resetCaretIfPastEnd (value, event) {
const caretPosition = event.target.selectionStart
if (caretPosition > value.length) {
event.target.setSelectionRange(value.length, value.length)
}
}
function toHexWei (value) { function toHexWei (value) {
return conversionUtil(value, { return conversionUtil(value, {
fromNumericBase: 'dec', fromNumericBase: 'dec',
@ -82,6 +74,8 @@ CurrencyDisplay.prototype.render = function () {
conversionRate, conversionRate,
}) })
const inputSizeMultiplier = readOnly ? 1 : 1.2;
return h('div', { return h('div', {
className, className,
style: { style: {
@ -95,35 +89,33 @@ CurrencyDisplay.prototype.render = function () {
h('input', { h('input', {
className: primaryBalanceClassName, className: primaryBalanceClassName,
value: `${value || initValueToRender} ${primaryCurrency}`, value: `${value || initValueToRender}`,
placeholder: `${0} ${primaryCurrency}`, placeholder: '0',
size: (value || initValueToRender).length * inputSizeMultiplier,
readOnly, readOnly,
onChange: (event) => { onChange: (event) => {
let newValue = event.target.value.split(' ')[0] let newValue = event.target.value
if (newValue === '') { if (newValue === '') {
this.setState({ value: '0' }) newValue = '0'
} }
else if (newValue.match(/^0[1-9]$/)) { else if (newValue.match(/^0[1-9]$/)) {
this.setState({ value: newValue.match(/[1-9]/)[0] }) newValue = newValue.match(/[1-9]/)[0]
} }
else if (newValue && !isValidInput(newValue)) {
if (newValue && !isValidInput(newValue)) {
event.preventDefault() event.preventDefault()
} }
else { else {
validate(this.getAmount(newValue))
this.setState({ value: newValue }) this.setState({ value: newValue })
} }
}, },
onBlur: event => !readOnly && handleChange(this.getAmount(event.target.value.split(' ')[0])), onBlur: event => !readOnly && handleChange(this.getAmount(event.target.value)),
onKeyUp: event => {
if (!readOnly) {
validate(toHexWei(value || initValueToRender))
resetCaretIfPastEnd(value || initValueToRender, event)
}
},
onClick: event => !readOnly && resetCaretIfPastEnd(value || initValueToRender, event),
}), }),
h('span.currency-display__currency-symbol', primaryCurrency),
]), ]),
]), ]),

@ -3,12 +3,19 @@ const { multiplyCurrencies } = require('../../conversion-util')
const MIN_GAS_PRICE_GWEI = '1' const MIN_GAS_PRICE_GWEI = '1'
const GWEI_FACTOR = '1e9' const GWEI_FACTOR = '1e9'
const MIN_GAS_PRICE = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, { const MIN_GAS_PRICE_HEX = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
multiplicandBase: 16, multiplicandBase: 16,
multiplierBase: 16, multiplierBase: 16,
toNumericBase: 'hex',
})
const MIN_GAS_PRICE_DEC = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
multiplicandBase: 16,
multiplierBase: 16,
toNumericBase: 'dec',
}) })
const MIN_GAS_LIMIT = (21000).toString(16) const MIN_GAS_LIMIT_HEX = (21000).toString(16)
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT, MIN_GAS_PRICE, { const MIN_GAS_LIMIT_DEC = 21000
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
toNumericBase: 'hex', toNumericBase: 'hex',
multiplicandBase: 16, multiplicandBase: 16,
multiplierBase: 16, multiplierBase: 16,
@ -16,8 +23,9 @@ const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT, MIN_GAS_PRICE, {
module.exports = { module.exports = {
MIN_GAS_PRICE_GWEI, MIN_GAS_PRICE_GWEI,
GWEI_FACTOR, MIN_GAS_PRICE_HEX,
MIN_GAS_PRICE, MIN_GAS_PRICE_DEC,
MIN_GAS_LIMIT, MIN_GAS_LIMIT_HEX,
MIN_GAS_LIMIT_DEC,
MIN_GAS_TOTAL, MIN_GAS_TOTAL,
} }

@ -0,0 +1,39 @@
const { addCurrencies, conversionGreaterThan } = require('../../conversion-util')
function isBalanceSufficient({
amount,
gasTotal,
balance,
primaryCurrency,
selectedToken,
amountConversionRate,
conversionRate,
}) {
const totalAmount = addCurrencies(amount, gasTotal, {
aBase: 16,
bBase: 16,
toNumericBase: 'hex',
})
const balanceIsSufficient = conversionGreaterThan(
{
value: balance,
fromNumericBase: 'hex',
fromCurrency: primaryCurrency,
conversionRate,
},
{
value: totalAmount,
fromNumericBase: 'hex',
conversionRate: amountConversionRate,
fromCurrency: selectedToken || primaryCurrency,
conversionRate: amountConversionRate,
},
)
return balanceIsSufficient
}
module.exports = {
isBalanceSufficient,
}

@ -17,6 +17,7 @@ const {
getAddressBook, getAddressBook,
getSendFrom, getSendFrom,
getCurrentCurrency, getCurrentCurrency,
getSelectedTokenToFiatRate,
} = require('../../selectors') } = require('../../selectors')
module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther) module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther)
@ -26,7 +27,6 @@ function mapStateToProps (state) {
const selectedAddress = getSelectedAddress(state) const selectedAddress = getSelectedAddress(state)
const selectedToken = getSelectedToken(state) const selectedToken = getSelectedToken(state)
const tokenExchangeRates = state.metamask.tokenExchangeRates const tokenExchangeRates = state.metamask.tokenExchangeRates
const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state)
const conversionRate = conversionRateSelector(state) const conversionRate = conversionRateSelector(state)
let data; let data;
@ -40,11 +40,7 @@ function mapStateToProps (state) {
primaryCurrency = selectedToken.symbol primaryCurrency = selectedToken.symbol
tokenToFiatRate = multiplyCurrencies( tokenToFiatRate = getSelectedTokenToFiatRate(state)
conversionRate,
selectedTokenExchangeRate,
{ toNumericBase: 'dec' }
)
} }
return { return {

@ -109,14 +109,15 @@ TxView.prototype.render = function () {
margin: '1em 0.9em', margin: '1em 0.9em',
alignItems: 'center', alignItems: 'center',
}, },
onClick: () => {
this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar()
},
}, [ }, [
h('div.fa.fa-bars', { h('div.fa.fa-bars', {
style: { style: {
fontSize: '1.3em', fontSize: '1.3em',
cursor: 'pointer',
},
onClick: () => {
this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar()
}, },
}, []), }, []),

@ -3,7 +3,8 @@ const connect = require('react-redux').connect
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const Identicon = require('./identicon') const Identicon = require('./identicon')
const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns // const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns
const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions') const actions = require('../actions')
const BalanceComponent = require('./balance-component') const BalanceComponent = require('./balance-component')
const TokenList = require('./token-list') const TokenList = require('./token-list')
@ -19,6 +20,7 @@ function mapStateToProps (state) {
identities: state.metamask.identities, identities: state.metamask.identities,
accounts: state.metamask.accounts, accounts: state.metamask.accounts,
tokens: state.metamask.tokens, tokens: state.metamask.tokens,
keyrings: state.metamask.keyrings,
selectedAddress: selectors.getSelectedAddress(state), selectedAddress: selectors.getSelectedAddress(state),
selectedIdentity: selectors.getSelectedIdentity(state), selectedIdentity: selectors.getSelectedIdentity(state),
selectedAccount: selectors.getSelectedAccount(state), selectedAccount: selectors.getSelectedAccount(state),
@ -28,9 +30,13 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) { function mapDispatchToProps (dispatch) {
return { return {
showSendPage: () => { dispatch(actions.showSendPage()) }, showSendPage: () => dispatch(actions.showSendPage()),
hideSidebar: () => { dispatch(actions.hideSidebar()) }, hideSidebar: () => dispatch(actions.hideSidebar()),
unsetSelectedToken: () => dispatch(actions.setSelectedToken()), unsetSelectedToken: () => dispatch(actions.setSelectedToken()),
showAccountDetailModal: () => {
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
},
showAddTokenPage: () => dispatch(actions.showAddTokenPage()),
} }
} }
@ -47,7 +53,7 @@ WalletView.prototype.renderWalletBalance = function () {
hideSidebar, hideSidebar,
sidebarOpen, sidebarOpen,
} = this.props } = this.props
console.log({ selectedAccount })
const selectedClass = selectedTokenAddress const selectedClass = selectedTokenAddress
? '' ? ''
: 'wallet-balance-wrapper--active' : 'wallet-balance-wrapper--active'
@ -73,13 +79,25 @@ WalletView.prototype.renderWalletBalance = function () {
WalletView.prototype.render = function () { WalletView.prototype.render = function () {
const { const {
network, responsiveDisplayClassname, identities, responsiveDisplayClassname,
selectedAddress, accounts, selectedAddress,
selectedIdentity, selectedIdentity,
keyrings,
showAccountDetailModal,
hideSidebar,
showAddTokenPage,
} = this.props } = this.props
// temporary logs + fake extra wallets // temporary logs + fake extra wallets
// console.log('walletview, selectedAccount:', selectedAccount) // console.log('walletview, selectedAccount:', selectedAccount)
const keyring = keyrings.find((kr) => {
return kr.accounts.includes(selectedAddress) ||
kr.accounts.includes(selectedIdentity.address)
})
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
return h('div.wallet-view.flex-column' + (responsiveDisplayClassname || ''), { return h('div.wallet-view.flex-column' + (responsiveDisplayClassname || ''), {
style: {}, style: {},
}, [ }, [
@ -88,57 +106,16 @@ WalletView.prototype.render = function () {
h('div.flex-column.wallet-view-account-details', { h('div.flex-column.wallet-view-account-details', {
style: {}, style: {},
}, [ }, [
h('div.wallet-view__sidebar-close', {
onClick: hideSidebar,
}),
h('div.flex-row.account-options-menu', { h('div.wallet-view__keyring-label', isLoose ? 'IMPORTED' : ''),
style: {
position: 'relative',
},
}, [
h(AccountDropdowns, { h('div.flex-column.flex-center.wallet-view__name-container', {
selected: selectedAddress, style: { margin: '0 auto' },
network, onClick: showAccountDetailModal,
identities,
useCssTransition: true,
enableAccountOptions: true,
dropdownWrapperStyle: {
padding: '1px 15px',
marginLeft: '-25px',
position: 'absolute',
width: '122%', // TODO, refactor all of this component out into media queries
},
menuItemStyles: {
padding: '0px 0px',
margin: '22px 0px',
},
}, []),
]),
h('div.flex-column.flex-center', {
}, [ }, [
h('div', {
style: {
position: 'relative',
},
}, [
h(AccountDropdowns, {
accounts,
style: {
position: 'absolute',
left: 'calc(50% + 28px + 5.5px)',
top: '14px',
},
innerStyle: {
padding: '10px 16px',
},
useCssTransition: true,
selected: selectedAddress,
network,
identities,
}, []),
]),
h(Identicon, { h(Identicon, {
diameter: 54, diameter: 54,
address: selectedAddress, address: selectedAddress,
@ -150,13 +127,20 @@ WalletView.prototype.render = function () {
selectedIdentity.name, selectedIdentity.name,
]), ]),
h('button.wallet-view__details-button', 'DETAILS'),
]), ]),
]), ]),
h('div.wallet-view__address', { onClick: () => copyToClipboard(selectedAddress) }, [
`${selectedAddress.slice(0, 4)}...${selectedAddress.slice(-4)}`,
h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }),
]),
// 'Wallet' - Title // 'Wallet' - Title
// Not visible on mobile // Not visible on mobile
h('div.flex-column.wallet-view-title-wrapper', {}, [ h('div.flex-column.wallet-view-title-wrapper', [
h('span.wallet-view-title', {}, [ h('span.wallet-view-title', [
'Wallet', 'Wallet',
]), ]),
]), ]),
@ -165,6 +149,9 @@ WalletView.prototype.render = function () {
h(TokenList), h(TokenList),
h('button.wallet-view__add-token-button', {
onClick: showAddTokenPage,
}, 'Add Token'),
]) ])
} }

@ -65,6 +65,8 @@
.keyring-label { .keyring-label {
margin-top: 5px; margin-top: 5px;
background-color: $black;
color: $dusty-gray;
} }
} }
@ -88,9 +90,20 @@
&__check-mark { &__check-mark {
width: 14px; width: 14px;
margin-right: 12px;
flex: 0 0 auto; flex: 0 0 auto;
} }
&__check-mark-icon {
background-image: url("images/check-white.svg");
height: 18px;
width: 18px;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
margin: 3px 0;
}
.identicon { .identicon {
margin: 0 12px 0 0; margin: 0 12px 0 0;
flex: 0 0 auto; flex: 0 0 auto;

@ -109,7 +109,18 @@
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background-color: $gallery; background-color: rgba(0, 0, 0, .05);
}
&:active {
background-color: rgba(0, 0, 0, .1);
}
.fa {
position: absolute;
right: 24px;
font-size: 24px;
line-height: 24px;
} }
} }

@ -37,7 +37,7 @@
overflow-y: auto; overflow-y: auto;
top: 0; top: 0;
box-shadow: none; box-shadow: none;
height: calc(100vh - 58px - 100px); height: calc(100vh - 58px - 85px);
border-top-left-radius: 0; border-top-left-radius: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
} }
@ -271,6 +271,7 @@ section .confirm-screen-account-number,
box-shadow: none; box-shadow: none;
flex: 1 0 auto; flex: 1 0 auto;
font-weight: 300; font-weight: 300;
margin: 0 8px;
} }
.btn-light.confirm-screen-cancel-button { .btn-light.confirm-screen-cancel-button {
@ -288,6 +289,7 @@ section .confirm-screen-account-number,
cursor: pointer; cursor: pointer;
flex: 1 0 auto; flex: 1 0 auto;
font-weight: 300; font-weight: 300;
margin: 0 8px;
} }
#pending-tx-form { #pending-tx-form {
@ -296,7 +298,7 @@ section .confirm-screen-account-number,
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
background-color: $white; background-color: $white;
padding: 19px 18px; padding: 12px 18px;
border-bottom-left-radius: 8px; border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px; border-bottom-right-radius: 8px;
width: 100%; width: 100%;

@ -22,6 +22,7 @@
line-height: 22px; line-height: 22px;
border: none; border: none;
outline: 0 !important; outline: 0 !important;
max-width: 100%;
} }
&__primary-currency { &__primary-currency {
@ -43,4 +44,13 @@
font-size: 12px; font-size: 12px;
line-height: 12px; line-height: 12px;
} }
&__input-wrapper {
position: relative;
display: flex;
}
&__currency-symbol {
margin-top: 1px;
}
} }

@ -372,6 +372,7 @@
resize: none; resize: none;
padding: 9px 13px 8px; padding: 9px 13px 8px;
text-transform: uppercase; text-transform: uppercase;
font-weight: 300;
} }

@ -1,3 +1,12 @@
.network-component--disabled {
border-color: transparent !important;
cursor: default;
.fa-caret-down {
opacity: 0;
}
}
.network-component.pointer { .network-component.pointer {
border: 1px solid $shark; border: 1px solid $shark;
border-radius: 82px; border-radius: 82px;
@ -40,7 +49,7 @@
.dropdown-menu-item { .dropdown-menu-item {
.menu-icon-circle, .menu-icon-circle,
.menu-icon-circle--active { .menu-icon-circle--active {
margin: 0 16px; margin: 0 14px;
} }
} }
@ -116,8 +125,8 @@
.menu-icon-circle div, .menu-icon-circle div,
.menu-icon-circle--active div { .menu-icon-circle--active div {
height: 17px; height: 12px;
width: 17px; width: 12px;
border-radius: 17px; border-radius: 17px;
} }

@ -45,6 +45,8 @@ $wallet-view-bg: $wild-sand;
flex-direction: column; flex-direction: column;
flex: 33.5 0 33.5%; flex: 33.5 0 33.5%;
background: $wallet-view-bg; background: $wallet-view-bg;
z-index: 200;
position: relative;
@media screen and (min-width: 576px) { @media screen and (min-width: 576px) {
overflow-y: scroll; overflow-y: scroll;
@ -52,7 +54,77 @@ $wallet-view-bg: $wild-sand;
} }
.wallet-view-account-details { .wallet-view-account-details {
flex: 0 0 150px; flex: 0 0 auto;
}
&__name-container {
flex: 0 0 auto;
cursor: pointer;
}
&__keyring-label {
height: 40px;
color: $dusty-gray;
font-family: Roboto;
font-size: 10px;
line-height: 40px;
text-align: right;
padding: 0 20px;
}
&__details-button {
color: $curious-blue;
font-size: 10px;
line-height: 13px;
text-align: center;
border: 1px solid $curious-blue;
border-radius: 10.5px;
background-color: transparent;
margin: 0 auto;
padding: 4px 12px;
flex: 0 0 auto;
}
&__address {
border-radius: 3px;
background-color: $alto;
color: $scorpion;
font-size: 14px;
line-height: 12px;
padding: 4px 12px;
margin: 24px auto;
font-weight: 300;
cursor: pointer;
flex: 0 0 auto;
}
&__sidebar-close {
@media screen and (max-width: 575px) {
&::after {
content: '\00D7';
font-size: 40px;
color: $tundora;
position: absolute;
top: 12px;
left: 12px;
cursor: pointer;
}
}
}
&__add-token-button {
flex: 0 0 auto;
color: $dusty-gray;
font-size: 14px;
line-height: 19px;
text-align: center;
margin: 36px auto;
border: 1px solid $dusty-gray;
border-radius: 2px;
font-weight: 300;
background: none;
padding: 9px 30px;
} }
} }
@ -173,15 +245,12 @@ $wallet-view-bg: $wild-sand;
// wallet view // wallet view
.account-name { .account-name {
font-size: 24px;
@media screen and (max-width: 575px) { font-weight: 200;
font-size: 102%; line-height: 20px;
margin-left: 3%; color: $scorpion;
} margin-top: 8px;
margin-bottom: 24px;
@media screen and (max-width: 575px) {
text-align: center;
}
} }
// account options dropdown // account options dropdown

@ -657,15 +657,14 @@
width: 163px; width: 163px;
text-align: center; text-align: center;
height: 55px; height: 55px;
width: 163px;
border-radius: 2px; border-radius: 2px;
background-color: $white; background-color: $white;
font-family: Roboto; font-family: Roboto;
font-size: 16px; font-size: 16px;
font-weight: 300; font-weight: 300;
line-height: 21px; line-height: 21px;
text-align: center;
border: 1px solid; border: 1px solid;
margin: 0 4px;
} }
&__next-btn, &__next-btn,
@ -738,6 +737,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
font-size: 22px; font-size: 22px;
position: relative;
} }
&__buttons { &__buttons {
@ -747,7 +747,7 @@
margin-right: 21.25px; margin-right: 21.25px;
} }
&__revert, &__cancel, &__save { &__revert, &__cancel, &__save, &__save__error {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -760,7 +760,7 @@
margin-left: 21.25px; margin-left: 21.25px;
} }
&__cancel, &__save { &__cancel, &__save, &__save__error {
height: 34.64px; height: 34.64px;
width: 85.74px; width: 85.74px;
border: 1px solid $dusty-gray; border: 1px solid $dusty-gray;
@ -769,6 +769,21 @@
font-size: 12px; font-size: 12px;
color: $dusty-gray; color: $dusty-gray;
} }
&__save__error {
opacity: 0.5;
cursor: auto;
}
&__error-message {
display: block;
position: absolute;
top: 4px;
right: 4px;
font-size: 12px;
line-height: 12px;
color: $red;
}
} }
&__gas-modal-card { &__gas-modal-card {

@ -38,6 +38,7 @@
@media screen and (max-width: 575px) { @media screen and (max-width: 575px) {
flex-direction: column; flex-direction: column;
padding: 10px 0;
} }
} }
@ -48,6 +49,11 @@
flex-direction: column; flex-direction: column;
padding: 0 5px; padding: 0 5px;
height: 71px; height: 71px;
@media screen and (max-width: 575px) {
height: initial;
padding: 5px 0;
}
} }
.settings__content-item-col { .settings__content-item-col {
@ -71,6 +77,7 @@
padding-left: 10px; padding-left: 10px;
font-size: 14px; font-size: 14px;
height: 40px; height: 40px;
border: 1px solid $alto;
} }
.settings__input::-webkit-input-placeholder { .settings__input::-webkit-input-placeholder {

@ -67,19 +67,21 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
position: fixed; position: fixed;
margin-top: 20px; margin-top: 20px;
margin-left: 105px; margin-left: 105px;
z-index: 2000;
&__close-area { &__close-area {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
z-index: 1000; z-index: 2100;
width: 100%; width: 100%;
height: 100%; height: 100%;
cursor: default;
} }
&__container { &__container {
padding: 16px 34px 32px; padding: 16px 34px 32px;
z-index: 1050; z-index: 2200;
position: relative; position: relative;
} }

@ -237,7 +237,6 @@ hr.horizontal-line {
color: #fff; color: #fff;
border-radius: 10px; border-radius: 10px;
padding: 4px; padding: 4px;
width: 41px;
text-align: center; text-align: center;
height: 15px; height: 15px;
} }

@ -1,5 +1,9 @@
const valuesFor = require('./util').valuesFor const valuesFor = require('./util').valuesFor
const {
multiplyCurrencies,
} = require('./conversion-util')
const selectors = { const selectors = {
getSelectedAddress, getSelectedAddress,
getSelectedIdentity, getSelectedIdentity,
@ -16,6 +20,8 @@ const selectors = {
getAddressBook, getAddressBook,
getSendFrom, getSendFrom,
getCurrentCurrency, getCurrentCurrency,
getSendAmount,
getSelectedTokenToFiatRate,
} }
module.exports = selectors module.exports = selectors
@ -123,6 +129,23 @@ function getSendFrom (state) {
return state.metamask.send.from return state.metamask.send.from
} }
function getSendAmount (state) {
return state.metamask.send.amount
}
function getCurrentCurrency (state) { function getCurrentCurrency (state) {
return state.metamask.currentCurrency return state.metamask.currentCurrency
} }
function getSelectedTokenToFiatRate (state) {
const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state)
const conversionRate = conversionRateSelector(state)
const tokenToFiatRate = multiplyCurrencies(
conversionRate,
selectedTokenExchangeRate,
{ toNumericBase: 'dec' }
)
return tokenToFiatRate
}

@ -2,6 +2,7 @@ const { inherits } = require('util')
const PersistentForm = require('../lib/persistent-form') const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const connect = require('react-redux').connect const connect = require('react-redux').connect
const classnames = require('classnames')
const Identicon = require('./components/identicon') const Identicon = require('./components/identicon')
const FromDropdown = require('./components/send/from-dropdown') const FromDropdown = require('./components/send/from-dropdown')
@ -19,6 +20,9 @@ const {
conversionGreaterThan, conversionGreaterThan,
addCurrencies, addCurrencies,
} = require('./conversion-util') } = require('./conversion-util')
const {
isBalanceSufficient,
} = require('./components/send/send-utils.js')
const { isValidAddress } = require('./util') const { isValidAddress } = require('./util')
module.exports = SendTransactionScreen module.exports = SendTransactionScreen
@ -237,28 +241,16 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
let amountError = null let amountError = null
const totalAmount = addCurrencies(amount, gasTotal, { const sufficientBalance = isBalanceSufficient({
aBase: 16, amount,
bBase: 16, gasTotal,
toNumericBase: 'hex', balance,
primaryCurrency,
selectedToken,
amountConversionRate,
conversionRate,
}) })
const sufficientBalance = conversionGreaterThan(
{
value: balance,
fromNumericBase: 'hex',
fromCurrency: primaryCurrency,
conversionRate,
},
{
value: totalAmount,
fromNumericBase: 'hex',
conversionRate: amountConversionRate,
fromCurrency: selectedToken || primaryCurrency,
conversionRate: amountConversionRate,
},
)
const amountLessThanZero = conversionGreaterThan( const amountLessThanZero = conversionGreaterThan(
{ value: 0, fromNumericBase: 'dec' }, { value: 0, fromNumericBase: 'dec' },
{ value: amount, fromNumericBase: 'hex' }, { value: amount, fromNumericBase: 'hex' },
@ -399,7 +391,6 @@ SendTransactionScreen.prototype.renderFooter = function () {
}, },
}, 'Cancel'), }, 'Cancel'),
h(`button.send-v2__next-btn${errorClass}`, { h(`button.send-v2__next-btn${errorClass}`, {
onClick: event => noErrors && this.onSubmit(event),
}, 'Next'), }, 'Next'),
]) ])
} }

@ -208,7 +208,7 @@ class Settings extends Component {
h('div.settings__content', [ h('div.settings__content', [
warning && h('div.settings__error', warning), warning && h('div.settings__error', warning),
this.renderCurrentConversion(), this.renderCurrentConversion(),
this.renderCurrentProvider(), // this.renderCurrentProvider(),
this.renderNewRpcUrl(), this.renderNewRpcUrl(),
this.renderStateLogs(), this.renderStateLogs(),
this.renderSeedWords(), this.renderSeedWords(),

@ -3450,9 +3450,9 @@ eth-json-rpc-middleware@^1.0.0, eth-json-rpc-middleware@^1.2.7:
promise-to-callback "^1.0.0" promise-to-callback "^1.0.0"
tape "^4.6.3" tape "^4.6.3"
eth-keyring-controller@^2.1.0: eth-keyring-controller@^2.1.2:
version "2.1.1" version "2.1.2"
resolved "https://registry.yarnpkg.com/eth-keyring-controller/-/eth-keyring-controller-2.1.1.tgz#08129c8300f0ac6de9110e0b8d51292b5c6327e3" resolved "https://registry.yarnpkg.com/eth-keyring-controller/-/eth-keyring-controller-2.1.2.tgz#1af179d8fd7ff470eb91e113a0fd3a440bd66bcc"
dependencies: dependencies:
bip39 "^2.4.0" bip39 "^2.4.0"
bluebird "^3.5.0" bluebird "^3.5.0"
@ -8540,14 +8540,12 @@ samsam@1.x, samsam@^1.1.3:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.2.1.tgz#edd39093a3184370cb859243b2bdf255e7d8ea67" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.2.1.tgz#edd39093a3184370cb859243b2bdf255e7d8ea67"
sandwich-expando@^1.0.5: sandwich-expando@^1.1.3:
version "1.1.1" version "1.1.3"
resolved "https://registry.yarnpkg.com/sandwich-expando/-/sandwich-expando-1.1.1.tgz#83806fcca2375af8b6c30e6f52ed4f989debb165" resolved "https://registry.yarnpkg.com/sandwich-expando/-/sandwich-expando-1.1.3.tgz#6ba78d034c32f8bf5ab5934c214f8384614a88a5"
dependencies: dependencies:
babel-preset-es2015 "^6.6.0" babel-preset-es2015 "^6.6.0"
babelify "^7.3.0" babelify "^7.3.0"
brfs "^1.4.3"
raphael "^2.2.0"
react "^15.0.2" react "^15.0.2"
react-dom "^15.0.2" react-dom "^15.0.2"
react-hyperscript "^2.4.0" react-hyperscript "^2.4.0"

Loading…
Cancel
Save