mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
[NewUI] SendV2-#8: Send container handles tokens; gas info dynamic from state (#2364)
* Adds memo field to send-v2. * Vertical align transaction with flexbox. * Customize Gas UI * Remove internal state from InputNumber and fix use in gastooltip. * Move customize-gas-modal to its own folder and minor cleanup * Create send container, get account info from state, and make currency display more reusable * Adjusts send-v2 and container for send-token. Dynamically getting suggested gas prices.
This commit is contained in:
parent
81f62a7443
commit
803eaaf968
@ -10,7 +10,7 @@ const NewKeyChainScreen = require('./new-keychain')
|
|||||||
// accounts
|
// accounts
|
||||||
const MainContainer = require('./main-container')
|
const MainContainer = require('./main-container')
|
||||||
const SendTransactionScreen = require('./send')
|
const SendTransactionScreen = require('./send')
|
||||||
const SendTransactionScreen2 = require('./send-v2.js')
|
const SendTransactionScreen2 = require('./components/send/send-v2-container')
|
||||||
const SendTokenScreen = require('./components/send-token')
|
const SendTokenScreen = require('./components/send-token')
|
||||||
const ConfirmTxScreen = require('./conf-tx')
|
const ConfirmTxScreen = require('./conf-tx')
|
||||||
// notice
|
// notice
|
||||||
@ -356,7 +356,12 @@ App.prototype.renderPrimary = function () {
|
|||||||
|
|
||||||
case 'sendToken':
|
case 'sendToken':
|
||||||
log.debug('rendering send token screen')
|
log.debug('rendering send token screen')
|
||||||
return h(SendTokenScreen, {key: 'sendToken'})
|
|
||||||
|
const SendTokenComponentToRender = checkFeatureToggle('send-v2')
|
||||||
|
? SendTransactionScreen2
|
||||||
|
: SendTokenScreen
|
||||||
|
|
||||||
|
return h(SendTokenComponentToRender, {key: 'sendToken'})
|
||||||
|
|
||||||
case 'newKeychain':
|
case 'newKeychain':
|
||||||
log.debug('rendering new keychain screen')
|
log.debug('rendering new keychain screen')
|
||||||
|
55
ui/app/components/customize-gas-modal/gas-modal-card.js
Normal file
55
ui/app/components/customize-gas-modal/gas-modal-card.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const InputNumber = require('../input-number.js')
|
||||||
|
const GasSlider = require('./gas-slider.js')
|
||||||
|
|
||||||
|
module.exports = GasModalCard
|
||||||
|
|
||||||
|
inherits(GasModalCard, Component)
|
||||||
|
function GasModalCard () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
GasModalCard.prototype.render = function () {
|
||||||
|
const {
|
||||||
|
memo,
|
||||||
|
identities,
|
||||||
|
onChange,
|
||||||
|
unitLabel,
|
||||||
|
value,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
step,
|
||||||
|
title,
|
||||||
|
copy
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return h('div.send-v2__gas-modal-card', [
|
||||||
|
|
||||||
|
h('div.send-v2__gas-modal-card__title', {}, title),
|
||||||
|
|
||||||
|
h('div.send-v2__gas-modal-card__copy', {}, copy),
|
||||||
|
|
||||||
|
h(InputNumber, {
|
||||||
|
unitLabel,
|
||||||
|
step,
|
||||||
|
max,
|
||||||
|
min,
|
||||||
|
placeholder: '0',
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}),
|
||||||
|
|
||||||
|
h(GasSlider, {
|
||||||
|
value,
|
||||||
|
step,
|
||||||
|
max,
|
||||||
|
min,
|
||||||
|
onChange,
|
||||||
|
}),
|
||||||
|
|
||||||
|
])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
50
ui/app/components/customize-gas-modal/gas-slider.js
Normal file
50
ui/app/components/customize-gas-modal/gas-slider.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
|
||||||
|
module.exports = GasSlider
|
||||||
|
|
||||||
|
inherits(GasSlider, Component)
|
||||||
|
function GasSlider () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
GasSlider.prototype.render = function () {
|
||||||
|
const {
|
||||||
|
memo,
|
||||||
|
identities,
|
||||||
|
onChange,
|
||||||
|
unitLabel,
|
||||||
|
value,
|
||||||
|
id,
|
||||||
|
step,
|
||||||
|
max,
|
||||||
|
min,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return h('div.gas-slider', [
|
||||||
|
|
||||||
|
h('input.gas-slider__input', {
|
||||||
|
type: 'range',
|
||||||
|
step,
|
||||||
|
max,
|
||||||
|
min,
|
||||||
|
value,
|
||||||
|
id: 'gasSlider',
|
||||||
|
onChange: event => onChange(event.target.value),
|
||||||
|
}, []),
|
||||||
|
|
||||||
|
h('div.gas-slider__bar', [
|
||||||
|
|
||||||
|
h('div.gas-slider__low'),
|
||||||
|
|
||||||
|
h('div.gas-slider__mid'),
|
||||||
|
|
||||||
|
h('div.gas-slider__high'),
|
||||||
|
|
||||||
|
]),
|
||||||
|
|
||||||
|
])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
91
ui/app/components/customize-gas-modal/index.js
Normal file
91
ui/app/components/customize-gas-modal/index.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const actions = require('../../actions')
|
||||||
|
const GasModalCard = require('./gas-modal-card')
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return {
|
||||||
|
hideModal: () => dispatch(actions.hideModal()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(CustomizeGasModal, Component)
|
||||||
|
function CustomizeGasModal () {
|
||||||
|
Component.call(this)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
gasPrice: '0.23',
|
||||||
|
gasLimit: '25000',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal)
|
||||||
|
|
||||||
|
CustomizeGasModal.prototype.render = function () {
|
||||||
|
const { hideModal } = this.props
|
||||||
|
const { gasPrice, gasLimit } = this.state
|
||||||
|
|
||||||
|
return h('div.send-v2__customize-gas', {}, [
|
||||||
|
h('div', {
|
||||||
|
}, [
|
||||||
|
h('div.send-v2__customize-gas__header', {}, [
|
||||||
|
|
||||||
|
h('div.send-v2__customize-gas__title', 'Customize Gas'),
|
||||||
|
|
||||||
|
h('div.send-v2__customize-gas__close', {
|
||||||
|
onClick: hideModal,
|
||||||
|
}),
|
||||||
|
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('div.send-v2__customize-gas__body', {}, [
|
||||||
|
|
||||||
|
h(GasModalCard, {
|
||||||
|
value: gasPrice,
|
||||||
|
min: 0.0,
|
||||||
|
max: 5.0,
|
||||||
|
step: 0.01,
|
||||||
|
onChange: gasPrice => this.setState({ gasPrice }),
|
||||||
|
title: 'Gas Price',
|
||||||
|
copy: 'We calculate the suggested gas prices based on network success rates.',
|
||||||
|
}),
|
||||||
|
|
||||||
|
h(GasModalCard, {
|
||||||
|
value: gasLimit,
|
||||||
|
min: 20000,
|
||||||
|
max: 100000,
|
||||||
|
step: 1,
|
||||||
|
onChange: gasLimit => this.setState({ gasLimit }),
|
||||||
|
title: 'Gas Limit',
|
||||||
|
copy: 'We calculate the suggested gas limit based on network success rates.',
|
||||||
|
}),
|
||||||
|
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('div.send-v2__customize-gas__footer', {}, [
|
||||||
|
|
||||||
|
h('div.send-v2__customize-gas__revert', {
|
||||||
|
onClick: () => console.log('Revert'),
|
||||||
|
}, ['Revert']),
|
||||||
|
|
||||||
|
h('div.send-v2__customize-gas__buttons', [
|
||||||
|
h('div.send-v2__customize-gas__cancel', {
|
||||||
|
onClick: this.props.hideModal,
|
||||||
|
}, ['CANCEL']),
|
||||||
|
|
||||||
|
h('div.send-v2__customize-gas__save', {
|
||||||
|
onClick: () => console.log('Save'),
|
||||||
|
}, ['SAVE']),
|
||||||
|
])
|
||||||
|
|
||||||
|
]),
|
||||||
|
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
const Component = require('react').Component
|
const Component = require('react').Component
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
|
const { addCurrencies } = require('../conversion-util')
|
||||||
|
|
||||||
module.exports = InputNumber
|
module.exports = InputNumber
|
||||||
|
|
||||||
@ -8,49 +9,37 @@ inherits(InputNumber, Component)
|
|||||||
function InputNumber () {
|
function InputNumber () {
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
|
|
||||||
this.state = {
|
|
||||||
value: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setValue = this.setValue.bind(this)
|
this.setValue = this.setValue.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
InputNumber.prototype.componentWillMount = function () {
|
|
||||||
const { initValue = 0 } = this.props
|
|
||||||
|
|
||||||
this.setState({ value: initValue })
|
|
||||||
}
|
|
||||||
|
|
||||||
InputNumber.prototype.setValue = function (newValue) {
|
InputNumber.prototype.setValue = function (newValue) {
|
||||||
const { fixed, min = -1, onChange } = this.props
|
const { fixed, min = -1, max = Infinity, onChange } = this.props
|
||||||
|
|
||||||
if (fixed) newValue = Number(newValue.toFixed(4))
|
newValue = Number(fixed ? newValue.toFixed(4) : newValue)
|
||||||
|
|
||||||
if (newValue >= min) {
|
if (newValue >= min && newValue <= max) {
|
||||||
this.setState({ value: newValue })
|
|
||||||
onChange(newValue)
|
onChange(newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputNumber.prototype.render = function () {
|
InputNumber.prototype.render = function () {
|
||||||
const { unitLabel, step = 1, placeholder } = this.props
|
const { unitLabel, step = 1, placeholder, value = 0 } = this.props
|
||||||
const { value } = this.state
|
|
||||||
|
|
||||||
return h('div.customize-gas-input-wrapper', {}, [
|
return h('div.customize-gas-input-wrapper', {}, [
|
||||||
h('input.customize-gas-input', {
|
h('input.customize-gas-input', {
|
||||||
placeholder,
|
placeholder,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
value,
|
value: value,
|
||||||
onChange: (e) => this.setValue(Number(e.target.value)),
|
onChange: (e) => this.setValue(e.target.value),
|
||||||
}),
|
}),
|
||||||
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
|
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
|
||||||
h('div.gas-tooltip-input-arrows', {}, [
|
h('div.gas-tooltip-input-arrows', {}, [
|
||||||
h('i.fa.fa-angle-up', {
|
h('i.fa.fa-angle-up', {
|
||||||
onClick: () => this.setValue(value + step),
|
onClick: () => this.setValue(addCurrencies(value, step)),
|
||||||
}),
|
}),
|
||||||
h('i.fa.fa-angle-down', {
|
h('i.fa.fa-angle-down', {
|
||||||
style: { cursor: 'pointer' },
|
style: { cursor: 'pointer' },
|
||||||
onClick: () => this.setValue(value - step),
|
onClick: () => this.setValue(addCurrencies(value, step * -1)),
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
@ -15,6 +15,7 @@ const ExportPrivateKeyModal = require('./export-private-key-modal')
|
|||||||
const NewAccountModal = require('./new-account-modal')
|
const NewAccountModal = require('./new-account-modal')
|
||||||
const ShapeshiftDepositTxModal = require('./shapeshift-deposit-tx-modal.js')
|
const ShapeshiftDepositTxModal = require('./shapeshift-deposit-tx-modal.js')
|
||||||
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal')
|
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal')
|
||||||
|
const CustomizeGasModal = require('../customize-gas-modal')
|
||||||
|
|
||||||
const accountModalStyle = {
|
const accountModalStyle = {
|
||||||
mobileModalStyle: {
|
mobileModalStyle: {
|
||||||
@ -156,6 +157,31 @@ const MODALS = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
CUSTOMIZE_GAS: {
|
||||||
|
contents: [
|
||||||
|
h(CustomizeGasModal, {}, []),
|
||||||
|
],
|
||||||
|
mobileModalStyle: {
|
||||||
|
width: '355px',
|
||||||
|
height: '598px',
|
||||||
|
// top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
|
||||||
|
top: '5%',
|
||||||
|
transform: 'none',
|
||||||
|
left: '0',
|
||||||
|
right: '0',
|
||||||
|
margin: '0 auto',
|
||||||
|
},
|
||||||
|
laptopModalStyle: {
|
||||||
|
width: '720px',
|
||||||
|
height: '377px',
|
||||||
|
top: '80px',
|
||||||
|
transform: 'none',
|
||||||
|
left: '0',
|
||||||
|
right: '0',
|
||||||
|
margin: '0 auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
DEFAULT: {
|
DEFAULT: {
|
||||||
contents: [],
|
contents: [],
|
||||||
mobileModalStyle: {},
|
mobileModalStyle: {},
|
||||||
|
@ -3,27 +3,34 @@ const h = require('react-hyperscript')
|
|||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const Identicon = require('../identicon')
|
const Identicon = require('../identicon')
|
||||||
|
const CurrencyDisplay = require('./currency-display')
|
||||||
|
const { conversionRateSelector } = require('../../selectors')
|
||||||
|
|
||||||
inherits(AccountListItem, Component)
|
inherits(AccountListItem, Component)
|
||||||
function AccountListItem () {
|
function AccountListItem () {
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = AccountListItem
|
function mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
conversionRate: conversionRateSelector(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(AccountListItem)
|
||||||
|
|
||||||
AccountListItem.prototype.render = function () {
|
AccountListItem.prototype.render = function () {
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
handleClick,
|
handleClick,
|
||||||
icon = null,
|
icon = null,
|
||||||
|
conversionRate,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const { identity, balancesToRender } = account
|
const { name, address, balance } = account
|
||||||
const { name, address } = identity
|
|
||||||
const { primary, secondary } = balancesToRender
|
|
||||||
|
|
||||||
return h('div.account-list-item', {
|
return h('div.account-list-item', {
|
||||||
onClick: () => handleClick(identity),
|
onClick: () => handleClick({ name, address, balance }),
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
h('div.account-list-item__top-row', {}, [
|
h('div.account-list-item__top-row', {}, [
|
||||||
@ -43,9 +50,17 @@ AccountListItem.prototype.render = function () {
|
|||||||
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('div.account-list-item__account-primary-balance', {}, primary),
|
h(CurrencyDisplay, {
|
||||||
|
primaryCurrency: 'ETH',
|
||||||
h('div.account-list-item__account-secondary-balance', {}, secondary),
|
convertedCurrency: 'USD',
|
||||||
|
value: balance,
|
||||||
|
conversionRate,
|
||||||
|
convertedPrefix: '$',
|
||||||
|
readOnly: true,
|
||||||
|
className: 'account-list-item__account-balances',
|
||||||
|
primaryBalanceClassName: 'account-list-item__account-primary-balance',
|
||||||
|
convertedBalanceClassName: 'account-list-item__account-secondary-balance',
|
||||||
|
}, name),
|
||||||
|
|
||||||
])
|
])
|
||||||
}
|
}
|
@ -11,8 +11,7 @@ function CurrencyDisplay () {
|
|||||||
Component.call(this)
|
Component.call(this)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
minWidth: null,
|
value: null,
|
||||||
currentScrollWidth: null,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,28 +28,50 @@ function resetCaretIfPastEnd (value, event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CurrencyDisplay.prototype.handleChangeInHexWei = function (value) {
|
||||||
|
const { handleChange } = this.props
|
||||||
|
|
||||||
|
const valueInHexWei = conversionUtil(value, {
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
toDenomination: 'WEI',
|
||||||
|
})
|
||||||
|
|
||||||
|
handleChange(valueInHexWei)
|
||||||
|
}
|
||||||
|
|
||||||
CurrencyDisplay.prototype.render = function () {
|
CurrencyDisplay.prototype.render = function () {
|
||||||
const {
|
const {
|
||||||
className,
|
className = 'currency-display',
|
||||||
|
primaryBalanceClassName = 'currency-display__input',
|
||||||
|
convertedBalanceClassName = 'currency-display__converted-value',
|
||||||
|
conversionRate,
|
||||||
primaryCurrency,
|
primaryCurrency,
|
||||||
convertedCurrency,
|
convertedCurrency,
|
||||||
value = '',
|
|
||||||
placeholder = '0',
|
|
||||||
conversionRate,
|
|
||||||
convertedPrefix = '',
|
convertedPrefix = '',
|
||||||
|
placeholder = '0',
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
handleChange,
|
value: initValue,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { minWidth } = this.state
|
const { value } = this.state
|
||||||
|
|
||||||
const convertedValue = conversionUtil(value, {
|
const initValueToRender = conversionUtil(initValue, {
|
||||||
fromNumericBase: 'dec',
|
fromNumericBase: 'hex',
|
||||||
fromCurrency: primaryCurrency,
|
toNumericBase: 'dec',
|
||||||
toCurrency: convertedCurrency,
|
fromDenomination: 'WEI',
|
||||||
|
numberOfDecimals: 6,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
})
|
})
|
||||||
|
|
||||||
return h('div.currency-display', {
|
const convertedValue = conversionUtil(value || initValueToRender, {
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
fromCurrency: primaryCurrency,
|
||||||
|
toCurrency: convertedCurrency,
|
||||||
|
numberOfDecimals: 2,
|
||||||
|
conversionRate,
|
||||||
|
})
|
||||||
|
|
||||||
|
return h('div', {
|
||||||
className,
|
className,
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
@ -58,35 +79,39 @@ CurrencyDisplay.prototype.render = function () {
|
|||||||
|
|
||||||
h('div.currency-display__input-wrapper', [
|
h('div.currency-display__input-wrapper', [
|
||||||
|
|
||||||
h('input.currency-display__input', {
|
h('input', {
|
||||||
value: `${value} ${primaryCurrency}`,
|
className: primaryBalanceClassName,
|
||||||
|
value: `${value || initValueToRender} ${primaryCurrency}`,
|
||||||
placeholder: `${0} ${primaryCurrency}`,
|
placeholder: `${0} ${primaryCurrency}`,
|
||||||
readOnly,
|
readOnly,
|
||||||
onChange: (event) => {
|
onChange: (event) => {
|
||||||
let newValue = event.target.value.split(' ')[0]
|
let newValue = event.target.value.split(' ')[0]
|
||||||
|
|
||||||
if (newValue === '') {
|
if (newValue === '') {
|
||||||
handleChange('0')
|
this.setState({ value: '0' })
|
||||||
}
|
}
|
||||||
else if (newValue.match(/^0[1-9]$/)) {
|
else if (newValue.match(/^0[1-9]$/)) {
|
||||||
handleChange(newValue.match(/[1-9]/)[0])
|
this.setState({ value: newValue.match(/[1-9]/)[0] })
|
||||||
}
|
}
|
||||||
else if (newValue && !isValidInput(newValue)) {
|
else if (newValue && !isValidInput(newValue)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
handleChange(newValue)
|
this.setState({ value: newValue })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onKeyUp: event => resetCaretIfPastEnd(value, event),
|
onBlur: event => this.handleChangeInHexWei(event.target.value.split(' ')[0]),
|
||||||
onClick: event => resetCaretIfPastEnd(value, event),
|
onKeyUp: event => resetCaretIfPastEnd(value || initValueToRender, event),
|
||||||
|
onClick: event => resetCaretIfPastEnd(value || initValueToRender, event),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('div.currency-display__converted-value', {}, `${convertedPrefix}${convertedValue} ${convertedCurrency}`),
|
h('div', {
|
||||||
|
className: convertedBalanceClassName,
|
||||||
|
}, `${convertedPrefix}${convertedValue} ${convertedCurrency}`),
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ function FromDropdown () {
|
|||||||
FromDropdown.prototype.getListItemIcon = function (currentAccount, selectedAccount) {
|
FromDropdown.prototype.getListItemIcon = function (currentAccount, selectedAccount) {
|
||||||
const listItemIcon = h(`i.fa.fa-check.fa-lg`, { style: { color: '#02c9b1' } })
|
const listItemIcon = h(`i.fa.fa-check.fa-lg`, { style: { color: '#02c9b1' } })
|
||||||
|
|
||||||
return currentAccount.identity.address === selectedAccount.identity.address
|
return currentAccount.address === selectedAccount.address
|
||||||
? listItemIcon
|
? listItemIcon
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
47
ui/app/components/send/gas-fee-display-v2.js
Normal file
47
ui/app/components/send/gas-fee-display-v2.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const CurrencyDisplay = require('./currency-display');
|
||||||
|
|
||||||
|
const { multiplyCurrencies } = require('../../conversion-util')
|
||||||
|
|
||||||
|
module.exports = GasFeeDisplay
|
||||||
|
|
||||||
|
inherits(GasFeeDisplay, Component)
|
||||||
|
function GasFeeDisplay () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
GasFeeDisplay.prototype.render = function () {
|
||||||
|
const {
|
||||||
|
conversionRate,
|
||||||
|
gasLimit,
|
||||||
|
gasPrice,
|
||||||
|
onClick,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
const readyToRender = Boolean(gasLimit && gasPrice)
|
||||||
|
|
||||||
|
return h('div', [
|
||||||
|
|
||||||
|
readyToRender
|
||||||
|
? h(CurrencyDisplay, {
|
||||||
|
primaryCurrency: 'ETH',
|
||||||
|
convertedCurrency: 'USD',
|
||||||
|
value: multiplyCurrencies(gasLimit, gasPrice, { toNumericBase: 'hex' }),
|
||||||
|
conversionRate,
|
||||||
|
convertedPrefix: '$',
|
||||||
|
readOnly: true,
|
||||||
|
})
|
||||||
|
: h('div.currency-display', 'Loading...')
|
||||||
|
,
|
||||||
|
|
||||||
|
h('div.send-v2__sliders-icon-container', {
|
||||||
|
onClick,
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-sliders.send-v2__sliders-icon'),
|
||||||
|
])
|
||||||
|
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
@ -73,7 +73,7 @@ GasTooltip.prototype.render = function () {
|
|||||||
step: 1,
|
step: 1,
|
||||||
min: 0,
|
min: 0,
|
||||||
placeholder: '0',
|
placeholder: '0',
|
||||||
initValue: gasPrice,
|
value: gasPrice,
|
||||||
onChange: (newPrice) => this.updateGasPrice(newPrice),
|
onChange: (newPrice) => this.updateGasPrice(newPrice),
|
||||||
}),
|
}),
|
||||||
h('div.gas-tooltip-input-label', {
|
h('div.gas-tooltip-input-label', {
|
||||||
@ -89,7 +89,7 @@ GasTooltip.prototype.render = function () {
|
|||||||
step: 1,
|
step: 1,
|
||||||
min: 0,
|
min: 0,
|
||||||
placeholder: '0',
|
placeholder: '0',
|
||||||
initValue: gasLimit,
|
value: gasLimit,
|
||||||
onChange: (newLimit) => this.updateGasLimit(newLimit),
|
onChange: (newLimit) => this.updateGasLimit(newLimit),
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
33
ui/app/components/send/memo-textarea.js
Normal file
33
ui/app/components/send/memo-textarea.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const Identicon = require('../identicon')
|
||||||
|
|
||||||
|
module.exports = MemoTextArea
|
||||||
|
|
||||||
|
inherits(MemoTextArea, Component)
|
||||||
|
function MemoTextArea () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoTextArea.prototype.render = function () {
|
||||||
|
const { memo, identities, onChange } = this.props
|
||||||
|
|
||||||
|
return h('div.send-v2__memo-text-area', [
|
||||||
|
|
||||||
|
h('textarea.send-v2__memo-text-area__input', {
|
||||||
|
placeholder: 'Optional',
|
||||||
|
value: memo,
|
||||||
|
onChange,
|
||||||
|
// onBlur: () => {
|
||||||
|
// this.setErrorsFor('memo')
|
||||||
|
// },
|
||||||
|
onFocus: event => {
|
||||||
|
// this.clearErrorsFor('memo')
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
62
ui/app/components/send/send-v2-container.js
Normal file
62
ui/app/components/send/send-v2-container.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
const connect = require('react-redux').connect
|
||||||
|
const actions = require('../../actions')
|
||||||
|
const abi = require('ethereumjs-abi')
|
||||||
|
const SendEther = require('../../send-v2')
|
||||||
|
|
||||||
|
const { multiplyCurrencies } = require('../../conversion-util')
|
||||||
|
|
||||||
|
const {
|
||||||
|
accountsWithSendEtherInfoSelector,
|
||||||
|
getCurrentAccountWithSendEtherInfo,
|
||||||
|
conversionRateSelector,
|
||||||
|
getSelectedToken,
|
||||||
|
getSelectedTokenExchangeRate,
|
||||||
|
getSelectedAddress,
|
||||||
|
} = require('../../selectors')
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
const selectedAddress = getSelectedAddress(state);
|
||||||
|
const selectedToken = getSelectedToken(state);
|
||||||
|
const tokenExchangeRates = state.metamask.tokenExchangeRates
|
||||||
|
const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state)
|
||||||
|
const conversionRate = conversionRateSelector(state)
|
||||||
|
|
||||||
|
let data;
|
||||||
|
let primaryCurrency;
|
||||||
|
let tokenToUSDRate;
|
||||||
|
if (selectedToken) {
|
||||||
|
data = Array.prototype.map.call(
|
||||||
|
abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
|
||||||
|
x => ('00' + x.toString(16)).slice(-2)
|
||||||
|
).join('')
|
||||||
|
|
||||||
|
primaryCurrency = selectedToken.symbol
|
||||||
|
|
||||||
|
tokenToUSDRate = multiplyCurrencies(
|
||||||
|
conversionRate,
|
||||||
|
selectedTokenExchangeRate,
|
||||||
|
{ toNumericBase: 'dec' }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
|
||||||
|
accounts: accountsWithSendEtherInfoSelector(state),
|
||||||
|
conversionRate,
|
||||||
|
selectedToken,
|
||||||
|
primaryCurrency,
|
||||||
|
data,
|
||||||
|
tokenToUSDRate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return {
|
||||||
|
showCustomizeGasModal: () => dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' })),
|
||||||
|
estimateGas: params => dispatch(actions.estimateGas(params)),
|
||||||
|
getGasPrice: () => dispatch(actions.getGasPrice()),
|
||||||
|
updateTokenExchangeRate: token => dispatch(actions.updateTokenExchangeRate(token)),
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ function ToAutoComplete () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ToAutoComplete.prototype.render = function () {
|
ToAutoComplete.prototype.render = function () {
|
||||||
const { to, identities, onChange } = this.props
|
const { to, accounts, onChange } = this.props
|
||||||
|
|
||||||
return h('div.send-v2__to-autocomplete', [
|
return h('div.send-v2__to-autocomplete', [
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ ToAutoComplete.prototype.render = function () {
|
|||||||
|
|
||||||
h('datalist#addresses', [
|
h('datalist#addresses', [
|
||||||
// Corresponds to the addresses owned.
|
// Corresponds to the addresses owned.
|
||||||
...Object.entries(identities).map(([key, { address, name }]) => {
|
...Object.entries(accounts).map(([key, { address, name }]) => {
|
||||||
return h('option', {
|
return h('option', {
|
||||||
value: address,
|
value: address,
|
||||||
label: name,
|
label: name,
|
||||||
|
@ -128,7 +128,8 @@ const conversionUtil = (value, {
|
|||||||
value: value || '0',
|
value: value || '0',
|
||||||
});
|
});
|
||||||
|
|
||||||
const addCurrencies = (a, b, { toNumericBase, numberOfDecimals }) => {
|
const addCurrencies = (a, b, options = {}) => {
|
||||||
|
const { toNumericBase, numberOfDecimals } = options
|
||||||
const value = (new BigNumber(a)).add(b);
|
const value = (new BigNumber(a)).add(b);
|
||||||
return converter({
|
return converter({
|
||||||
value,
|
value,
|
||||||
@ -137,6 +138,16 @@ const addCurrencies = (a, b, { toNumericBase, numberOfDecimals }) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const multiplyCurrencies = (a, b, options = {}) => {
|
||||||
|
const { toNumericBase, numberOfDecimals } = options
|
||||||
|
const value = (new BigNumber(a)).times(b);
|
||||||
|
return converter({
|
||||||
|
value,
|
||||||
|
toNumericBase,
|
||||||
|
numberOfDecimals,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const conversionGreaterThan = (
|
const conversionGreaterThan = (
|
||||||
{ value, fromNumericBase },
|
{ value, fromNumericBase },
|
||||||
{ value: compareToValue, fromNumericBase: compareToBase },
|
{ value: compareToValue, fromNumericBase: compareToBase },
|
||||||
@ -152,5 +163,6 @@ const conversionGreaterThan = (
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
conversionUtil,
|
conversionUtil,
|
||||||
addCurrencies,
|
addCurrencies,
|
||||||
|
multiplyCurrencies,
|
||||||
conversionGreaterThan,
|
conversionGreaterThan,
|
||||||
}
|
}
|
@ -24,6 +24,16 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__account-balances {
|
||||||
|
height: auto;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #9b9b9b;
|
||||||
|
margin-left: 34px;
|
||||||
|
margin-top: 4px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
&__account-name {
|
&__account-name {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
@ -34,13 +44,22 @@
|
|||||||
right: 12px;
|
right: 12px;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__account-primary-balance,
|
||||||
|
&__account-secondary-balance {
|
||||||
|
font-family: Roboto;
|
||||||
|
line-height: 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
&__account-primary-balance {
|
&__account-primary-balance {
|
||||||
margin-left: 34px;
|
color: $scorpion;
|
||||||
margin-top: 4px;
|
border: none;
|
||||||
|
outline: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__account-secondary-balance {
|
&__account-secondary-balance {
|
||||||
margin-left: 34px;
|
|
||||||
color: $dusty-gray;
|
color: $dusty-gray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__input-wrapper {
|
|
||||||
margin-top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__input {
|
&__input {
|
||||||
color: $scorpion;
|
color: $scorpion;
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
|
51
ui/app/css/itcss/components/gas-slider.scss
Normal file
51
ui/app/css/itcss/components/gas-slider.scss
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
.gas-slider {
|
||||||
|
position: relative;
|
||||||
|
width: 313px;
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
width: 317px;
|
||||||
|
margin-left: -2px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range] {
|
||||||
|
-webkit-appearance: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none !important;
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
border: 2px solid #B8B8B8;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.08);
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bar {
|
||||||
|
height: 6px;
|
||||||
|
width: 313px;
|
||||||
|
background: $alto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: absolute;
|
||||||
|
top: 11px;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__low, &__high {
|
||||||
|
height: 6px;
|
||||||
|
width: 49px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__low {
|
||||||
|
background-color: $crimson;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__high {
|
||||||
|
background-color: $caribbean-green;
|
||||||
|
}
|
||||||
|
}
|
@ -35,3 +35,6 @@
|
|||||||
@import './account-menu.scss';
|
@import './account-menu.scss';
|
||||||
|
|
||||||
@import './menu.scss';
|
@import './menu.scss';
|
||||||
|
|
||||||
|
@import './gas-slider.scss';
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@
|
|||||||
.gas-tooltip-input-arrows {
|
.gas-tooltip-input-arrows {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 178px;
|
right: 4px;
|
||||||
width: 17px;
|
width: 17px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
border: 1px solid #dadada;
|
border: 1px solid #dadada;
|
||||||
@ -420,7 +420,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__send-eth-icon {
|
&__send-header-icon-container {
|
||||||
|
z-index: 25;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__send-header-icon {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
@ -428,11 +437,6 @@
|
|||||||
z-index: 25;
|
z-index: 25;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
|
|
||||||
@media screen and (max-width: $break-small) {
|
|
||||||
position: relative;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__send-arrow-icon {
|
&__send-arrow-icon {
|
||||||
@ -472,7 +476,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
left: 178px;
|
left: 178px;
|
||||||
top: 71px;
|
top: 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
@ -512,7 +516,9 @@
|
|||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
margin-top: 16px;
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__from-dropdown {
|
&__from-dropdown {
|
||||||
@ -550,7 +556,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__to-autocomplete {
|
&__to-autocomplete, &__memo-text-area {
|
||||||
&__input {
|
&__input {
|
||||||
height: 54px;
|
height: 54px;
|
||||||
width: 240px;
|
width: 240px;
|
||||||
@ -566,6 +572,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__sliders-icon-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
border: 1px solid $curious-blue;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: $white;
|
||||||
|
padding: 5px;
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__sliders-icon {
|
||||||
|
color: $curious-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__memo-text-area {
|
||||||
|
&__input {
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
height: 92px;
|
height: 92px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -573,8 +605,7 @@
|
|||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-top: 1px solid $alto;
|
border-top: 1px solid $alto;
|
||||||
position: absolute;
|
margin-top: 29px;
|
||||||
bottom: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__next-btn,
|
&__next-btn,
|
||||||
@ -607,4 +638,155 @@
|
|||||||
color: $dusty-gray;
|
color: $dusty-gray;
|
||||||
border-color: $dusty-gray;
|
border-color: $dusty-gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__customize-gas {
|
||||||
|
border: 1px solid #D8D8D8;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0,0,0,0.14);
|
||||||
|
font-family: Roboto;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
width: 355px;
|
||||||
|
height: 598px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
height: 52px;
|
||||||
|
border-bottom: 1px solid $alto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin-left: 19.25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__close::after {
|
||||||
|
content: '\00D7';
|
||||||
|
font-size: 1.8em;
|
||||||
|
color: $dusty-gray;
|
||||||
|
font-family: sans-serif;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 19.25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
height: 248px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
width: 355px;
|
||||||
|
height: 470px;
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
height: 75px;
|
||||||
|
border-top: 1px solid $alto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 181.75px;
|
||||||
|
margin-right: 21.25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__revert, &__cancel, &__save {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__revert {
|
||||||
|
color: $silver-chalice;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-left: 21.25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cancel, &__save {
|
||||||
|
height: 34.64px;
|
||||||
|
width: 85.74px;
|
||||||
|
border: 1px solid $dusty-gray;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-family: 'DIN OT';
|
||||||
|
font-size: 12px;
|
||||||
|
color: $dusty-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__gas-modal-card {
|
||||||
|
width: 360px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
height: 26px;
|
||||||
|
width: 84px;
|
||||||
|
color: $tundora;
|
||||||
|
font-family: Roboto;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 26px;
|
||||||
|
margin-top: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__copy {
|
||||||
|
height: 38px;
|
||||||
|
width: 314px;
|
||||||
|
color: $tundora;
|
||||||
|
font-family: Roboto;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 19px;
|
||||||
|
margin-top: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-gas-input-wrapper {
|
||||||
|
margin-top: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-gas-input {
|
||||||
|
height: 54px;
|
||||||
|
width: 315px;
|
||||||
|
border: 1px solid $geyser;
|
||||||
|
background-color: $white;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gas-tooltip-input-arrows {
|
||||||
|
width: 32px;
|
||||||
|
height: 54px;
|
||||||
|
border-left: 1px solid #dadada;
|
||||||
|
font-size: 18px;
|
||||||
|
color: $tundora;
|
||||||
|
right: 0px;
|
||||||
|
padding: 1px 4px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"]::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"]:hover::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,16 @@ const selectors = {
|
|||||||
getSelectedIdentity,
|
getSelectedIdentity,
|
||||||
getSelectedAccount,
|
getSelectedAccount,
|
||||||
getSelectedToken,
|
getSelectedToken,
|
||||||
|
getSelectedTokenExchangeRate,
|
||||||
conversionRateSelector,
|
conversionRateSelector,
|
||||||
transactionsSelector,
|
transactionsSelector,
|
||||||
|
accountsWithSendEtherInfoSelector,
|
||||||
|
getCurrentAccountWithSendEtherInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = selectors
|
module.exports = selectors
|
||||||
|
|
||||||
function getSelectedAddress (state) {
|
function getSelectedAddress (state) {
|
||||||
// TODO: accounts is not defined. Is it needed?
|
|
||||||
const selectedAddress = state.metamask.selectedAddress || Object.keys(state.metamask.accounts)[0]
|
const selectedAddress = state.metamask.selectedAddress || Object.keys(state.metamask.accounts)[0]
|
||||||
|
|
||||||
return selectedAddress
|
return selectedAddress
|
||||||
@ -40,10 +42,41 @@ function getSelectedToken (state) {
|
|||||||
return selectedToken || null
|
return selectedToken || null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSelectedTokenExchangeRate (state) {
|
||||||
|
const tokenExchangeRates = state.metamask.tokenExchangeRates
|
||||||
|
const selectedToken = getSelectedToken(state) || {}
|
||||||
|
const { symbol = '' } = selectedToken
|
||||||
|
|
||||||
|
const pair = `${symbol.toLowerCase()}_eth`
|
||||||
|
const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
|
||||||
|
|
||||||
|
return tokenExchangeRate
|
||||||
|
}
|
||||||
|
|
||||||
function conversionRateSelector (state) {
|
function conversionRateSelector (state) {
|
||||||
return state.metamask.conversionRate
|
return state.metamask.conversionRate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function accountsWithSendEtherInfoSelector (state) {
|
||||||
|
const {
|
||||||
|
accounts,
|
||||||
|
identities,
|
||||||
|
} = state.metamask
|
||||||
|
|
||||||
|
const accountsWithSendEtherInfo = Object.entries(accounts).map(([key, account]) => {
|
||||||
|
return Object.assign({}, account, identities[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
return accountsWithSendEtherInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentAccountWithSendEtherInfo (state) {
|
||||||
|
const currentAddress = getSelectedAddress(state)
|
||||||
|
const accounts = accountsWithSendEtherInfoSelector(state)
|
||||||
|
|
||||||
|
return accounts.find(({ address }) => address === currentAddress)
|
||||||
|
}
|
||||||
|
|
||||||
function transactionsSelector (state) {
|
function transactionsSelector (state) {
|
||||||
const { network, selectedTokenAddress } = state.metamask
|
const { network, selectedTokenAddress } = state.metamask
|
||||||
const unapprovedMsgs = valuesFor(state.metamask.unapprovedMsgs)
|
const unapprovedMsgs = valuesFor(state.metamask.unapprovedMsgs)
|
||||||
|
@ -2,61 +2,135 @@ 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 Identicon = require('./components/identicon')
|
||||||
const FromDropdown = require('./components/send/from-dropdown')
|
const FromDropdown = require('./components/send/from-dropdown')
|
||||||
const ToAutoComplete = require('./components/send/to-autocomplete')
|
const ToAutoComplete = require('./components/send/to-autocomplete')
|
||||||
const CurrencyDisplay = require('./components/send/currency-display')
|
const CurrencyDisplay = require('./components/send/currency-display')
|
||||||
|
const MemoTextArea = require('./components/send/memo-textarea')
|
||||||
|
const GasFeeDisplay = require('./components/send/gas-fee-display-v2')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
const { showModal } = require('./actions')
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
module.exports = SendTransactionScreen
|
||||||
const mockAccounts = Array.from(new Array(5))
|
|
||||||
.map((v, i) => ({
|
|
||||||
identity: {
|
|
||||||
name: `Test Account Name ${i}`,
|
|
||||||
address: `0x02f567704cc6569127e18e3d00d2c85bcbfa6f0${i}`,
|
|
||||||
},
|
|
||||||
balancesToRender: {
|
|
||||||
primary: `100${i}.000001 ETH`,
|
|
||||||
secondary: `$30${i},000.00 USD`,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
const conversionRate = 301.0005
|
|
||||||
|
|
||||||
return {
|
|
||||||
accounts: mockAccounts,
|
|
||||||
conversionRate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(SendTransactionScreen, PersistentForm)
|
inherits(SendTransactionScreen, PersistentForm)
|
||||||
function SendTransactionScreen () {
|
function SendTransactionScreen () {
|
||||||
PersistentForm.call(this)
|
PersistentForm.call(this)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
newTx: {
|
|
||||||
from: '',
|
from: '',
|
||||||
to: '',
|
to: '',
|
||||||
gasPrice: null,
|
gasPrice: null,
|
||||||
gas: '0.001',
|
gasLimit: null,
|
||||||
amount: '10',
|
amount: '0x0',
|
||||||
txData: null,
|
txData: null,
|
||||||
memo: '',
|
memo: '',
|
||||||
},
|
|
||||||
dropdownOpen: false,
|
dropdownOpen: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SendTransactionScreen.prototype.componentWillMount = function () {
|
||||||
|
const {
|
||||||
|
updateTokenExchangeRate,
|
||||||
|
selectedToken = {},
|
||||||
|
getGasPrice,
|
||||||
|
estimateGas,
|
||||||
|
selectedAddress,
|
||||||
|
data,
|
||||||
|
} = this.props
|
||||||
|
const { symbol } = selectedToken || {}
|
||||||
|
|
||||||
|
const estimateGasParams = {
|
||||||
|
from: selectedAddress,
|
||||||
|
gas: '746a528800',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol) {
|
||||||
|
updateTokenExchangeRate(symbol)
|
||||||
|
Object.assign(estimateGasParams, { value: '0x0' })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
Object.assign(estimateGasParams, { data })
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
getGasPrice(),
|
||||||
|
estimateGas({
|
||||||
|
from: selectedAddress,
|
||||||
|
gas: '746a528800',
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
.then(([blockGasPrice, estimatedGas]) => {
|
||||||
|
this.setState({
|
||||||
|
gasPrice: blockGasPrice,
|
||||||
|
gasLimit: estimatedGas,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
SendTransactionScreen.prototype.renderHeaderIcon = function () {
|
||||||
|
const { selectedToken } = this.props
|
||||||
|
|
||||||
|
return h('div.send-v2__send-header-icon-container', [
|
||||||
|
selectedToken
|
||||||
|
? h(Identicon, {
|
||||||
|
diameter: 40,
|
||||||
|
address: selectedToken.address,
|
||||||
|
})
|
||||||
|
: h('img.send-v2__send-header-icon', { src: '../images/eth_logo.svg' })
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
SendTransactionScreen.prototype.renderTitle = function () {
|
||||||
|
const { selectedToken } = this.props
|
||||||
|
|
||||||
|
return h('div.send-v2__title', [selectedToken ? 'Send Tokens' : 'Send Funds'])
|
||||||
|
}
|
||||||
|
|
||||||
|
SendTransactionScreen.prototype.renderCopy = function () {
|
||||||
|
const { selectedToken } = this.props
|
||||||
|
|
||||||
|
const tokenText = selectedToken ? 'tokens' : 'ETH'
|
||||||
|
|
||||||
|
return h('div', [
|
||||||
|
|
||||||
|
h('div.send-v2__copy', `Only send ${tokenText} to an Ethereum address.`),
|
||||||
|
|
||||||
|
h('div.send-v2__copy', 'Sending to a different crytpocurrency that is not Ethereum may result in permanent loss.'),
|
||||||
|
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.render = function () {
|
SendTransactionScreen.prototype.render = function () {
|
||||||
const { accounts, conversionRate } = this.props
|
const {
|
||||||
const { dropdownOpen, newTx } = this.state
|
accounts,
|
||||||
const { to, amount, gas } = newTx
|
conversionRate,
|
||||||
|
tokenToUSDRate,
|
||||||
|
selectedToken,
|
||||||
|
showCustomizeGasModal,
|
||||||
|
selectedAccount,
|
||||||
|
primaryCurrency = 'ETH',
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
const {
|
||||||
|
dropdownOpen,
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
gasLimit,
|
||||||
|
gasPrice,
|
||||||
|
memo,
|
||||||
|
} = this.state
|
||||||
|
|
||||||
|
const amountConversionRate = selectedToken ? tokenToUSDRate : conversionRate
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
h('div.send-v2__container', [
|
h('div.send-v2__container', [
|
||||||
h('div.send-v2__header', {}, [
|
h('div.send-v2__header', {}, [
|
||||||
|
|
||||||
h('img.send-v2__send-eth-icon', { src: '../images/eth_logo.svg' }),
|
this.renderHeaderIcon(),
|
||||||
|
|
||||||
h('div.send-v2__arrow-background', [
|
h('div.send-v2__arrow-background', [
|
||||||
h('i.fa.fa-lg.fa-arrow-circle-right.send-v2__send-arrow-icon'),
|
h('i.fa.fa-lg.fa-arrow-circle-right.send-v2__send-arrow-icon'),
|
||||||
@ -66,11 +140,9 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('div.send-v2__title', 'Send Funds'),
|
this.renderTitle(),
|
||||||
|
|
||||||
h('div.send-v2__copy', 'Only send ETH to an Ethereum address.'),
|
this.renderCopy(),
|
||||||
|
|
||||||
h('div.send-v2__copy', 'Sending to a different crytpocurrency that is not Ethereum may result in permanent loss.'),
|
|
||||||
|
|
||||||
h('div.send-v2__form', {}, [
|
h('div.send-v2__form', {}, [
|
||||||
|
|
||||||
@ -81,10 +153,11 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
h(FromDropdown, {
|
h(FromDropdown, {
|
||||||
dropdownOpen,
|
dropdownOpen,
|
||||||
accounts,
|
accounts,
|
||||||
selectedAccount: accounts[0],
|
selectedAccount,
|
||||||
setFromField: () => console.log('Set From Field'),
|
setFromField: () => console.log('Set From Field'),
|
||||||
openDropdown: () => this.setState({ dropdownOpen: true }),
|
openDropdown: () => this.setState({ dropdownOpen: true }),
|
||||||
closeDropdown: () => this.setState({ dropdownOpen: false }),
|
closeDropdown: () => this.setState({ dropdownOpen: false }),
|
||||||
|
conversionRate,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
]),
|
]),
|
||||||
@ -95,13 +168,11 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
|
|
||||||
h(ToAutoComplete, {
|
h(ToAutoComplete, {
|
||||||
to,
|
to,
|
||||||
identities: accounts.map(({ identity }) => identity),
|
accounts,
|
||||||
onChange: (event) => {
|
onChange: (event) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
newTx: {
|
...this.state,
|
||||||
...this.state.newTx,
|
|
||||||
to: event.target.value,
|
to: event.target.value,
|
||||||
},
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -113,17 +184,15 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
h('div.send-v2__form-label', 'Amount:'),
|
h('div.send-v2__form-label', 'Amount:'),
|
||||||
|
|
||||||
h(CurrencyDisplay, {
|
h(CurrencyDisplay, {
|
||||||
primaryCurrency: 'ETH',
|
primaryCurrency,
|
||||||
convertedCurrency: 'USD',
|
convertedCurrency: 'USD',
|
||||||
value: amount,
|
value: amount,
|
||||||
conversionRate,
|
conversionRate: amountConversionRate,
|
||||||
convertedPrefix: '$',
|
convertedPrefix: '$',
|
||||||
handleChange: (value) => {
|
handleChange: (value) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
newTx: {
|
...this.state,
|
||||||
...this.state.newTx,
|
|
||||||
amount: value,
|
amount: value,
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@ -134,13 +203,33 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
|
|
||||||
h('div.send-v2__form-label', 'Gas fee:'),
|
h('div.send-v2__form-label', 'Gas fee:'),
|
||||||
|
|
||||||
h(CurrencyDisplay, {
|
h(GasFeeDisplay, {
|
||||||
primaryCurrency: 'ETH',
|
gasLimit,
|
||||||
convertedCurrency: 'USD',
|
gasPrice,
|
||||||
value: gas,
|
|
||||||
conversionRate,
|
conversionRate,
|
||||||
convertedPrefix: '$',
|
onClick: showCustomizeGasModal,
|
||||||
readOnly: true,
|
}),
|
||||||
|
|
||||||
|
h('div.send-v2__sliders-icon-container', {
|
||||||
|
onClick: showCustomizeGasModal,
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-sliders.send-v2__sliders-icon'),
|
||||||
|
])
|
||||||
|
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('div.send-v2__form-row', [
|
||||||
|
|
||||||
|
h('div.send-v2__form-label', 'Transaction Memo:'),
|
||||||
|
|
||||||
|
h(MemoTextArea, {
|
||||||
|
memo,
|
||||||
|
onChange: (event) => {
|
||||||
|
this.setState({
|
||||||
|
...this.state,
|
||||||
|
memo: event.target.value,
|
||||||
|
})
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
]),
|
]),
|
||||||
|
Loading…
Reference in New Issue
Block a user