aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorbrunobar79 <brunobar79@gmail.com>2018-07-02 05:41:34 +0800
committerbrunobar79 <brunobar79@gmail.com>2018-07-02 05:41:34 +0800
commit1494cc5e6c5943095c89e3b243b0a6152876e72c (patch)
treee34e74e79e8d41ce5903cc8d6eedf5caa19bc354 /ui
parent6e563acd93cbaf20fb233a267104fc6af3384287 (diff)
parentb2e64f24ecbc9e309869e678254cf755ffe11b40 (diff)
downloadtangerine-wallet-browser-1494cc5e6c5943095c89e3b243b0a6152876e72c.tar
tangerine-wallet-browser-1494cc5e6c5943095c89e3b243b0a6152876e72c.tar.gz
tangerine-wallet-browser-1494cc5e6c5943095c89e3b243b0a6152876e72c.tar.bz2
tangerine-wallet-browser-1494cc5e6c5943095c89e3b243b0a6152876e72c.tar.lz
tangerine-wallet-browser-1494cc5e6c5943095c89e3b243b0a6152876e72c.tar.xz
tangerine-wallet-browser-1494cc5e6c5943095c89e3b243b0a6152876e72c.tar.zst
tangerine-wallet-browser-1494cc5e6c5943095c89e3b243b0a6152876e72c.zip
fix merge conflicts
Diffstat (limited to 'ui')
-rw-r--r--ui/app/actions.js21
-rw-r--r--ui/app/app.js4
-rw-r--r--ui/app/components/customize-gas-modal/index.js48
-rw-r--r--ui/app/components/dropdowns/token-menu-dropdown.js53
-rw-r--r--ui/app/components/ens-input.js77
-rw-r--r--ui/app/components/identicon.js1
-rw-r--r--ui/app/components/input-number.js12
-rw-r--r--ui/app/components/pages/home.js8
-rw-r--r--ui/app/components/pages/notice.js12
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js2
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js2
-rw-r--r--ui/app/components/send/currency-display.js9
-rw-r--r--ui/app/components/send/gas-fee-display-v2.js53
-rw-r--r--ui/app/components/send_/account-list-item/account-list-item.container.js4
-rw-r--r--ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js2
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js17
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js4
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js9
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js2
-rw-r--r--ui/app/components/send_/send-content/send-content.component.js2
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js61
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js1
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js55
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js2
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js4
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js2
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js2
-rw-r--r--ui/app/components/send_/send-content/send-to-row/send-to-row.component.js6
-rw-r--r--ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js6
-rw-r--r--ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js12
-rw-r--r--ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js6
-rw-r--r--ui/app/components/send_/send.component.js8
-rw-r--r--ui/app/components/send_/send.constants.js2
-rw-r--r--ui/app/components/send_/send.container.js2
-rw-r--r--ui/app/components/send_/send.selectors.js5
-rw-r--r--ui/app/components/send_/send.utils.js49
-rw-r--r--ui/app/components/send_/tests/send-component.test.js14
-rw-r--r--ui/app/components/send_/tests/send-container.test.js2
-rw-r--r--ui/app/components/send_/tests/send-selectors.test.js10
-rw-r--r--ui/app/components/send_/tests/send-utils.test.js48
-rw-r--r--ui/app/components/shapeshift-form.js2
-rw-r--r--ui/app/components/signature-request.js12
-rw-r--r--ui/app/components/token-balance.js2
-rw-r--r--ui/app/conversion-util.js11
-rw-r--r--ui/app/css/itcss/components/currency-display.scss15
-rw-r--r--ui/app/css/itcss/components/hero-balance.scss15
-rw-r--r--ui/app/css/itcss/components/modal.scss31
-rw-r--r--ui/app/css/itcss/components/newui-sections.scss8
-rw-r--r--ui/app/css/itcss/components/request-signature.scss6
-rw-r--r--ui/app/css/itcss/components/token-list.scss6
-rw-r--r--ui/app/reducers/app.js11
-rw-r--r--ui/app/reducers/metamask.js4
-rw-r--r--ui/app/selectors.js5
-rw-r--r--ui/app/util.js5
54 files changed, 569 insertions, 203 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 758696203..c822ef3ee 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -177,6 +177,8 @@ var actions = {
CLEAR_SEND: 'CLEAR_SEND',
OPEN_FROM_DROPDOWN: 'OPEN_FROM_DROPDOWN',
CLOSE_FROM_DROPDOWN: 'CLOSE_FROM_DROPDOWN',
+ GAS_LOADING_STARTED: 'GAS_LOADING_STARTED',
+ GAS_LOADING_FINISHED: 'GAS_LOADING_FINISHED',
setGasLimit,
setGasPrice,
updateGasData,
@@ -192,6 +194,8 @@ var actions = {
updateSendErrors,
clearSend,
setSelectedAddress,
+ gasLoadingStarted,
+ gasLoadingFinished,
// app messages
confirmSeedWords: confirmSeedWords,
showAccountDetail: showAccountDetail,
@@ -782,8 +786,9 @@ function updateGasData ({
to,
value,
}) {
- const estimatedGasPrice = estimateGasPriceFromRecentBlocks(recentBlocks)
return (dispatch) => {
+ dispatch(actions.gasLoadingStarted())
+ const estimatedGasPrice = estimateGasPriceFromRecentBlocks(recentBlocks)
return Promise.all([
Promise.resolve(estimatedGasPrice),
estimateGas({
@@ -804,14 +809,28 @@ function updateGasData ({
.then((gasEstimate) => {
dispatch(actions.setGasTotal(gasEstimate))
dispatch(updateSendErrors({ gasLoadingError: null }))
+ dispatch(actions.gasLoadingFinished())
})
.catch(err => {
log.error(err)
dispatch(updateSendErrors({ gasLoadingError: 'gasLoadingError' }))
+ dispatch(actions.gasLoadingFinished())
})
}
}
+function gasLoadingStarted () {
+ return {
+ type: actions.GAS_LOADING_STARTED,
+ }
+}
+
+function gasLoadingFinished () {
+ return {
+ type: actions.GAS_LOADING_FINISHED,
+ }
+}
+
function updateSendTokenBalance ({
selectedToken,
tokenContract,
diff --git a/ui/app/app.js b/ui/app/app.js
index ec2329463..d0e48a368 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -314,7 +314,7 @@ function mapStateToProps (state) {
noActiveNotices,
seedWords,
unapprovedTxs,
- lastUnreadNotice,
+ nextUnreadNotice,
lostAccounts,
unapprovedMsgCount,
unapprovedPersonalMsgCount,
@@ -348,7 +348,7 @@ function mapStateToProps (state) {
network: state.metamask.network,
provider: state.metamask.provider,
forgottenPassword: state.appState.forgottenPassword,
- lastUnreadNotice,
+ nextUnreadNotice,
lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [],
currentCurrency: state.metamask.currentCurrency,
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index c8522a3c7..cd8f76ed5 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -33,6 +33,7 @@ const {
const {
getGasPrice,
getGasLimit,
+ getGasIsLoading,
getForceGasMin,
conversionRateSelector,
getSendAmount,
@@ -51,6 +52,7 @@ function mapStateToProps (state) {
return {
gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state),
+ gasIsLoading: getGasIsLoading(state),
forceGasMin: getForceGasMin(state),
conversionRate,
amount: getSendAmount(state),
@@ -73,7 +75,7 @@ function mapDispatchToProps (dispatch) {
}
}
-function getOriginalState (props) {
+function getFreshState (props) {
const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
@@ -97,7 +99,11 @@ inherits(CustomizeGasModal, Component)
function CustomizeGasModal (props) {
Component.call(this)
- this.state = getOriginalState(props)
+ const originalState = getFreshState(props)
+ this.state = {
+ ...originalState,
+ originalState,
+ }
}
CustomizeGasModal.contextTypes = {
@@ -106,6 +112,36 @@ CustomizeGasModal.contextTypes = {
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal)
+CustomizeGasModal.prototype.componentWillReceiveProps = function (nextProps) {
+ const currentState = getFreshState(this.props)
+ const {
+ gasPrice: currentGasPrice,
+ gasLimit: currentGasLimit,
+ } = currentState
+ const newState = getFreshState(nextProps)
+ const {
+ gasPrice: newGasPrice,
+ gasLimit: newGasLimit,
+ gasTotal: newGasTotal,
+ } = newState
+ const gasPriceChanged = currentGasPrice !== newGasPrice
+ const gasLimitChanged = currentGasLimit !== newGasLimit
+
+ if (gasPriceChanged) {
+ this.setState({
+ gasPrice: newGasPrice,
+ gasTotal: newGasTotal,
+ priceSigZeros: '',
+ priceSigDec: '',
+ })
+ }
+ if (gasLimitChanged) {
+ this.setState({ gasLimit: newGasLimit, gasTotal: newGasTotal })
+ }
+ if (gasLimitChanged || gasPriceChanged) {
+ this.validate({ gasLimit: newGasLimit, gasTotal: newGasTotal })
+ }
+}
CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
const {
@@ -137,7 +173,7 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
}
CustomizeGasModal.prototype.revert = function () {
- this.setState(getOriginalState(this.props))
+ this.setState(this.state.originalState)
}
CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
@@ -233,7 +269,7 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
}
CustomizeGasModal.prototype.render = function () {
- const { hideModal, forceGasMin } = this.props
+ const { hideModal, forceGasMin, gasIsLoading } = this.props
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
let convertedGasPrice = conversionUtil(gasPrice, {
@@ -266,7 +302,7 @@ CustomizeGasModal.prototype.render = function () {
toNumericBase: 'dec',
})
- return h('div.send-v2__customize-gas', {}, [
+ return !gasIsLoading && h('div.send-v2__customize-gas', {}, [
h('div.send-v2__customize-gas__content', {
}, [
h('div.send-v2__customize-gas__header', {}, [
@@ -288,6 +324,7 @@ CustomizeGasModal.prototype.render = function () {
onChange: value => this.convertAndSetGasPrice(value),
title: this.context.t('gasPrice'),
copy: this.context.t('gasPriceCalculation'),
+ gasIsLoading,
}),
h(GasModalCard, {
@@ -297,6 +334,7 @@ CustomizeGasModal.prototype.render = function () {
onChange: value => this.convertAndSetGasLimit(value),
title: this.context.t('gasLimit'),
copy: this.context.t('gasLimitCalculation'),
+ gasIsLoading,
}),
]),
diff --git a/ui/app/components/dropdowns/token-menu-dropdown.js b/ui/app/components/dropdowns/token-menu-dropdown.js
index b70d0b893..fac7c451b 100644
--- a/ui/app/components/dropdowns/token-menu-dropdown.js
+++ b/ui/app/components/dropdowns/token-menu-dropdown.js
@@ -4,14 +4,21 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
-
+const genAccountLink = require('etherscan-link').createAccountLink
+const copyToClipboard = require('copy-to-clipboard')
+const { Menu, Item, CloseArea } = require('./components/menu')
TokenMenuDropdown.contextTypes = {
t: PropTypes.func,
}
-module.exports = connect(null, mapDispatchToProps)(TokenMenuDropdown)
+module.exports = connect(mapStateToProps, mapDispatchToProps)(TokenMenuDropdown)
+function mapStateToProps (state) {
+ return {
+ network: state.metamask.network,
+ }
+}
function mapDispatchToProps (dispatch) {
return {
@@ -37,22 +44,34 @@ TokenMenuDropdown.prototype.onClose = function (e) {
TokenMenuDropdown.prototype.render = function () {
const { showHideTokenConfirmationModal } = this.props
- return h('div.token-menu-dropdown', {}, [
- h('div.token-menu-dropdown__close-area', {
+ return h(Menu, { className: 'token-menu-dropdown', isShowing: true }, [
+ h(CloseArea, {
onClick: this.onClose,
}),
- h('div.token-menu-dropdown__container', {}, [
- h('div.token-menu-dropdown__options', {}, [
-
- h('div.token-menu-dropdown__option', {
- onClick: (e) => {
- e.stopPropagation()
- showHideTokenConfirmationModal(this.props.token)
- this.props.onClose()
- },
- }, this.context.t('hideToken')),
-
- ]),
- ]),
+ h(Item, {
+ onClick: (e) => {
+ e.stopPropagation()
+ showHideTokenConfirmationModal(this.props.token)
+ this.props.onClose()
+ },
+ text: this.context.t('hideToken'),
+ }),
+ h(Item, {
+ onClick: (e) => {
+ e.stopPropagation()
+ copyToClipboard(this.props.token.address)
+ this.props.onClose()
+ },
+ text: this.context.t('copyContractAddress'),
+ }),
+ h(Item, {
+ onClick: (e) => {
+ e.stopPropagation()
+ const url = genAccountLink(this.props.token.address, this.props.network)
+ global.platform.openWindow({ url })
+ this.props.onClose()
+ },
+ text: this.context.t('viewOnEtherscan'),
+ }),
])
}
diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js
index aff4b6ef6..292dcdde6 100644
--- a/ui/app/components/ens-input.js
+++ b/ui/app/components/ens-input.js
@@ -12,6 +12,7 @@ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
const connect = require('react-redux').connect
const ToAutoComplete = require('./send/to-autocomplete')
const log = require('loglevel')
+const { isValidENSAddress } = require('../util')
EnsInput.contextTypes = {
t: PropTypes.func,
@@ -25,31 +26,34 @@ function EnsInput () {
Component.call(this)
}
-EnsInput.prototype.render = function () {
- const props = this.props
- const opts = extend(props, {
- list: 'addresses',
- onChange: (recipient) => {
- const network = this.props.network
- const networkHasEnsSupport = getNetworkEnsSupport(network)
+EnsInput.prototype.onChange = function (recipient) {
+ const network = this.props.network
+ const networkHasEnsSupport = getNetworkEnsSupport(network)
- props.onChange(recipient)
+ this.props.onChange({ toAddress: recipient })
- if (!networkHasEnsSupport) return
+ if (!networkHasEnsSupport) return
- if (recipient.match(ensRE) === null) {
- return this.setState({
- loadingEns: false,
- ensResolution: null,
- ensFailure: null,
- })
- }
+ if (recipient.match(ensRE) === null) {
+ return this.setState({
+ loadingEns: false,
+ ensResolution: null,
+ ensFailure: null,
+ toError: null,
+ })
+ }
- this.setState({
- loadingEns: true,
- })
- this.checkName(recipient)
- },
+ this.setState({
+ loadingEns: true,
+ })
+ this.checkName(recipient)
+}
+
+EnsInput.prototype.render = function () {
+ const props = this.props
+ const opts = extend(props, {
+ list: 'addresses',
+ onChange: this.onChange.bind(this),
})
return h('div', {
style: { width: '100%', position: 'relative' },
@@ -85,17 +89,27 @@ EnsInput.prototype.lookupEnsName = function (recipient) {
nickname: recipient.trim(),
hoverText: address + '\n' + this.context.t('clickCopy'),
ensFailure: false,
+ toError: null,
})
}
})
.catch((reason) => {
- log.error(reason)
- return this.setState({
+ const setStateObj = {
loadingEns: false,
- ensResolution: ZERO_ADDRESS,
+ ensResolution: recipient,
ensFailure: true,
- hoverText: reason.message,
- })
+ toError: null,
+ }
+ if (isValidENSAddress(recipient) && reason.message === 'ENS name not defined.') {
+ setStateObj.hoverText = this.context.t('ensNameNotFound')
+ setStateObj.toError = 'ensNameNotFound'
+ setStateObj.ensFailure = false
+ } else {
+ log.error(reason)
+ setStateObj.hoverText = reason.message
+ }
+
+ return this.setState(setStateObj)
})
}
@@ -105,9 +119,14 @@ EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
// If an address is sent without a nickname, meaning not from ENS or from
// the user's own accounts, a default of a one-space string is used.
const nickname = state.nickname || ' '
+ if (prevProps.network !== this.props.network) {
+ const provider = global.ethereumProvider
+ this.ens = new ENS({ provider, network: this.props.network })
+ this.onChange(ensResolution)
+ }
if (prevState && ensResolution && this.props.onChange &&
ensResolution !== prevState.ensResolution) {
- this.props.onChange(ensResolution, nickname)
+ this.props.onChange({ toAddress: ensResolution, nickname, toError: state.toError })
}
}
@@ -124,7 +143,9 @@ EnsInput.prototype.ensIcon = function (recipient) {
}
EnsInput.prototype.ensIconContents = function (recipient) {
- const { loadingEns, ensFailure, ensResolution } = this.state || { ensResolution: ZERO_ADDRESS}
+ const { loadingEns, ensFailure, ensResolution, toError } = this.state || { ensResolution: ZERO_ADDRESS }
+
+ if (toError) return
if (loadingEns) {
return h('img', {
diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js
index dce9b0449..424048745 100644
--- a/ui/app/components/identicon.js
+++ b/ui/app/components/identicon.js
@@ -36,6 +36,7 @@ IdenticonComponent.prototype.render = function () {
key: 'identicon-' + address,
style: {
display: 'flex',
+ flexShrink: 0,
alignItems: 'center',
justifyContent: 'center',
height: diameter,
diff --git a/ui/app/components/input-number.js b/ui/app/components/input-number.js
index de5fcca54..59c6842ef 100644
--- a/ui/app/components/input-number.js
+++ b/ui/app/components/input-number.js
@@ -22,12 +22,16 @@ function isValidInput (text) {
return re.test(text)
}
+function removeLeadingZeroes (str) {
+ return str.replace(/^0*(?=\d)/, '')
+}
+
InputNumber.prototype.setValue = function (newValue) {
+ newValue = removeLeadingZeroes(newValue)
if (newValue && !isValidInput(newValue)) return
const { fixed, min = -1, max = Infinity, onChange } = this.props
newValue = fixed ? newValue.toFixed(4) : newValue
-
const newValueGreaterThanMin = conversionGTE(
{ value: newValue || '0', fromNumericBase: 'dec' },
{ value: min, fromNumericBase: 'hex' },
@@ -47,7 +51,7 @@ InputNumber.prototype.setValue = function (newValue) {
}
InputNumber.prototype.render = function () {
- const { unitLabel, step = 1, placeholder, value = 0 } = this.props
+ const { unitLabel, step = 1, placeholder, value } = this.props
return h('div.customize-gas-input-wrapper', {}, [
h('input', {
@@ -63,11 +67,11 @@ InputNumber.prototype.render = function () {
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
h('div.gas-tooltip-input-arrows', {}, [
h('i.fa.fa-angle-up', {
- onClick: () => this.setValue(addCurrencies(value, step)),
+ onClick: () => this.setValue(addCurrencies(value, step, { toNumericBase: 'dec' })),
}),
h('i.fa.fa-angle-down', {
style: { cursor: 'pointer' },
- onClick: () => this.setValue(subtractCurrencies(value, step)),
+ onClick: () => this.setValue(subtractCurrencies(value, step, { toNumericBase: 'dec' })),
}),
]),
])
diff --git a/ui/app/components/pages/home.js b/ui/app/components/pages/home.js
index 9110f8202..c53413d3b 100644
--- a/ui/app/components/pages/home.js
+++ b/ui/app/components/pages/home.js
@@ -86,9 +86,9 @@ class Home extends Component {
// if (!props.noActiveNotices) {
// log.debug('rendering notice screen for unread notices.')
// return h(NoticeScreen, {
- // notice: props.lastUnreadNotice,
+ // notice: props.nextUnreadNotice,
// key: 'NoticeScreen',
- // onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
+ // onConfirm: () => props.dispatch(actions.markNoticeRead(props.nextUnreadNotice)),
// })
// } else if (props.lostAccounts && props.lostAccounts.length > 0) {
// log.debug('rendering notice screen for lost accounts view.')
@@ -279,7 +279,7 @@ function mapStateToProps (state) {
noActiveNotices,
seedWords,
unapprovedTxs,
- lastUnreadNotice,
+ nextUnreadNotice,
lostAccounts,
unapprovedMsgCount,
unapprovedPersonalMsgCount,
@@ -313,7 +313,7 @@ function mapStateToProps (state) {
network: state.metamask.network,
provider: state.metamask.provider,
forgottenPassword: state.appState.forgottenPassword,
- lastUnreadNotice,
+ nextUnreadNotice,
lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [],
currentCurrency: state.metamask.currentCurrency,
diff --git a/ui/app/components/pages/notice.js b/ui/app/components/pages/notice.js
index 2329a9147..a9077b98b 100644
--- a/ui/app/components/pages/notice.js
+++ b/ui/app/components/pages/notice.js
@@ -154,11 +154,11 @@ class Notice extends Component {
const mapStateToProps = state => {
const { metamask } = state
- const { noActiveNotices, lastUnreadNotice, lostAccounts } = metamask
+ const { noActiveNotices, nextUnreadNotice, lostAccounts } = metamask
return {
noActiveNotices,
- lastUnreadNotice,
+ nextUnreadNotice,
lostAccounts,
}
}
@@ -171,21 +171,21 @@ Notice.propTypes = {
const mapDispatchToProps = dispatch => {
return {
- markNoticeRead: lastUnreadNotice => dispatch(actions.markNoticeRead(lastUnreadNotice)),
+ markNoticeRead: nextUnreadNotice => dispatch(actions.markNoticeRead(nextUnreadNotice)),
markAccountsFound: () => dispatch(actions.markAccountsFound()),
}
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
- const { noActiveNotices, lastUnreadNotice, lostAccounts } = stateProps
+ const { noActiveNotices, nextUnreadNotice, lostAccounts } = stateProps
const { markNoticeRead, markAccountsFound } = dispatchProps
let notice
let onConfirm
if (!noActiveNotices) {
- notice = lastUnreadNotice
- onConfirm = () => markNoticeRead(lastUnreadNotice)
+ notice = nextUnreadNotice
+ onConfirm = () => markNoticeRead(nextUnreadNotice)
} else if (lostAccounts && lostAccounts.length > 0) {
notice = generateLostAccountsNotice(lostAccounts)
onConfirm = () => markAccountsFound()
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index bbf5683f0..22b2670d8 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -20,7 +20,7 @@ const {
calcGasTotal,
isBalanceSufficient,
} = require('../send_/send.utils')
-const GasFeeDisplay = require('../send/gas-fee-display-v2')
+const GasFeeDisplay = require('../send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component').default
const SenderToRecipient = require('../sender-to-recipient')
const NetworkDisplay = require('../network-display')
const currencyFormatter = require('currency-formatter')
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index ee066b8f4..535347cee 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -11,7 +11,7 @@ abiDecoder.addABI(tokenAbi)
const actions = require('../../actions')
const clone = require('clone')
const Identicon = require('../identicon')
-const GasFeeDisplay = require('../send/gas-fee-display-v2.js')
+const GasFeeDisplay = require('../send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js').default
const NetworkDisplay = require('../network-display')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js
index 3bc9ad226..5e2c5fdf6 100644
--- a/ui/app/components/send/currency-display.js
+++ b/ui/app/components/send/currency-display.js
@@ -57,6 +57,7 @@ CurrencyDisplay.prototype.getValueToRender = function ({ selectedToken, conversi
return selectedToken
? conversionUtil(ethUtil.addHexPrefix(value), {
fromNumericBase: 'hex',
+ toNumericBase: 'dec',
toCurrency: symbol,
conversionRate: multiplier,
invertConversionRate: true,
@@ -91,8 +92,12 @@ CurrencyDisplay.prototype.getConvertedValueToRender = function (nonFormattedValu
: convertedValue
}
+function removeLeadingZeroes (str) {
+ return str.replace(/^0*(?=\d)/, '')
+}
+
CurrencyDisplay.prototype.handleChange = function (newVal) {
- this.setState({ valueToRender: newVal })
+ this.setState({ valueToRender: removeLeadingZeroes(newVal) })
this.props.onChange(this.getAmount(newVal))
}
@@ -113,6 +118,7 @@ CurrencyDisplay.prototype.render = function () {
readOnly = false,
inError = false,
onBlur,
+ step,
} = this.props
const { valueToRender } = this.state
@@ -147,6 +153,7 @@ CurrencyDisplay.prototype.render = function () {
width: this.getInputWidth(valueToRender, readOnly),
},
min: 0,
+ step,
}),
h('span.currency-display__currency-symbol', primaryCurrency),
diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js
deleted file mode 100644
index 1423aa84d..000000000
--- a/ui/app/components/send/gas-fee-display-v2.js
+++ /dev/null
@@ -1,53 +0,0 @@
-const Component = require('react').Component
-const PropTypes = require('prop-types')
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const CurrencyDisplay = require('./currency-display')
-const connect = require('react-redux').connect
-
-GasFeeDisplay.contextTypes = {
- t: PropTypes.func,
-}
-
-module.exports = connect()(GasFeeDisplay)
-
-
-inherits(GasFeeDisplay, Component)
-function GasFeeDisplay () {
- Component.call(this)
-}
-
-GasFeeDisplay.prototype.render = function () {
- const {
- conversionRate,
- gasTotal,
- onClick,
- primaryCurrency = 'ETH',
- convertedCurrency,
- gasLoadingError,
- } = this.props
-
- return h('div.send-v2__gas-fee-display', [
-
- gasTotal
- ? h(CurrencyDisplay, {
- primaryCurrency,
- convertedCurrency,
- value: gasTotal,
- conversionRate,
- convertedPrefix: '$',
- readOnly: true,
- })
- : gasLoadingError
- ? h('div.currency-display.currency-display--message', this.context.t('setGasPrice'))
- : h('div.currency-display', this.context.t('loading')),
-
- h('button.sliders-icon-container', {
- onClick,
- disabled: !gasTotal && !gasLoadingError,
- }, [
- h('i.fa.fa-sliders.sliders-icon'),
- ]),
-
- ])
-}
diff --git a/ui/app/components/send_/account-list-item/account-list-item.container.js b/ui/app/components/send_/account-list-item/account-list-item.container.js
index 3151b1f1d..4b4519288 100644
--- a/ui/app/components/send_/account-list-item/account-list-item.container.js
+++ b/ui/app/components/send_/account-list-item/account-list-item.container.js
@@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import {
getConversionRate,
- getConvertedCurrency,
+ getCurrentCurrency,
} from '../send.selectors.js'
import AccountListItem from './account-list-item.component'
@@ -10,6 +10,6 @@ export default connect(mapStateToProps)(AccountListItem)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
- currentCurrency: getConvertedCurrency(state),
+ currentCurrency: getCurrentCurrency(state),
}
}
diff --git a/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js b/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js
index 49da920e6..af0859117 100644
--- a/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js
+++ b/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js
@@ -12,7 +12,7 @@ proxyquire('../account-list-item.container.js', {
},
'../send.selectors.js': {
getConversionRate: (s) => `mockConversionRate:${s}`,
- getConvertedCurrency: (s) => `mockCurrentCurrency:${s}`,
+ getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`,
},
})
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
index 8aefeed4a..196538c11 100644
--- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
@@ -23,6 +23,7 @@ export default class SendAmountRow extends Component {
tokenBalance: PropTypes.string,
updateSendAmount: PropTypes.func,
updateSendAmountError: PropTypes.func,
+ updateGas: PropTypes.func,
}
validateAmount (amount) {
@@ -56,6 +57,14 @@ export default class SendAmountRow extends Component {
updateSendAmount(amount)
}
+ updateGas (amount) {
+ const { selectedToken, updateGas } = this.props
+
+ if (selectedToken) {
+ updateGas({ amount })
+ }
+ }
+
render () {
const {
amount,
@@ -77,12 +86,16 @@ export default class SendAmountRow extends Component {
<CurrencyDisplay
conversionRate={amountConversionRate}
convertedCurrency={convertedCurrency}
- onBlur={newAmount => this.updateAmount(newAmount)}
+ onBlur={newAmount => {
+ this.updateGas(newAmount)
+ this.updateAmount(newAmount)
+ }}
onChange={newAmount => this.validateAmount(newAmount)}
inError={inError}
primaryCurrency={primaryCurrency || 'ETH'}
selectedToken={selectedToken}
- value={amount || '0x0'}
+ value={amount}
+ step="any"
/>
</SendRowWrapper>
)
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
index bbbf56971..b816d948f 100644
--- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
@@ -2,7 +2,7 @@ import { connect } from 'react-redux'
import {
getAmountConversionRate,
getConversionRate,
- getConvertedCurrency,
+ getCurrentCurrency,
getGasTotal,
getPrimaryCurrency,
getSelectedToken,
@@ -31,7 +31,7 @@ function mapStateToProps (state) {
amountConversionRate: getAmountConversionRate(state),
balance: getSendFromBalance(state),
conversionRate: getConversionRate(state),
- convertedCurrency: getConvertedCurrency(state),
+ convertedCurrency: getCurrentCurrency(state),
gasTotal: getGasTotal(state),
inError: sendAmountIsInError(state),
primaryCurrency: getPrimaryCurrency(state),
diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
index 2205579ca..579e18585 100644
--- a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
+++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
@@ -12,10 +12,12 @@ const propsMethodSpies = {
setMaxModeTo: sinon.spy(),
updateSendAmount: sinon.spy(),
updateSendAmountError: sinon.spy(),
+ updateGas: sinon.spy(),
}
sinon.spy(SendAmountRow.prototype, 'updateAmount')
sinon.spy(SendAmountRow.prototype, 'validateAmount')
+sinon.spy(SendAmountRow.prototype, 'updateGas')
describe('SendAmountRow Component', function () {
let wrapper
@@ -36,6 +38,7 @@ describe('SendAmountRow Component', function () {
tokenBalance={'mockTokenBalance'}
updateSendAmount={propsMethodSpies.updateSendAmount}
updateSendAmountError={propsMethodSpies.updateSendAmountError}
+ updateGas={propsMethodSpies.updateGas}
/>, { context: { t: str => str + '_t' } })
instance = wrapper.instance()
})
@@ -139,8 +142,14 @@ describe('SendAmountRow Component', function () {
assert.equal(primaryCurrency, 'mockPrimaryCurrency')
assert.deepEqual(selectedToken, { address: 'mockTokenAddress' })
assert.equal(value, 'mockAmount')
+ assert.equal(SendAmountRow.prototype.updateGas.callCount, 0)
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 0)
onBlur('mockNewAmount')
+ assert.equal(SendAmountRow.prototype.updateGas.callCount, 1)
+ assert.deepEqual(
+ SendAmountRow.prototype.updateGas.getCall(0).args,
+ ['mockNewAmount']
+ )
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 1)
assert.deepEqual(
SendAmountRow.prototype.updateAmount.getCall(0).args,
diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
index e4c913c69..94d9918a7 100644
--- a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
+++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
@@ -24,7 +24,7 @@ proxyquire('../send-amount-row.container.js', {
'../../send.selectors': {
getAmountConversionRate: (s) => `mockAmountConversionRate:${s}`,
getConversionRate: (s) => `mockConversionRate:${s}`,
- getConvertedCurrency: (s) => `mockConvertedCurrency:${s}`,
+ getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`,
getGasTotal: (s) => `mockGasTotal:${s}`,
getPrimaryCurrency: (s) => `mockPrimaryCurrency:${s}`,
getSelectedToken: (s) => `mockSelectedToken:${s}`,
diff --git a/ui/app/components/send_/send-content/send-content.component.js b/ui/app/components/send_/send-content/send-content.component.js
index 3a14054eb..adc114c0e 100644
--- a/ui/app/components/send_/send-content/send-content.component.js
+++ b/ui/app/components/send_/send-content/send-content.component.js
@@ -18,7 +18,7 @@ export default class SendContent extends Component {
<div className="send-v2__form">
<SendFromRow />
<SendToRow updateGas={(updateData) => this.props.updateGas(updateData)} />
- <SendAmountRow />
+ <SendAmountRow updateGas={(updateData) => this.props.updateGas(updateData)} />
<SendGasRow />
</div>
</PageContainerContent>
diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js
new file mode 100644
index 000000000..b1fd67412
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js
@@ -0,0 +1,61 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import CurrencyDisplay from '../../../../send/currency-display'
+
+
+export default class GasFeeDisplay extends Component {
+
+ static propTypes = {
+ conversionRate: PropTypes.number,
+ primaryCurrency: PropTypes.string,
+ convertedCurrency: PropTypes.string,
+ gasLoadingError: PropTypes.bool,
+ gasTotal: PropTypes.string,
+ onClick: PropTypes.func,
+ };
+
+ render() {
+ const {
+ conversionRate,
+ gasTotal,
+ onClick,
+ primaryCurrency = 'ETH',
+ convertedCurrency,
+ gasLoadingError,
+ } = this.props
+
+ return (
+ <div className="send-v2__gas-fee-display">
+ {gasTotal
+ ? <CurrencyDisplay
+ primaryCurrency={primaryCurrency}
+ convertedCurrency={convertedCurrency}
+ value={gasTotal}
+ conversionRate={conversionRate}
+ gasLoadingError={gasLoadingError}
+ convertedPrefix={'$'}
+ readOnly
+ />
+ : gasLoadingError
+ ? <div className="currency-display.currency-display--message">
+ {this.context.t('setGasPrice')}
+ </div>
+ : <div className="currency-display">
+ {this.context.t('loading')}
+ </div>
+ }
+ <button
+ className="sliders-icon-container"
+ onClick={onClick}
+ disabled={!gasTotal && !gasLoadingError}
+ >
+ <i className="fa fa-sliders sliders-icon" />
+ </button>
+ </div>
+ )
+ }
+}
+
+GasFeeDisplay.contextTypes = {
+ t: PropTypes.func,
+}
diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js
new file mode 100644
index 000000000..dba0edb7b
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js
@@ -0,0 +1 @@
+export { default } from './gas-fee-display.component'
diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js
new file mode 100644
index 000000000..66f3a94df
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js
@@ -0,0 +1,55 @@
+import React from 'react'
+import assert from 'assert'
+import {shallow} from 'enzyme'
+import GasFeeDisplay from '../gas-fee-display.component'
+import CurrencyDisplay from '../../../../../send/currency-display'
+import sinon from 'sinon'
+
+
+const propsMethodSpies = {
+ showCustomizeGasModal: sinon.spy(),
+}
+
+describe('SendGasRow Component', function() {
+ let wrapper
+
+ beforeEach(() => {
+ wrapper = shallow(<GasFeeDisplay
+ conversionRate={20}
+ gasTotal={'mockGasTotal'}
+ onClick={propsMethodSpies.showCustomizeGasModal}
+ primaryCurrency={'mockPrimaryCurrency'}
+ convertedCurrency={'mockConvertedCurrency'}
+ />, {context: {t: str => str + '_t'}})
+ })
+
+ afterEach(() => {
+ propsMethodSpies.showCustomizeGasModal.resetHistory()
+ })
+
+ describe('render', () => {
+ it('should render a CurrencyDisplay component', () => {
+ assert.equal(wrapper.find(CurrencyDisplay).length, 1)
+ })
+
+ it('should render the CurrencyDisplay with the correct props', () => {
+ const {
+ conversionRate,
+ convertedCurrency,
+ value,
+ } = wrapper.find(CurrencyDisplay).props()
+ assert.equal(conversionRate, 20)
+ assert.equal(convertedCurrency, 'mockConvertedCurrency')
+ assert.equal(value, 'mockGasTotal')
+ })
+
+ it('should render the Button with the correct props', () => {
+ const {
+ onClick,
+ } = wrapper.find('button').props()
+ assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 0)
+ onClick()
+ assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 1)
+ })
+ })
+})
diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js
index c80d8c0bb..17cea3d4e 100644
--- a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js
+++ b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SendRowWrapper from '../send-row-wrapper/'
-import GasFeeDisplay from '../../../send/gas-fee-display-v2'
+import GasFeeDisplay from './gas-fee-display/gas-fee-display.component'
export default class SendGasRow extends Component {
diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js
index 20d3daa59..6e6fbc8a8 100644
--- a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js
+++ b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js
@@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import {
getConversionRate,
- getConvertedCurrency,
+ getCurrentCurrency,
getGasTotal,
} from '../../send.selectors.js'
import { sendGasIsInError } from './send-gas-row.selectors.js'
@@ -13,7 +13,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
- convertedCurrency: getConvertedCurrency(state),
+ convertedCurrency: getCurrentCurrency(state),
gasTotal: getGasTotal(state),
gasLoadingError: sendGasIsInError(state),
}
diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js
index e4f05d708..db37f18be 100644
--- a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js
+++ b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js
@@ -5,7 +5,7 @@ import sinon from 'sinon'
import SendGasRow from '../send-gas-row.component.js'
import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component'
-import GasFeeDisplay from '../../../../send/gas-fee-display-v2'
+import GasFeeDisplay from '../gas-fee-display/gas-fee-display.component'
const propsMethodSpies = {
showCustomizeGasModal: sinon.spy(),
diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js
index 9135524d1..e928c8aba 100644
--- a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js
+++ b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js
@@ -19,7 +19,7 @@ proxyquire('../send-gas-row.container.js', {
},
'../../send.selectors.js': {
getConversionRate: (s) => `mockConversionRate:${s}`,
- getConvertedCurrency: (s) => `mockConvertedCurrency:${s}`,
+ getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`,
getGasTotal: (s) => `mockGasTotal:${s}`,
},
'./send-gas-row.selectors.js': { sendGasIsInError: (s) => `mockGasLoadingError:${s}` },
diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js b/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js
index 0a83186a5..1c2ecdf9c 100644
--- a/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js
+++ b/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js
@@ -19,9 +19,9 @@ export default class SendToRow extends Component {
updateSendToError: PropTypes.func,
};
- handleToChange (to, nickname = '') {
+ handleToChange (to, nickname = '', toError) {
const { updateSendTo, updateSendToError, updateGas } = this.props
- const toErrorObject = getToErrorObject(to)
+ const toErrorObject = getToErrorObject(to, toError)
updateSendTo(to, nickname)
updateSendToError(toErrorObject)
if (toErrorObject.to === null) {
@@ -53,7 +53,7 @@ export default class SendToRow extends Component {
inError={inError}
name={'address'}
network={network}
- onChange={(newTo, newNickname) => this.handleToChange(newTo, newNickname)}
+ onChange={({ toAddress, nickname, toError }) => this.handleToChange(toAddress, nickname, toError)}
openDropdown={() => openToDropdown()}
placeholder={this.context.t('recipientAddress')}
to={to}
diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js b/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js
index cea51ee20..6b90a9f09 100644
--- a/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js
+++ b/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js
@@ -4,12 +4,10 @@ const {
} = require('../../send.constants')
const { isValidAddress } = require('../../../../util')
-function getToErrorObject (to) {
- let toError = null
-
+function getToErrorObject (to, toError = null) {
if (!to) {
toError = REQUIRED_ERROR
- } else if (!isValidAddress(to)) {
+ } else if (!isValidAddress(to) && !toError) {
toError = INVALID_RECIPIENT_ADDRESS_ERROR
}
diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js
index 58fe51dcf..781371004 100644
--- a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js
+++ b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js
@@ -6,8 +6,8 @@ import proxyquire from 'proxyquire'
const SendToRow = proxyquire('../send-to-row.component.js', {
'./send-to-row.utils.js': {
- getToErrorObject: (to) => ({
- to: to === false ? null : `mockToErrorObject:${to}`,
+ getToErrorObject: (to, toError) => ({
+ to: to === false ? null : `mockToErrorObject:${to}${toError}`,
}),
},
}).default
@@ -67,11 +67,11 @@ describe('SendToRow Component', function () {
it('should call updateSendToError', () => {
assert.equal(propsMethodSpies.updateSendToError.callCount, 0)
- instance.handleToChange('mockTo2')
+ instance.handleToChange('mockTo2', '', 'mockToError')
assert.equal(propsMethodSpies.updateSendToError.callCount, 1)
assert.deepEqual(
propsMethodSpies.updateSendToError.getCall(0).args,
- [{ to: 'mockToErrorObject:mockTo2' }]
+ [{ to: 'mockToErrorObject:mockTo2mockToError' }]
)
})
@@ -138,11 +138,11 @@ describe('SendToRow Component', function () {
openDropdown()
assert.equal(propsMethodSpies.openToDropdown.callCount, 1)
assert.equal(SendToRow.prototype.handleToChange.callCount, 0)
- onChange('mockNewTo', 'mockNewNickname')
+ onChange({ toAddress: 'mockNewTo', nickname: 'mockNewNickname', toError: 'mockToError' })
assert.equal(SendToRow.prototype.handleToChange.callCount, 1)
assert.deepEqual(
SendToRow.prototype.handleToChange.getCall(0).args,
- ['mockNewTo', 'mockNewNickname']
+ ['mockNewTo', 'mockNewNickname', 'mockToError']
)
})
})
diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js
index 615c9581b..4d2447c32 100644
--- a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js
+++ b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js
@@ -40,6 +40,12 @@ describe('send-to-row utils', () => {
to: null,
})
})
+
+ it('should return the passed error if to is truthy but invalid if to is truthy and valid', () => {
+ assert.deepEqual(getToErrorObject('invalid #$ 345878', 'someExplicitError'), {
+ to: 'someExplicitError',
+ })
+ })
})
})
diff --git a/ui/app/components/send_/send.component.js b/ui/app/components/send_/send.component.js
index 516251e22..219b362f2 100644
--- a/ui/app/components/send_/send.component.js
+++ b/ui/app/components/send_/send.component.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import PersistentForm from '../../../lib/persistent-form'
import {
getAmountErrorObject,
+ getToAddressForGasUpdate,
doesAmountErrorRequireUpdate,
} from './send.utils'
@@ -38,7 +39,7 @@ export default class SendTransactionScreen extends PersistentForm {
updateSendTokenBalance: PropTypes.func,
};
- updateGas ({ to } = {}) {
+ updateGas ({ to: updatedToAddress, amount: value } = {}) {
const {
amount,
blockGasLimit,
@@ -48,6 +49,7 @@ export default class SendTransactionScreen extends PersistentForm {
recentBlocks,
selectedAddress,
selectedToken = {},
+ to: currentToAddress,
updateAndSetGasTotal,
} = this.props
@@ -59,8 +61,8 @@ export default class SendTransactionScreen extends PersistentForm {
recentBlocks,
selectedAddress,
selectedToken,
- to: to && to.toLowerCase(),
- value: amount,
+ to: getToAddressForGasUpdate(updatedToAddress, currentToAddress),
+ value: value || amount,
})
}
diff --git a/ui/app/components/send_/send.constants.js b/ui/app/components/send_/send.constants.js
index df5dee371..8acdf0641 100644
--- a/ui/app/components/send_/send.constants.js
+++ b/ui/app/components/send_/send.constants.js
@@ -36,6 +36,7 @@ const ONE_GWEI_IN_WEI_HEX = ethUtil.addHexPrefix(conversionUtil('0x1', {
}))
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
+const BASE_TOKEN_GAS_COST = '0x186a0' // Hex for 100000, a base estimate for token transfers.
module.exports = {
INSUFFICIENT_FUNDS_ERROR,
@@ -52,4 +53,5 @@ module.exports = {
REQUIRED_ERROR,
SIMPLE_GAS_COST,
TOKEN_TRANSFER_FUNCTION_SIGNATURE,
+ BASE_TOKEN_GAS_COST,
}
diff --git a/ui/app/components/send_/send.container.js b/ui/app/components/send_/send.container.js
index 1fd96d61f..185653c5f 100644
--- a/ui/app/components/send_/send.container.js
+++ b/ui/app/components/send_/send.container.js
@@ -19,6 +19,7 @@ import {
getSendAmount,
getSendEditingTransactionId,
getSendFromObject,
+ getSendTo,
getTokenBalance,
} from './send.selectors'
import {
@@ -54,6 +55,7 @@ function mapStateToProps (state) {
recentBlocks: getRecentBlocks(state),
selectedAddress: getSelectedAddress(state),
selectedToken: getSelectedToken(state),
+ to: getSendTo(state),
tokenBalance: getTokenBalance(state),
tokenContract: getSelectedTokenContract(state),
tokenToFiatRate: getSelectedTokenToFiatRate(state),
diff --git a/ui/app/components/send_/send.selectors.js b/ui/app/components/send_/send.selectors.js
index 7e7cfe2e9..f910f7caf 100644
--- a/ui/app/components/send_/send.selectors.js
+++ b/ui/app/components/send_/send.selectors.js
@@ -14,7 +14,6 @@ const selectors = {
getAmountConversionRate,
getBlockGasLimit,
getConversionRate,
- getConvertedCurrency,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency,
getCurrentNetwork,
@@ -98,10 +97,6 @@ function getConversionRate (state) {
return state.metamask.conversionRate
}
-function getConvertedCurrency (state) {
- return state.metamask.currentCurrency
-}
-
function getCurrentAccountWithSendEtherInfo (state) {
const currentAddress = getSelectedAddress(state)
const accounts = accountsWithSendEtherInfoSelector(state)
diff --git a/ui/app/components/send_/send.utils.js b/ui/app/components/send_/send.utils.js
index 67699be77..872df1d2f 100644
--- a/ui/app/components/send_/send.utils.js
+++ b/ui/app/components/send_/send.utils.js
@@ -4,11 +4,13 @@ const {
conversionGTE,
multiplyCurrencies,
conversionGreaterThan,
+ conversionLessThan,
} = require('../../conversion-util')
const {
calcTokenAmount,
} = require('../../token-util')
const {
+ BASE_TOKEN_GAS_COST,
INSUFFICIENT_FUNDS_ERROR,
INSUFFICIENT_TOKENS_ERROR,
NEGATIVE_ETH_ERROR,
@@ -20,6 +22,7 @@ const abi = require('ethereumjs-abi')
const ethUtil = require('ethereumjs-util')
module.exports = {
+ addGasBuffer,
calcGasTotal,
calcTokenBalance,
doesAmountErrorRequireUpdate,
@@ -27,6 +30,7 @@ module.exports = {
estimateGasPriceFromRecentBlocks,
generateTokenTransferData,
getAmountErrorObject,
+ getToAddressForGasUpdate,
isBalanceSufficient,
isTokenBalanceSufficient,
}
@@ -175,12 +179,13 @@ async function estimateGas ({ selectedAddress, selectedToken, blockGasLimit, to,
}
// if recipient has no code, gas is 21k max:
- const hasRecipient = Boolean(to)
- if (hasRecipient && !selectedToken) {
- const code = await global.eth.getCode(to)
+ if (!selectedToken) {
+ const code = Boolean(to) && await global.eth.getCode(to)
if (!code || code === '0x') {
return SIMPLE_GAS_COST
}
+ } else if (selectedToken && !to) {
+ return BASE_TOKEN_GAS_COST
}
paramsForGasEstimate.to = selectedToken ? selectedToken.address : to
@@ -201,16 +206,46 @@ async function estimateGas ({ selectedAddress, selectedToken, blockGasLimit, to,
err.message.includes('gas required exceeds allowance or always failing transaction')
)
if (simulationFailed) {
- return resolve(paramsForGasEstimate.gas)
+ const estimateWithBuffer = addGasBuffer(paramsForGasEstimate.gas, blockGasLimit, 1.5)
+ return resolve(ethUtil.addHexPrefix(estimateWithBuffer))
} else {
return reject(err)
}
}
- return resolve(estimatedGas.toString(16))
+ const estimateWithBuffer = addGasBuffer(estimatedGas.toString(16), blockGasLimit, 1.5)
+ return resolve(ethUtil.addHexPrefix(estimateWithBuffer))
})
})
}
+function addGasBuffer (initialGasLimitHex, blockGasLimitHex, bufferMultiplier = 1.5) {
+ const upperGasLimit = multiplyCurrencies(blockGasLimitHex, 0.9, {
+ toNumericBase: 'hex',
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ numberOfDecimals: '0',
+ })
+ const bufferedGasLimit = multiplyCurrencies(initialGasLimitHex, bufferMultiplier, {
+ toNumericBase: 'hex',
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ numberOfDecimals: '0',
+ })
+
+ // if initialGasLimit is above blockGasLimit, dont modify it
+ if (conversionGreaterThan(
+ { value: initialGasLimitHex, fromNumericBase: 'hex' },
+ { value: upperGasLimit, fromNumericBase: 'hex' },
+ )) return initialGasLimitHex
+ // if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
+ if (conversionLessThan(
+ { value: bufferedGasLimit, fromNumericBase: 'hex' },
+ { value: upperGasLimit, fromNumericBase: 'hex' },
+ )) return bufferedGasLimit
+ // otherwise use blockGasLimit
+ return upperGasLimit
+}
+
function generateTokenTransferData ({ toAddress = '0x0', amount = '0x0', selectedToken }) {
if (!selectedToken) return
return TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
@@ -237,3 +272,7 @@ function estimateGasPriceFromRecentBlocks (recentBlocks) {
return lowestPrices[Math.floor(lowestPrices.length / 2)]
}
+
+function getToAddressForGasUpdate (...addresses) {
+ return [...addresses, ''].find(str => str !== undefined && str !== null).toLowerCase()
+}
diff --git a/ui/app/components/send_/tests/send-component.test.js b/ui/app/components/send_/tests/send-component.test.js
index 4e33d8f63..4ba9b226d 100644
--- a/ui/app/components/send_/tests/send-component.test.js
+++ b/ui/app/components/send_/tests/send-component.test.js
@@ -201,7 +201,7 @@ describe('Send Component', function () {
})
describe('updateGas', () => {
- it('should call updateAndSetGasTotal with the correct params', () => {
+ it('should call updateAndSetGasTotal with the correct params if no to prop is passed', () => {
propsMethodSpies.updateAndSetGasTotal.resetHistory()
wrapper.instance().updateGas()
assert.equal(propsMethodSpies.updateAndSetGasTotal.callCount, 1)
@@ -215,12 +215,22 @@ describe('Send Component', function () {
recentBlocks: ['mockBlock'],
selectedAddress: 'mockSelectedAddress',
selectedToken: 'mockSelectedToken',
- to: undefined,
+ to: '',
value: 'mockAmount',
}
)
})
+ it('should call updateAndSetGasTotal with the correct params if a to prop is passed', () => {
+ propsMethodSpies.updateAndSetGasTotal.resetHistory()
+ wrapper.setProps({ to: 'someAddress' })
+ wrapper.instance().updateGas()
+ assert.equal(
+ propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0].to,
+ 'someaddress',
+ )
+ })
+
it('should call updateAndSetGasTotal with to set to lowercase if passed', () => {
propsMethodSpies.updateAndSetGasTotal.resetHistory()
wrapper.instance().updateGas({ to: '0xABC' })
diff --git a/ui/app/components/send_/tests/send-container.test.js b/ui/app/components/send_/tests/send-container.test.js
index 056aad148..91484f4d8 100644
--- a/ui/app/components/send_/tests/send-container.test.js
+++ b/ui/app/components/send_/tests/send-container.test.js
@@ -39,6 +39,7 @@ proxyquire('../send.container.js', {
getSelectedTokenContract: (s) => `mockTokenContract:${s}`,
getSelectedTokenToFiatRate: (s) => `mockTokenToFiatRate:${s}`,
getSendAmount: (s) => `mockAmount:${s}`,
+ getSendTo: (s) => `mockTo:${s}`,
getSendEditingTransactionId: (s) => `mockEditingTransactionId:${s}`,
getSendFromObject: (s) => `mockFrom:${s}`,
getTokenBalance: (s) => `mockTokenBalance:${s}`,
@@ -70,6 +71,7 @@ describe('send container', () => {
recentBlocks: 'mockRecentBlocks:mockState',
selectedAddress: 'mockSelectedAddress:mockState',
selectedToken: 'mockSelectedToken:mockState',
+ to: 'mockTo:mockState',
tokenBalance: 'mockTokenBalance:mockState',
tokenContract: 'mockTokenContract:mockState',
tokenToFiatRate: 'mockTokenToFiatRate:mockState',
diff --git a/ui/app/components/send_/tests/send-selectors.test.js b/ui/app/components/send_/tests/send-selectors.test.js
index 152af8059..218da656b 100644
--- a/ui/app/components/send_/tests/send-selectors.test.js
+++ b/ui/app/components/send_/tests/send-selectors.test.js
@@ -8,7 +8,6 @@ const {
getBlockGasLimit,
getAmountConversionRate,
getConversionRate,
- getConvertedCurrency,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency,
getCurrentNetwork,
@@ -154,15 +153,6 @@ describe('send selectors', () => {
})
})
- describe('getConvertedCurrency()', () => {
- it('should return the currently selected currency', () => {
- assert.equal(
- getConvertedCurrency(mockState),
- 'USD'
- )
- })
- })
-
describe('getCurrentAccountWithSendEtherInfo()', () => {
it('should return the currently selected account with identity info', () => {
assert.deepEqual(
diff --git a/ui/app/components/send_/tests/send-utils.test.js b/ui/app/components/send_/tests/send-utils.test.js
index b3f6372ef..a518a64e9 100644
--- a/ui/app/components/send_/tests/send-utils.test.js
+++ b/ui/app/components/send_/tests/send-utils.test.js
@@ -2,6 +2,7 @@ import assert from 'assert'
import sinon from 'sinon'
import proxyquire from 'proxyquire'
import {
+ BASE_TOKEN_GAS_COST,
ONE_GWEI_IN_WEI_HEX,
SIMPLE_GAS_COST,
} from '../send.constants'
@@ -18,10 +19,12 @@ const {
const stubs = {
addCurrencies: sinon.stub().callsFake((a, b, obj) => a + b),
conversionUtil: sinon.stub().callsFake((val, obj) => parseInt(val, 16)),
- conversionGTE: sinon.stub().callsFake((obj1, obj2) => obj1.value > obj2.value),
+ conversionGTE: sinon.stub().callsFake((obj1, obj2) => obj1.value >= obj2.value),
multiplyCurrencies: sinon.stub().callsFake((a, b) => `${a}x${b}`),
calcTokenAmount: sinon.stub().callsFake((a, d) => 'calc:' + a + d),
rawEncode: sinon.stub().returns([16, 1100]),
+ conversionGreaterThan: sinon.stub().callsFake((obj1, obj2) => obj1.value > obj2.value),
+ conversionLessThan: sinon.stub().callsFake((obj1, obj2) => obj1.value < obj2.value),
}
const sendUtils = proxyquire('../send.utils.js', {
@@ -30,6 +33,8 @@ const sendUtils = proxyquire('../send.utils.js', {
conversionUtil: stubs.conversionUtil,
conversionGTE: stubs.conversionGTE,
multiplyCurrencies: stubs.multiplyCurrencies,
+ conversionGreaterThan: stubs.conversionGreaterThan,
+ conversionLessThan: stubs.conversionLessThan,
},
'../../token-util': { calcTokenAmount: stubs.calcTokenAmount },
'ethereumjs-abi': {
@@ -44,6 +49,7 @@ const {
estimateGasPriceFromRecentBlocks,
generateTokenTransferData,
getAmountErrorObject,
+ getToAddressForGasUpdate,
calcTokenBalance,
isBalanceSufficient,
isTokenBalanceSufficient,
@@ -255,7 +261,7 @@ describe('send utils', () => {
estimateGasMethod: sinon.stub().callsFake(
(data, cb) => cb(
data.to.match(/willFailBecauseOf:/) ? { message: data.to.match(/:(.+)$/)[1] } : null,
- { toString: (n) => `mockToString:${n}` }
+ { toString: (n) => `0xabc${n}` }
)
),
}
@@ -279,13 +285,23 @@ describe('send utils', () => {
})
it('should call ethQuery.estimateGas with the expected params', async () => {
- const result = await estimateGas(baseMockParams)
+ const result = await sendUtils.estimateGas(baseMockParams)
assert.equal(baseMockParams.estimateGasMethod.callCount, 1)
assert.deepEqual(
baseMockParams.estimateGasMethod.getCall(0).args[0],
Object.assign({ gasPrice: undefined, value: undefined }, baseExpectedCall)
)
- assert.equal(result, 'mockToString:16')
+ assert.equal(result, '0xabc16')
+ })
+
+ it('should call ethQuery.estimateGas with the expected params when initialGasLimitHex is lower than the upperGasLimit', async () => {
+ const result = await estimateGas(Object.assign({}, baseMockParams, { blockGasLimit: '0xbcd' }))
+ assert.equal(baseMockParams.estimateGasMethod.callCount, 1)
+ assert.deepEqual(
+ baseMockParams.estimateGasMethod.getCall(0).args[0],
+ Object.assign({ gasPrice: undefined, value: undefined }, baseExpectedCall, { gas: '0xbcdx0.95' })
+ )
+ assert.equal(result, '0xabc16x1.5')
})
it('should call ethQuery.estimateGas with a value of 0x0 and the expected data and to if passed a selectedToken', async () => {
@@ -300,7 +316,7 @@ describe('send utils', () => {
to: 'mockAddress',
})
)
- assert.equal(result, 'mockToString:16')
+ assert.equal(result, '0xabc16')
})
it(`should return ${SIMPLE_GAS_COST} if ethQuery.getCode does not return '0x'`, async () => {
@@ -309,12 +325,23 @@ describe('send utils', () => {
assert.equal(result, SIMPLE_GAS_COST)
})
+ it(`should return ${SIMPLE_GAS_COST} if not passed a selectedToken or truthy to address`, async () => {
+ assert.equal(baseMockParams.estimateGasMethod.callCount, 0)
+ const result = await estimateGas(Object.assign({}, baseMockParams, { to: null }))
+ assert.equal(result, SIMPLE_GAS_COST)
+ })
+
it(`should not return ${SIMPLE_GAS_COST} if passed a selectedToken`, async () => {
assert.equal(baseMockParams.estimateGasMethod.callCount, 0)
const result = await estimateGas(Object.assign({}, baseMockParams, { to: '0x123', selectedToken: { address: '' } }))
assert.notEqual(result, SIMPLE_GAS_COST)
})
+ it(`should return ${BASE_TOKEN_GAS_COST} if passed a selectedToken but no to address`, async () => {
+ const result = await estimateGas(Object.assign({}, baseMockParams, { to: null, selectedToken: { address: '' } }))
+ assert.equal(result, BASE_TOKEN_GAS_COST)
+ })
+
it(`should return the adjusted blockGasLimit if it fails with a 'Transaction execution error.'`, async () => {
const result = await estimateGas(Object.assign({}, baseMockParams, {
to: 'isContract willFailBecauseOf:Transaction execution error.',
@@ -401,4 +428,15 @@ describe('send utils', () => {
assert.equal(estimateGasPriceFromRecentBlocks(mockRecentBlocks), '0x5')
})
})
+
+ describe('getToAddressForGasUpdate()', () => {
+ it('should return empty string if all params are undefined or null', () => {
+ assert.equal(getToAddressForGasUpdate(undefined, null), '')
+ })
+
+ it('should return the first string that is not defined or null in lower case', () => {
+ assert.equal(getToAddressForGasUpdate('A', null), 'a')
+ assert.equal(getToAddressForGasUpdate(undefined, 'B'), 'b')
+ })
+ })
})
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index 93d2023b5..2c4ba40bf 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -181,7 +181,7 @@ ShapeshiftForm.prototype.render = function () {
return h('div.shapeshift-form-wrapper', [
showQrCode
? this.renderQrCode()
- : h('div.shapeshift-form', [
+ : h('div.modal-shapeshift-form', [
h('div.shapeshift-form__selectors', [
h('div.shapeshift-form__selector', [
diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js
index ab780dcf4..bbb340fcf 100644
--- a/ui/app/components/signature-request.js
+++ b/ui/app/components/signature-request.js
@@ -178,7 +178,14 @@ SignatureRequest.prototype.renderBody = function () {
rows = data
} else if (type === 'eth_sign') {
rows = [{ name: this.context.t('message'), value: data }]
- notice = this.context.t('signNotice')
+ notice = [this.context.t('signNotice'),
+ h('span.request-signature__help-link', {
+ onClick: () => {
+ global.platform.openWindow({
+ url: 'https://consensys.zendesk.com/hc/en-us/articles/360004427792',
+ })
+ },
+ }, this.context.t('learnMore'))]
}
return h('div.request-signature__body', {}, [
@@ -197,6 +204,9 @@ SignatureRequest.prototype.renderBody = function () {
h('div.request-signature__rows', [
...rows.map(({ name, value }) => {
+ if (typeof value === 'boolean') {
+ value = value.toString()
+ }
return h('div.request-signature__row', [
h('div.request-signature__row-title', [`${name}:`]),
h('div.request-signature__row-value', value),
diff --git a/ui/app/components/token-balance.js b/ui/app/components/token-balance.js
index 1900ccec7..df3bd59bb 100644
--- a/ui/app/components/token-balance.js
+++ b/ui/app/components/token-balance.js
@@ -34,7 +34,7 @@ TokenBalance.prototype.render = function () {
return isLoading
? h('span', '')
: h('span.token-balance', [
- h('span.token-balance__amount', string),
+ h('span.hide-text-overflow.token-balance__amount', string),
!balanceOnly && h('span.token-balance__symbol', symbol),
])
}
diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js
index 100402d95..337763067 100644
--- a/ui/app/conversion-util.js
+++ b/ui/app/conversion-util.js
@@ -190,6 +190,16 @@ const conversionGreaterThan = (
return firstValue.gt(secondValue)
}
+const conversionLessThan = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstValue = converter({ ...firstProps })
+ const secondValue = converter({ ...secondProps })
+
+ return firstValue.lt(secondValue)
+}
+
const conversionMax = (
{ ...firstProps },
{ ...secondProps },
@@ -229,6 +239,7 @@ module.exports = {
addCurrencies,
multiplyCurrencies,
conversionGreaterThan,
+ conversionLessThan,
conversionGTE,
conversionLTE,
conversionMax,
diff --git a/ui/app/css/itcss/components/currency-display.scss b/ui/app/css/itcss/components/currency-display.scss
index 3560b0b0c..b1a74dce2 100644
--- a/ui/app/css/itcss/components/currency-display.scss
+++ b/ui/app/css/itcss/components/currency-display.scss
@@ -1,6 +1,5 @@
.currency-display {
height: 54px;
- width: 100%ß;
border: 1px solid $alto;
border-radius: 4px;
background-color: $white;
@@ -21,7 +20,7 @@
line-height: 22px;
border: none;
outline: 0 !important;
- max-width: 100%;
+ max-width: 22ch;
}
&__primary-currency {
@@ -47,14 +46,22 @@
&__input-wrapper {
position: relative;
display: flex;
+ flex: 1;
+ max-width: 100%;
+
+ input[type="number"] {
+ -moz-appearance: textfield;
+ }
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
+ -moz-appearance: none;
display: none;
}
input[type="number"]:hover::-webkit-inner-spin-button {
-webkit-appearance: none;
+ -moz-appearance: none;
display: none;
}
}
@@ -67,12 +74,14 @@
.react-numeric-input {
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
+ -moz-appearance: none;
display: none;
}
input[type="number"]:hover::-webkit-inner-spin-button {
-webkit-appearance: none;
+ -moz-appearance: none;
display: none;
}
}
-} \ No newline at end of file
+}
diff --git a/ui/app/css/itcss/components/hero-balance.scss b/ui/app/css/itcss/components/hero-balance.scss
index 09d66aedd..eba93ecb4 100644
--- a/ui/app/css/itcss/components/hero-balance.scss
+++ b/ui/app/css/itcss/components/hero-balance.scss
@@ -27,25 +27,37 @@
@media screen and (max-width: $break-small) {
flex-direction: column;
flex: 0 0 auto;
+ max-width: 100%;
}
@media screen and (min-width: $break-large) {
flex-direction: row;
flex-grow: 3;
+ min-width: 0;
}
}
.balance-display {
.token-amount {
color: $black;
+ max-width: 100%;
+
+ .token-balance {
+ display: flex;
+ }
}
@media screen and (max-width: $break-small) {
+ max-width: 100%;
text-align: center;
.token-amount {
font-size: 1.75rem;
margin-top: 1rem;
+
+ .token-balance {
+ flex-direction: column;
+ }
}
.fiat-amount {
@@ -56,9 +68,10 @@
}
@media screen and (min-width: $break-large) {
- margin-left: .8em;
+ margin: 0 .8em;
justify-content: flex-start;
align-items: flex-start;
+ min-width: 0;
.token-amount {
font-size: 1.5rem;
diff --git a/ui/app/css/itcss/components/modal.scss b/ui/app/css/itcss/components/modal.scss
index 74658f656..42ef7ae0a 100644
--- a/ui/app/css/itcss/components/modal.scss
+++ b/ui/app/css/itcss/components/modal.scss
@@ -642,10 +642,31 @@
display: flex;
flex-flow: column nowrap;
flex: 1;
+ align-items: center;
@media screen and (max-width: 575px) {
height: 0;
}
+
+ .shapeshift-form-wrapper {
+ display: flex;
+ flex-flow: column;
+ justify-content: center;
+ align-items: center;
+ flex: 1 0 auto;
+
+ .shapeshift-form, .modal-shapeshift-form {
+ border-radius: 8px;
+ background-color: rgba(0, 0, 0, .05);
+ padding: 17px 15px;
+ margin-bottom: 10px;
+
+ &__caret {
+ width: auto;
+ flex: 1;
+ }
+ }
+ }
}
&__logo {
@@ -773,17 +794,15 @@
margin-top: 28px;
flex: 1 0 auto;
- .shapeshift-form {
- width: auto;
+ .shapeshift-form, .modal-shapeshift-form {
+ border-radius: 8px;
+ background-color: rgba(0, 0, 0, .05);
+ padding: 17px 15px;
&__caret {
width: auto;
flex: 1;
}
-
- @media screen and (max-width: 575px) {
- width: auto;
- }
}
}
diff --git a/ui/app/css/itcss/components/newui-sections.scss b/ui/app/css/itcss/components/newui-sections.scss
index bbe0ee661..667e45ba2 100644
--- a/ui/app/css/itcss/components/newui-sections.scss
+++ b/ui/app/css/itcss/components/newui-sections.scss
@@ -26,14 +26,16 @@ $wallet-view-bg: $alabaster;
//Account and transaction details
.account-and-transaction-details {
display: flex;
- flex: 1 0 auto;
+ flex: 1 1 auto;
+ min-width: 0;
}
// tx view
.tx-view {
- flex: 63.5 0 66.5%;
+ flex: 1 1 66.5%;
background: $tx-view-bg;
+ min-width: 0;
// No title on mobile
@media screen and (max-width: 575px) {
@@ -286,7 +288,7 @@ $wallet-view-bg: $alabaster;
}
.token-balance__amount {
- padding-right: 6px;
+ padding: 0 6px;
}
// first time
diff --git a/ui/app/css/itcss/components/request-signature.scss b/ui/app/css/itcss/components/request-signature.scss
index e6916c418..4707ff60e 100644
--- a/ui/app/css/itcss/components/request-signature.scss
+++ b/ui/app/css/itcss/components/request-signature.scss
@@ -183,6 +183,12 @@
padding: 6px 18px 15px;
}
+ &__help-link {
+ cursor: pointer;
+ text-decoration: underline;
+ color: $curious-blue;
+ }
+
&__footer {
width: 100%;
display: flex;
diff --git a/ui/app/css/itcss/components/token-list.scss b/ui/app/css/itcss/components/token-list.scss
index 72fda372f..4b706abce 100644
--- a/ui/app/css/itcss/components/token-list.scss
+++ b/ui/app/css/itcss/components/token-list.scss
@@ -81,13 +81,9 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
}
.token-menu-dropdown {
- height: 55px;
width: 80%;
- border-radius: 4px;
- background-color: rgba(0, 0, 0, .82);
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .5);
position: absolute;
- top: 60px;
+ top: 52px;
right: 25px;
z-index: 2000;
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index 4e9d0848c..9cacf5fe7 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -62,6 +62,7 @@ function reduceApp (state, action) {
warning: null,
buyView: {},
isMouseUser: false,
+ gasIsLoading: false,
}, state.appState)
switch (action.type) {
@@ -675,6 +676,16 @@ function reduceApp (state, action) {
isMouseUser: action.value,
})
+ case actions.GAS_LOADING_STARTED:
+ return extend(appState, {
+ gasIsLoading: true,
+ })
+
+ case actions.GAS_LOADING_FINISHED:
+ return extend(appState, {
+ gasIsLoading: false,
+ })
+
default:
return appState
}
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index a4d1aece5..6c8ac9ed7 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -21,7 +21,7 @@ function reduceMetamask (state, action) {
identities: {},
unapprovedTxs: {},
noActiveNotices: true,
- lastUnreadNotice: undefined,
+ nextUnreadNotice: undefined,
frequentRpcList: [],
addressBook: [],
selectedTokenAddress: null,
@@ -65,7 +65,7 @@ function reduceMetamask (state, action) {
case actions.SHOW_NOTICE:
return extend(metamaskState, {
noActiveNotices: false,
- lastUnreadNotice: action.value,
+ nextUnreadNotice: action.value,
})
case actions.CLEAR_NOTICES:
diff --git a/ui/app/selectors.js b/ui/app/selectors.js
index a29294b86..cf0affe9c 100644
--- a/ui/app/selectors.js
+++ b/ui/app/selectors.js
@@ -16,6 +16,7 @@ const selectors = {
transactionsSelector,
accountsWithSendEtherInfoSelector,
getCurrentAccountWithSendEtherInfo,
+ getGasIsLoading,
getGasPrice,
getGasLimit,
getForceGasMin,
@@ -117,6 +118,10 @@ function transactionsSelector (state) {
.sort((a, b) => b.time - a.time)
}
+function getGasIsLoading (state) {
+ return state.appState.gasIsLoading
+}
+
function getGasPrice (state) {
return state.metamask.send.gasPrice
}
diff --git a/ui/app/util.js b/ui/app/util.js
index 1ccd17ba7..8c85c5926 100644
--- a/ui/app/util.js
+++ b/ui/app/util.js
@@ -36,6 +36,7 @@ module.exports = {
miniAddressSummary: miniAddressSummary,
isAllOneCase: isAllOneCase,
isValidAddress: isValidAddress,
+ isValidENSAddress,
numericBalance: numericBalance,
parseBalance: parseBalance,
formatBalance: formatBalance,
@@ -87,6 +88,10 @@ function isValidAddress (address) {
return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
}
+function isValidENSAddress (address) {
+ return address.match(/^.{7,}\.(eth|test)$/)
+}
+
function isInvalidChecksumAddress (address) {
var prefixed = ethUtil.addHexPrefix(address)
if (address === '0x0000000000000000000000000000000000000000') return false