aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components
diff options
context:
space:
mode:
authorChi Kei Chan <chikeichan@gmail.com>2017-10-26 10:46:42 +0800
committerChi Kei Chan <chikeichan@gmail.com>2017-10-26 10:46:42 +0800
commitdb5326dfc288015a4d9f792653a71fd4d7abca33 (patch)
treecb93f52f999816d07a6146274093aaebdb7ed8d3 /ui/app/components
parenta9a841ba01f6bffa29a3e0491e3a88f6aff72ac7 (diff)
parentb2e440e4ffba6db29cf8a928a41534c1f204d485 (diff)
downloadtangerine-wallet-browser-db5326dfc288015a4d9f792653a71fd4d7abca33.tar.gz
tangerine-wallet-browser-db5326dfc288015a4d9f792653a71fd4d7abca33.tar.zst
tangerine-wallet-browser-db5326dfc288015a4d9f792653a71fd4d7abca33.zip
Merge branch 'NewUI-flat' into uat
Diffstat (limited to 'ui/app/components')
-rw-r--r--ui/app/components/account-dropdowns.js7
-rw-r--r--ui/app/components/account-menu/index.js20
-rw-r--r--ui/app/components/bn-as-decimal-input.js16
-rw-r--r--ui/app/components/buy-button-subview.js2
-rw-r--r--ui/app/components/customize-gas-modal/index.js162
-rw-r--r--ui/app/components/dropdowns/account-dropdown-mini.js78
-rw-r--r--ui/app/components/dropdowns/components/dropdown.js4
-rw-r--r--ui/app/components/dropdowns/simple-dropdown.js91
-rw-r--r--ui/app/components/editable-label.js114
-rw-r--r--ui/app/components/ens-input.js2
-rw-r--r--ui/app/components/identicon.js2
-rw-r--r--ui/app/components/menu-droppo.js2
-rw-r--r--ui/app/components/modals/account-details-modal.js21
-rw-r--r--ui/app/components/modals/buy-options-modal.js4
-rw-r--r--ui/app/components/modals/export-private-key-modal.js5
-rw-r--r--ui/app/components/modals/modal.js7
-rw-r--r--ui/app/components/modals/new-account-modal.js16
-rw-r--r--ui/app/components/network.js7
-rw-r--r--ui/app/components/notice.js2
-rw-r--r--ui/app/components/pending-personal-msg.js47
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js4
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js4
-rw-r--r--ui/app/components/qr-code.js10
-rw-r--r--ui/app/components/send/account-list-item.js8
-rw-r--r--ui/app/components/send/currency-display.js42
-rw-r--r--ui/app/components/send/send-constants.js20
-rw-r--r--ui/app/components/send/send-utils.js39
-rw-r--r--ui/app/components/send/send-v2-container.js9
-rw-r--r--ui/app/components/send/to-autocomplete.js118
-rw-r--r--ui/app/components/shapeshift-form.js4
-rw-r--r--ui/app/components/signature-request.js255
-rw-r--r--ui/app/components/tab-bar.js63
-rw-r--r--ui/app/components/tx-view.js9
-rw-r--r--ui/app/components/typed-message-renderer.js4
-rw-r--r--ui/app/components/wallet-view.js112
35 files changed, 981 insertions, 329 deletions
diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js
index 1b46e532a..0c34a5154 100644
--- a/ui/app/components/account-dropdowns.js
+++ b/ui/app/components/account-dropdowns.js
@@ -161,8 +161,6 @@ class AccountDropdowns extends Component {
)
}
-
-
renderAccountOptions () {
const { actions } = this.props
const { optionsMenuActive } = this.state
@@ -297,6 +295,11 @@ AccountDropdowns.propTypes = {
identities: PropTypes.objectOf(PropTypes.object),
selected: PropTypes.string,
keyrings: PropTypes.array,
+ actions: PropTypes.objectOf(PropTypes.func),
+ network: PropTypes.string,
+ style: PropTypes.object,
+ enableAccountOptions: PropTypes.bool,
+ enableAccountsSelector: PropTypes.bool,
}
const mapDispatchToProps = (dispatch) => {
diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js
index 85bd21076..38c7bcb2d 100644
--- a/ui/app/components/account-menu/index.js
+++ b/ui/app/components/account-menu/index.js
@@ -46,6 +46,10 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.showImportPage())
dispatch(actions.toggleAccountMenu())
},
+ showInfoPage: () => {
+ dispatch(actions.showInfoPage())
+ dispatch(actions.toggleAccountMenu())
+ },
}
}
@@ -57,16 +61,18 @@ AccountMenu.prototype.render = function () {
showImportPage,
lockMetamask,
showConfigPage,
+ showInfoPage,
} = this.props
return h(Menu, { className: 'account-menu', isShowing: isAccountMenuOpen }, [
h(CloseArea, { onClick: toggleAccountMenu }),
h(Item, {
className: 'account-menu__header',
- onClick: lockMetamask,
}, [
'My Accounts',
- h('button.account-menu__logout-button', 'Log out'),
+ h('button.account-menu__logout-button', {
+ onClick: lockMetamask,
+ }, 'Log out'),
]),
h(Divider),
h('div.account-menu__accounts', this.renderAccounts()),
@@ -83,6 +89,7 @@ AccountMenu.prototype.render = function () {
}),
h(Divider),
h(Item, {
+ onClick: showInfoPage,
icon: h('img', { src: 'images/mm-info-icon.svg' }),
text: 'Info & Help',
}),
@@ -98,15 +105,14 @@ AccountMenu.prototype.renderAccounts = function () {
const {
identities,
accounts,
- selected,
+ selectedAddress,
keyrings,
showAccountDetail,
} = this.props
- console.log({ accounts })
return Object.keys(identities).map((key, index) => {
const identity = identities[key]
- const isSelected = identity.address === selected
+ const isSelected = identity.address === selectedAddress
const balanceValue = accounts[key] ? accounts[key].balance : ''
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...'
@@ -122,7 +128,7 @@ AccountMenu.prototype.renderAccounts = function () {
{ onClick: () => showAccountDetail(identity.address) },
[
h('div.account-menu__check-mark', [
- isSelected ? h('i.fa.fa-check') : null,
+ isSelected ? h('div.account-menu__check-mark-icon') : null,
]),
h(
@@ -148,6 +154,6 @@ AccountMenu.prototype.indicateIfLoose = function (keyring) {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
- return isLoose ? h('.keyring-label', 'LOOSE') : null
+ return isLoose ? h('.keyring-label', 'IMPORTED') : null
} catch (e) { return }
}
diff --git a/ui/app/components/bn-as-decimal-input.js b/ui/app/components/bn-as-decimal-input.js
index d84834d06..22e37602e 100644
--- a/ui/app/components/bn-as-decimal-input.js
+++ b/ui/app/components/bn-as-decimal-input.js
@@ -31,6 +31,8 @@ BnAsDecimalInput.prototype.render = function () {
const suffix = props.suffix
const style = props.style
const valueString = value.toString(10)
+ const newMin = min && this.downsize(min.toString(10), scale)
+ const newMax = max && this.downsize(max.toString(10), scale)
const newValue = this.downsize(valueString, scale)
return (
@@ -47,8 +49,8 @@ BnAsDecimalInput.prototype.render = function () {
type: 'number',
step: 'any',
required: true,
- min,
- max,
+ min: newMin,
+ max: newMax,
style: extend({
display: 'block',
textAlign: 'right',
@@ -128,15 +130,17 @@ BnAsDecimalInput.prototype.updateValidity = function (event) {
}
BnAsDecimalInput.prototype.constructWarning = function () {
- const { name, min, max } = this.props
+ const { name, min, max, scale, suffix } = this.props
+ const newMin = min && this.downsize(min.toString(10), scale)
+ const newMax = max && this.downsize(max.toString(10), scale)
let message = name ? name + ' ' : ''
if (min && max) {
- message += `must be greater than or equal to ${min} and less than or equal to ${max}.`
+ message += `must be greater than or equal to ${newMin} ${suffix} and less than or equal to ${newMax} ${suffix}.`
} else if (min) {
- message += `must be greater than or equal to ${min}.`
+ message += `must be greater than or equal to ${newMin} ${suffix}.`
} else if (max) {
- message += `must be less than or equal to ${max}.`
+ message += `must be less than or equal to ${newMax} ${suffix}.`
} else {
message += 'Invalid input.'
}
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
index a36f41df5..d5958787b 100644
--- a/ui/app/components/buy-button-subview.js
+++ b/ui/app/components/buy-button-subview.js
@@ -76,7 +76,7 @@ BuyButtonSubview.prototype.headerSubview = function () {
paddingTop: '4px',
paddingBottom: '4px',
},
- }, 'Buy Eth'),
+ }, 'Deposit Eth'),
]),
// loading indication
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index 744891c47..722ed2b23 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -6,23 +6,46 @@ const actions = require('../../actions')
const GasModalCard = require('./gas-modal-card')
const {
- MIN_GAS_PRICE,
- MIN_GAS_LIMIT,
+ MIN_GAS_PRICE_DEC,
+ MIN_GAS_LIMIT_DEC,
+ MIN_GAS_PRICE_GWEI,
} = require('../send/send-constants')
-const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
+const {
+ isBalanceSufficient,
+} = require('../send/send-utils')
+
+const {
+ conversionUtil,
+ multiplyCurrencies,
+ conversionGreaterThan,
+} = require('../../conversion-util')
const {
getGasPrice,
getGasLimit,
conversionRateSelector,
+ getSendAmount,
+ getSelectedToken,
+ getSendFrom,
+ getCurrentAccountWithSendEtherInfo,
+ getSelectedTokenToFiatRate,
} = require('../../selectors')
function mapStateToProps (state) {
+ const selectedToken = getSelectedToken(state)
+ const currentAccount = getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state)
+ const conversionRate = conversionRateSelector(state)
+
return {
gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state),
- conversionRate: conversionRateSelector(state),
+ conversionRate,
+ amount: getSendAmount(state),
+ balance: currentAccount.balance,
+ primaryCurrency: selectedToken && selectedToken.symbol,
+ selectedToken,
+ amountConversionRate: selectedToken ? getSelectedTokenToFiatRate(state) : conversionRate,
}
}
@@ -35,19 +58,34 @@ function mapDispatchToProps (dispatch) {
}
}
+function getOriginalState(props) {
+ const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
+ const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
+
+ const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
+ toNumericBase: 'hex',
+ multiplicandBase: 16,
+ multiplierBase: 16,
+ })
+
+ return {
+ gasPrice,
+ gasLimit,
+ gasTotal,
+ error: null,
+ }
+}
+
inherits(CustomizeGasModal, Component)
function CustomizeGasModal (props) {
Component.call(this)
- this.state = {
- gasPrice: props.gasPrice || MIN_GAS_PRICE,
- gasLimit: props.gasLimit || MIN_GAS_LIMIT,
- }
+ this.state = getOriginalState(props)
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal)
-CustomizeGasModal.prototype.save = function (gasPrice, gasLimit) {
+CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
const {
updateGasPrice,
updateGasLimit,
@@ -55,41 +93,105 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit) {
updateGasTotal
} = this.props
- const newGasTotal = multiplyCurrencies(gasLimit, gasPrice, {
- toNumericBase: 'hex',
- multiplicandBase: 16,
- multiplierBase: 16,
- })
-
updateGasPrice(gasPrice)
updateGasLimit(gasLimit)
- updateGasTotal(newGasTotal)
+ updateGasTotal(gasTotal)
hideModal()
}
+CustomizeGasModal.prototype.revert = function () {
+ this.setState(getOriginalState(this.props))
+}
+
+CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
+ const {
+ amount,
+ balance,
+ primaryCurrency,
+ selectedToken,
+ amountConversionRate,
+ conversionRate,
+ } = this.props
+
+ let error = null
+
+ const balanceIsSufficient = isBalanceSufficient({
+ amount,
+ gasTotal,
+ balance,
+ primaryCurrency,
+ selectedToken,
+ amountConversionRate,
+ conversionRate,
+ })
+
+ if (!balanceIsSufficient) {
+ error = 'Insufficient balance for current gas total'
+ }
+
+ const gasLimitTooLow = gasLimit && conversionGreaterThan(
+ {
+ value: MIN_GAS_LIMIT_DEC,
+ fromNumericBase: 'dec',
+ conversionRate,
+ },
+ {
+ value: gasLimit,
+ fromNumericBase: 'hex',
+ },
+ )
+
+ if (gasLimitTooLow) {
+ error = 'Gas limit must be at least 21000'
+ }
+
+ this.setState({ error })
+ return error
+}
+
CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) {
- const convertedGasLimit = conversionUtil(newGasLimit, {
+ const { gasPrice } = this.state
+
+ const gasLimit = conversionUtil(newGasLimit, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
})
- this.setState({ gasLimit: convertedGasLimit })
+ const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
+ toNumericBase: 'hex',
+ multiplicandBase: 16,
+ multiplierBase: 16,
+ })
+
+ this.validate({ gasTotal, gasLimit })
+
+ this.setState({ gasTotal, gasLimit })
}
CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
- const convertedGasPrice = conversionUtil(newGasPrice, {
+ const { gasLimit } = this.state
+
+ const gasPrice = conversionUtil(newGasPrice, {
fromNumericBase: 'dec',
toNumericBase: 'hex',
fromDenomination: 'GWEI',
toDenomination: 'WEI',
})
- this.setState({ gasPrice: convertedGasPrice })
+ const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
+ toNumericBase: 'hex',
+ multiplicandBase: 16,
+ multiplierBase: 16,
+ })
+
+ this.validate({ gasTotal })
+
+ this.setState({ gasTotal, gasPrice })
}
CustomizeGasModal.prototype.render = function () {
const { hideModal, conversionRate } = this.props
- const { gasPrice, gasLimit } = this.state
+ const { gasPrice, gasLimit, gasTotal, error } = this.state
const convertedGasPrice = conversionUtil(gasPrice, {
fromNumericBase: 'hex',
@@ -104,7 +206,7 @@ CustomizeGasModal.prototype.render = function () {
})
return h('div.send-v2__customize-gas', {}, [
- h('div', {
+ h('div.send-v2__customize-gas__content', {
}, [
h('div.send-v2__customize-gas__header', {}, [
@@ -120,17 +222,17 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, {
value: convertedGasPrice,
- min: MIN_GAS_PRICE,
+ min: MIN_GAS_PRICE_GWEI,
// max: 1000,
step: 1,
onChange: value => this.convertAndSetGasPrice(value),
- title: 'Gas Price',
+ title: 'Gas Price (GWEI)',
copy: 'We calculate the suggested gas prices based on network success rates.',
}),
h(GasModalCard, {
value: convertedGasLimit,
- min: MIN_GAS_LIMIT,
+ min: 1,
// max: 100000,
step: 1,
onChange: value => this.convertAndSetGasLimit(value),
@@ -141,9 +243,13 @@ CustomizeGasModal.prototype.render = function () {
]),
h('div.send-v2__customize-gas__footer', {}, [
+
+ error && h('div.send-v2__customize-gas__error-message', [
+ error,
+ ]),
h('div.send-v2__customize-gas__revert', {
- onClick: () => console.log('Revert'),
+ onClick: () => this.revert(),
}, ['Revert']),
h('div.send-v2__customize-gas__buttons', [
@@ -151,8 +257,8 @@ CustomizeGasModal.prototype.render = function () {
onClick: this.props.hideModal,
}, ['CANCEL']),
- h('div.send-v2__customize-gas__save', {
- onClick: () => this.save(gasPrice, gasLimit),
+ h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, {
+ onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal),
}, ['SAVE']),
])
diff --git a/ui/app/components/dropdowns/account-dropdown-mini.js b/ui/app/components/dropdowns/account-dropdown-mini.js
new file mode 100644
index 000000000..96057d2b4
--- /dev/null
+++ b/ui/app/components/dropdowns/account-dropdown-mini.js
@@ -0,0 +1,78 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const Identicon = require('../identicon')
+const AccountListItem = require('../send/account-list-item')
+
+module.exports = AccountDropdownMini
+
+inherits(AccountDropdownMini, Component)
+function AccountDropdownMini () {
+ Component.call(this)
+}
+
+AccountDropdownMini.prototype.getListItemIcon = function (currentAccount, selectedAccount) {
+ const listItemIcon = h(`i.fa.fa-check.fa-lg`, { style: { color: '#02c9b1' } })
+
+ return currentAccount.address === selectedAccount.address
+ ? listItemIcon
+ : null
+}
+
+AccountDropdownMini.prototype.renderDropdown = function () {
+ const {
+ accounts,
+ selectedAccount,
+ closeDropdown,
+ onSelect,
+ } = this.props
+
+ return h('div', {}, [
+
+ h('div.account-dropdown-mini__close-area', {
+ onClick: closeDropdown,
+ }),
+
+ h('div.account-dropdown-mini__list', {}, [
+
+ ...accounts.map(account => h(AccountListItem, {
+ account,
+ displayBalance: false,
+ displayAddress: false,
+ handleClick: () => {
+ onSelect(account)
+ closeDropdown()
+ },
+ icon: this.getListItemIcon(account, selectedAccount),
+ }))
+
+ ]),
+
+ ])
+}
+
+AccountDropdownMini.prototype.render = function () {
+ const {
+ accounts,
+ selectedAccount,
+ openDropdown,
+ closeDropdown,
+ dropdownOpen,
+ } = this.props
+
+ return h('div.account-dropdown-mini', {}, [
+
+ h(AccountListItem, {
+ account: selectedAccount,
+ handleClick: openDropdown,
+ displayBalance: false,
+ displayAddress: false,
+ icon: h(`i.fa.fa-caret-down.fa-lg`, { style: { color: '#dedede' } })
+ }),
+
+ dropdownOpen && this.renderDropdown(),
+
+ ])
+
+}
+
diff --git a/ui/app/components/dropdowns/components/dropdown.js b/ui/app/components/dropdowns/components/dropdown.js
index 991c89cb8..ca68e55f7 100644
--- a/ui/app/components/dropdowns/components/dropdown.js
+++ b/ui/app/components/dropdowns/components/dropdown.js
@@ -65,6 +65,9 @@ Dropdown.propTypes = {
onClick: PropTypes.func.isRequired,
children: PropTypes.node,
style: PropTypes.object.isRequired,
+ onClickOutside: PropTypes.func,
+ innerStyle: PropTypes.object,
+ useCssTransition: PropTypes.bool,
}
class DropdownMenuItem extends Component {
@@ -100,6 +103,7 @@ DropdownMenuItem.propTypes = {
closeMenu: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
children: PropTypes.node,
+ style: PropTypes.object,
}
module.exports = {
diff --git a/ui/app/components/dropdowns/simple-dropdown.js b/ui/app/components/dropdowns/simple-dropdown.js
new file mode 100644
index 000000000..8cea78518
--- /dev/null
+++ b/ui/app/components/dropdowns/simple-dropdown.js
@@ -0,0 +1,91 @@
+const { Component, PropTypes } = require('react')
+const h = require('react-hyperscript')
+const classnames = require('classnames')
+const R = require('ramda')
+
+class SimpleDropdown extends Component {
+ constructor (props) {
+ super(props)
+ this.state = {
+ isOpen: false,
+ }
+ }
+
+ getDisplayValue () {
+ const { selectedOption, options } = this.props
+ const matchesOption = option => option.value === selectedOption
+ const matchingOption = R.find(matchesOption)(options)
+ return matchingOption
+ ? matchingOption.displayValue || matchingOption.value
+ : selectedOption
+ }
+
+ handleClose () {
+ this.setState({ isOpen: false })
+ }
+
+ toggleOpen () {
+ const { isOpen } = this.state
+ this.setState({ isOpen: !isOpen })
+ }
+
+ renderOptions () {
+ const { options, onSelect, selectedOption } = this.props
+
+ return h('div', [
+ h('div.simple-dropdown__close-area', {
+ onClick: event => {
+ event.stopPropagation()
+ this.handleClose()
+ },
+ }),
+ h('div.simple-dropdown__options', [
+ ...options.map(option => {
+ return h(
+ 'div.simple-dropdown__option',
+ {
+ className: classnames({
+ 'simple-dropdown__option--selected': option.value === selectedOption,
+ }),
+ key: option.value,
+ onClick: () => {
+ if (option.value !== selectedOption) {
+ onSelect(option.value)
+ }
+
+ this.handleClose()
+ },
+ },
+ option.displayValue || option.value,
+ )
+ }),
+ ]),
+ ])
+ }
+
+ render () {
+ const { placeholder } = this.props
+ const { isOpen } = this.state
+
+ return h(
+ 'div.simple-dropdown',
+ {
+ onClick: () => this.toggleOpen(),
+ },
+ [
+ h('div.simple-dropdown__selected', this.getDisplayValue() || placeholder || 'Select'),
+ h('i.fa.fa-caret-down.fa-lg.simple-dropdown__caret'),
+ isOpen && this.renderOptions(),
+ ]
+ )
+ }
+}
+
+SimpleDropdown.propTypes = {
+ options: PropTypes.array.isRequired,
+ placeholder: PropTypes.string,
+ onSelect: PropTypes.func,
+ selectedOption: PropTypes.string,
+}
+
+module.exports = SimpleDropdown
diff --git a/ui/app/components/editable-label.js b/ui/app/components/editable-label.js
index 167be7eaf..eb41ec50c 100644
--- a/ui/app/components/editable-label.js
+++ b/ui/app/components/editable-label.js
@@ -1,56 +1,88 @@
-const Component = require('react').Component
+const { Component } = require('react')
+const PropTypes = require('prop-types')
const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const findDOMNode = require('react-dom').findDOMNode
+const classnames = require('classnames')
-module.exports = EditableLabel
+class EditableLabel extends Component {
+ constructor (props) {
+ super(props)
-inherits(EditableLabel, Component)
-function EditableLabel () {
- Component.call(this)
-}
+ this.state = {
+ isEditing: false,
+ value: props.defaultValue || '',
+ }
+ }
+
+ handleSubmit () {
+ const { value } = this.state
+
+ if (value === '') {
+ return
+ }
+
+ Promise.resolve(this.props.onSubmit(value))
+ .then(() => this.setState({ isEditing: false }))
+ }
+
+ saveIfEnter (event) {
+ if (event.key === 'Enter') {
+ this.handleSubmit()
+ }
+ }
-EditableLabel.prototype.render = function () {
- const props = this.props
- const state = this.state
+ renderEditing () {
+ const { value } = this.state
- if (state && state.isEditingLabel) {
- return h('div.editable-label', [
- h('input.sizing-input', {
- defaultValue: props.textValue,
- maxLength: '20',
+ return ([
+ h('input.large-input.editable-label__input', {
+ type: 'text',
+ required: true,
+ value: this.state.value,
onKeyPress: (event) => {
- this.saveIfEnter(event)
+ if (event.key === 'Enter') {
+ this.handleSubmit()
+ }
},
+ onChange: event => this.setState({ value: event.target.value }),
+ className: classnames({ 'editable-label__input--error': value === '' }),
}),
- h('button.editable-button', {
- onClick: () => this.saveText(),
- }, 'Save'),
+ h('div.editable-label__icon-wrapper', [
+ h('i.fa.fa-check.editable-label__icon', {
+ onClick: () => this.handleSubmit(),
+ }),
+ ]),
])
- } else {
- return h('div.name-label', {
- onClick: (event) => {
- const nameAttribute = event.target.getAttribute('name')
- // checks for class to handle smaller CTA above the account name
- const classAttribute = event.target.getAttribute('class')
- if (nameAttribute === 'edit' || classAttribute === 'edit-text') {
- this.setState({ isEditingLabel: true })
- }
- },
- }, this.props.children)
}
-}
-EditableLabel.prototype.saveIfEnter = function (event) {
- if (event.key === 'Enter') {
- this.saveText()
+ renderReadonly () {
+ return ([
+ h('div.editable-label__value', this.state.value),
+ h('div.editable-label__icon-wrapper', [
+ h('i.fa.fa-pencil.editable-label__icon', {
+ onClick: () => this.setState({ isEditing: true }),
+ }),
+ ]),
+ ])
+ }
+
+ render () {
+ const { isEditing } = this.state
+ const { className } = this.props
+
+ return (
+ h('div.editable-label', { className: classnames(className) },
+ isEditing
+ ? this.renderEditing()
+ : this.renderReadonly()
+ )
+ )
}
}
-EditableLabel.prototype.saveText = function () {
- var container = findDOMNode(this)
- var text = container.querySelector('.editable-label input').value
- var truncatedText = text.substring(0, 20)
- this.props.saveText(truncatedText)
- this.setState({ isEditingLabel: false, textLabel: truncatedText })
+EditableLabel.propTypes = {
+ onSubmit: PropTypes.func.isRequired,
+ defaultValue: PropTypes.string,
+ className: PropTypes.string,
}
+
+module.exports = EditableLabel
diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js
index fb8c8e579..6553053f7 100644
--- a/ui/app/components/ens-input.js
+++ b/ui/app/components/ens-input.js
@@ -6,7 +6,7 @@ const debounce = require('debounce')
const copyToClipboard = require('copy-to-clipboard')
const ENS = require('ethjs-ens')
const networkMap = require('ethjs-ens/lib/network-map.json')
-const ensRE = /.+\.eth$/
+const ensRE = /.+\..+$/
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js
index 259fa4d73..d30b7cd56 100644
--- a/ui/app/components/identicon.js
+++ b/ui/app/components/identicon.js
@@ -55,6 +55,7 @@ IdenticonComponent.prototype.componentDidMount = function () {
if (!address) return
+ // eslint-disable-next-line react/no-find-dom-node
var container = findDOMNode(this)
var diameter = props.diameter || this.defaultDiameter
@@ -70,6 +71,7 @@ IdenticonComponent.prototype.componentDidUpdate = function () {
if (!address) return
+ // eslint-disable-next-line react/no-find-dom-node
var container = findDOMNode(this)
var children = container.children
diff --git a/ui/app/components/menu-droppo.js b/ui/app/components/menu-droppo.js
index 95b75f54c..c80bee2be 100644
--- a/ui/app/components/menu-droppo.js
+++ b/ui/app/components/menu-droppo.js
@@ -97,6 +97,7 @@ MenuDroppoComponent.prototype.componentDidMount = function () {
if (this && document.body) {
this.globalClickHandler = this.globalClickOccurred.bind(this)
document.body.addEventListener('click', this.globalClickHandler)
+ // eslint-disable-next-line react/no-find-dom-node
var container = findDOMNode(this)
this.container = container
}
@@ -110,6 +111,7 @@ MenuDroppoComponent.prototype.componentWillUnmount = function () {
MenuDroppoComponent.prototype.globalClickOccurred = function (event) {
const target = event.target
+ // eslint-disable-next-line react/no-find-dom-node
const container = findDOMNode(this)
if (target !== container &&
diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js
index 37a62e1c0..e3c936702 100644
--- a/ui/app/components/modals/account-details-modal.js
+++ b/ui/app/components/modals/account-details-modal.js
@@ -7,6 +7,7 @@ const AccountModalContainer = require('./account-modal-container')
const { getSelectedIdentity, getSelectedAddress } = require('../../selectors')
const genAccountLink = require('../../../lib/account-link.js')
const QrView = require('../qr-code')
+const EditableLabel = require('../editable-label')
function mapStateToProps (state) {
return {
@@ -23,6 +24,7 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
},
hideModal: () => dispatch(actions.hideModal()),
+ saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)),
}
}
@@ -41,14 +43,19 @@ AccountDetailsModal.prototype.render = function () {
selectedIdentity,
network,
showExportPrivateKeyModal,
- hideModal,
+ saveAccountLabel,
} = this.props
const { name, address } = selectedIdentity
return h(AccountModalContainer, {}, [
+ h(EditableLabel, {
+ className: 'account-modal__name',
+ defaultValue: name,
+ onSubmit: label => saveAccountLabel(address, label),
+ }),
+
h(QrView, {
Qr: {
- message: name,
data: address,
},
}),
@@ -57,14 +64,12 @@ AccountDetailsModal.prototype.render = function () {
h('button.btn-clear', {
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
- }, [ 'View account on Etherscan' ]),
+ }, 'View account on Etherscan'),
// Holding on redesign for Export Private Key functionality
h('button.btn-clear', {
- onClick: () => {
- showExportPrivateKeyModal()
- },
- }, [ 'Export private key' ]),
-
+ onClick: () => showExportPrivateKeyModal(),
+ }, 'Export private key'),
+
])
}
diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js
index f1a5aa9fd..33615c483 100644
--- a/ui/app/components/modals/buy-options-modal.js
+++ b/ui/app/components/modals/buy-options-modal.js
@@ -42,7 +42,7 @@ BuyOptions.prototype.render = function () {
h('div.buy-modal-content-title', {
style: {},
}, 'Transfers'),
- h('div', {}, 'How would you like to buy Ether?'),
+ h('div', {}, 'How would you like to deposit Ether?'),
]),
h('div.buy-modal-content-options.flex-column.flex-center', {}, [
@@ -54,7 +54,7 @@ BuyOptions.prototype.render = function () {
},
}, [
h('div.buy-modal-content-option-title', {}, 'Coinbase'),
- h('div.buy-modal-content-option-subtitle', {}, 'Buy with Fiat'),
+ h('div.buy-modal-content-option-subtitle', {}, 'Deposit with Fiat'),
]),
// h('div.buy-modal-content-option', {}, [
diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js
index 302596eda..2d8470634 100644
--- a/ui/app/components/modals/export-private-key-modal.js
+++ b/ui/app/components/modals/export-private-key-modal.js
@@ -66,7 +66,6 @@ ExportPrivateKeyModal.prototype.renderPasswordInput = function (privateKey) {
})
: h('input.private-key-password-input', {
type: 'password',
- placeholder: 'Type password',
onChange: event => this.setState({ password: event.target.value }),
})
}
@@ -84,7 +83,7 @@ ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password,
(privateKey
? this.renderButton('btn-clear', () => hideModal(), 'Done')
- : this.renderButton('btn-clear', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Download')
+ : this.renderButton('btn-clear', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Show')
),
])
@@ -118,7 +117,7 @@ ExportPrivateKeyModal.prototype.render = function () {
h('div.account-modal-divider'),
- h('span.modal-body-title', 'Download Private Keys'),
+ h('span.modal-body-title', 'Show Private Keys'),
h('div.private-key-password', {}, [
this.renderPasswordLabel(privateKey),
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index 88deb2bb0..e15dd6c1b 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -162,10 +162,9 @@ const MODALS = {
h(CustomizeGasModal, {}, []),
],
mobileModalStyle: {
- width: '355px',
- height: '598px',
- // top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
- top: '5%',
+ width: '100vw',
+ height: '100vh',
+ top: '0',
transform: 'none',
left: '0',
right: '0',
diff --git a/ui/app/components/modals/new-account-modal.js b/ui/app/components/modals/new-account-modal.js
index 25beb6745..b78de1d8d 100644
--- a/ui/app/components/modals/new-account-modal.js
+++ b/ui/app/components/modals/new-account-modal.js
@@ -28,6 +28,7 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.hideModal())
})
},
+ showImportPage: () => dispatch(actions.showImportPage()),
}
}
@@ -36,7 +37,7 @@ function NewAccountModal () {
Component.call(this)
this.state = {
- newAccountName: ''
+ newAccountName: '',
}
}
@@ -63,7 +64,7 @@ NewAccountModal.prototype.render = function () {
h('div.new-account-input-wrapper', {}, [
h('input.new-account-input', {
placeholder: 'E.g. My new account',
- onChange: (event) => this.setState({ newAccountName: event.target.value })
+ onChange: event => this.setState({ newAccountName: event.target.value }),
}, []),
]),
@@ -71,13 +72,16 @@ NewAccountModal.prototype.render = function () {
'or',
]),
- h('div.new-account-modal-content.after-input', {}, [
- 'Import an account',
- ]),
+ h('div.new-account-modal-content.after-input.pointer', {
+ onClick: () => {
+ this.props.hideModal()
+ this.props.showImportPage()
+ },
+ }, 'Import an account'),
h('div.new-account-modal-content.button', {}, [
h('button.btn-clear', {
- onClick: () => this.props.createAccount(newAccountName)
+ onClick: () => this.props.createAccount(newAccountName),
}, [
'SAVE',
]),
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index b24505750..229d02e36 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -64,13 +64,18 @@ Network.prototype.render = function () {
return (
h('div.network-component.pointer', {
className: classnames('network-component pointer', {
+ 'network-component--disabled': this.props.disabled,
'ethereum-network': providerName === 'mainnet',
'ropsten-test-network': providerName === 'ropsten' || parseInt(networkNumber) === 3,
'kovan-test-network': providerName === 'kovan',
'rinkeby-test-network': providerName === 'rinkeby',
}),
title: hoverText,
- onClick: (event) => this.props.onClick(event),
+ onClick: (event) => {
+ if (!this.props.disabled) {
+ this.props.onClick(event)
+ }
+ },
}, [
(function () {
switch (iconName) {
diff --git a/ui/app/components/notice.js b/ui/app/components/notice.js
index abfff1f5c..941ac33e6 100644
--- a/ui/app/components/notice.js
+++ b/ui/app/components/notice.js
@@ -117,6 +117,7 @@ Notice.prototype.render = function () {
}
Notice.prototype.componentDidMount = function () {
+ // eslint-disable-next-line react/no-find-dom-node
var node = findDOMNode(this)
linker.setupListener(node)
if (document.getElementsByClassName('notice-box')[0].clientHeight < 310) {
@@ -125,6 +126,7 @@ Notice.prototype.componentDidMount = function () {
}
Notice.prototype.componentWillUnmount = function () {
+ // eslint-disable-next-line react/no-find-dom-node
var node = findDOMNode(this)
linker.teardownListener(node)
}
diff --git a/ui/app/components/pending-personal-msg.js b/ui/app/components/pending-personal-msg.js
deleted file mode 100644
index 4542adb28..000000000
--- a/ui/app/components/pending-personal-msg.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const PendingTxDetails = require('./pending-personal-msg-details')
-
-module.exports = PendingMsg
-
-inherits(PendingMsg, Component)
-function PendingMsg () {
- Component.call(this)
-}
-
-PendingMsg.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- return (
-
- h('div', {
- key: msgData.id,
- }, [
-
- // header
- h('h3', {
- style: {
- fontWeight: 'bold',
- textAlign: 'center',
- },
- }, 'Sign Message'),
-
- // message details
- h(PendingTxDetails, state),
-
- // sign + cancel
- h('.flex-row.flex-space-around', [
- h('button', {
- onClick: state.cancelPersonalMessage,
- }, 'Cancel'),
- h('button', {
- onClick: state.signPersonalMessage,
- }, 'Sign'),
- ]),
- ])
-
- )
-}
-
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index 7162c7122..2f178f179 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -50,7 +50,7 @@ ConfirmSendEther.prototype.getAmount = function () {
const { conversionRate, currentCurrency } = this.props
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
- console.log(`conversionRate, currentCurrency`, conversionRate, currentCurrency);
+
const FIAT = conversionUtil(txParams.value, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
@@ -194,7 +194,7 @@ ConfirmSendEther.prototype.render = function () {
this.inputs = []
return (
- h('div.confirm-screen-container', {
+ h('div.confirm-screen-container.confirm-send-ether', {
style: { minWidth: '355px' },
}, [
// Main Send token Card
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index a4c3d16e3..abb7a0770 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -224,7 +224,7 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
]),
h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${fiatAmount + fiatGas} ${currentCurrency}`),
+ h('div.confirm-screen-row-info', `${addCurrencies(fiatAmount, fiatGas)} ${currentCurrency}`),
h('div.confirm-screen-row-detail', `${addCurrencies(tokenAmount, tokenGas || '0')} ${symbol}`),
]),
])
@@ -263,7 +263,7 @@ ConfirmSendToken.prototype.render = function () {
this.inputs = []
return (
- h('div.confirm-screen-container', {
+ h('div.confirm-screen-container.confirm-send-token', {
style: { minWidth: '355px' },
}, [
// Main Send token Card
diff --git a/ui/app/components/qr-code.js b/ui/app/components/qr-code.js
index cc723df14..83885539c 100644
--- a/ui/app/components/qr-code.js
+++ b/ui/app/components/qr-code.js
@@ -29,11 +29,11 @@ QrCodeView.prototype.render = function () {
const qrImage = qrCode(4, 'M')
qrImage.addData(address)
qrImage.make()
- return h('.div.flex-column.flex-center', {
- style: {
- },
- }, [
- Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('.qr-header', Qr.message),
+
+ return h('.div.flex-column.flex-center', [
+ Array.isArray(Qr.message)
+ ? h('.message-container', this.renderMultiMessage())
+ : Qr.message && h('.qr-header', Qr.message),
this.props.warning ? this.props.warning && h('span.error.flex-center', {
style: {
diff --git a/ui/app/components/send/account-list-item.js b/ui/app/components/send/account-list-item.js
index ba7eec940..cc514cbd4 100644
--- a/ui/app/components/send/account-list-item.js
+++ b/ui/app/components/send/account-list-item.js
@@ -27,6 +27,8 @@ AccountListItem.prototype.render = function () {
icon = null,
conversionRate,
currentCurrency,
+ displayBalance = true,
+ displayAddress = false,
} = this.props
const { name, address, balance } = account || {}
@@ -46,13 +48,15 @@ AccountListItem.prototype.render = function () {
},
),
- h('div.account-list-item__account-name', {}, name),
+ h('div.account-list-item__account-name', {}, name || address),
icon && h('div.account-list-item__icon', [icon]),
]),
- h(CurrencyDisplay, {
+ displayAddress && name && h('div.account-list-item__account-address', address),
+
+ displayBalance && h(CurrencyDisplay, {
primaryCurrency: 'ETH',
convertedCurrency: currentCurrency,
value: balance,
diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js
index 7180b94d3..5dba6a8dd 100644
--- a/ui/app/components/send/currency-display.js
+++ b/ui/app/components/send/currency-display.js
@@ -2,7 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Identicon = require('../identicon')
-const { conversionUtil } = require('../../conversion-util')
+const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
module.exports = CurrencyDisplay
@@ -20,14 +20,6 @@ function isValidInput (text) {
return re.test(text)
}
-function resetCaretIfPastEnd (value, event) {
- const caretPosition = event.target.selectionStart
-
- if (caretPosition > value.length) {
- event.target.setSelectionRange(value.length, value.length)
- }
-}
-
function toHexWei (value) {
return conversionUtil(value, {
fromNumericBase: 'dec',
@@ -40,7 +32,9 @@ CurrencyDisplay.prototype.getAmount = function (value) {
const { selectedToken } = this.props
const { decimals } = selectedToken || {}
const multiplier = Math.pow(10, Number(decimals || 0))
- const sendAmount = '0x' + Number(value * multiplier).toString(16)
+
+ const sendAmount = multiplyCurrencies(value, multiplier, {toNumericBase: 'hex'})
+
return selectedToken
? sendAmount
: toHexWei(value)
@@ -80,6 +74,8 @@ CurrencyDisplay.prototype.render = function () {
conversionRate,
})
+ const inputSizeMultiplier = readOnly ? 1 : 1.2;
+
return h('div', {
className,
style: {
@@ -93,35 +89,33 @@ CurrencyDisplay.prototype.render = function () {
h('input', {
className: primaryBalanceClassName,
- value: `${value || initValueToRender} ${primaryCurrency}`,
- placeholder: `${0} ${primaryCurrency}`,
+ value: `${value || initValueToRender}`,
+ placeholder: '0',
+ size: (value || initValueToRender).length * inputSizeMultiplier,
readOnly,
onChange: (event) => {
- let newValue = event.target.value.split(' ')[0]
+ let newValue = event.target.value
if (newValue === '') {
- this.setState({ value: '0' })
+ newValue = '0'
}
else if (newValue.match(/^0[1-9]$/)) {
- this.setState({ value: newValue.match(/[1-9]/)[0] })
+ newValue = newValue.match(/[1-9]/)[0]
}
- else if (newValue && !isValidInput(newValue)) {
+
+ if (newValue && !isValidInput(newValue)) {
event.preventDefault()
}
else {
+ validate(this.getAmount(newValue))
this.setState({ value: newValue })
}
},
- onBlur: event => !readOnly && handleChange(this.getAmount(event.target.value.split(' ')[0])),
- onKeyUp: event => {
- if (!readOnly) {
- validate(toHexWei(value || initValueToRender))
- resetCaretIfPastEnd(value || initValueToRender, event)
- }
- },
- onClick: event => !readOnly && resetCaretIfPastEnd(value || initValueToRender, event),
+ onBlur: event => !readOnly && handleChange(this.getAmount(event.target.value)),
}),
+ h('span.currency-display__currency-symbol', primaryCurrency),
+
]),
]),
diff --git a/ui/app/components/send/send-constants.js b/ui/app/components/send/send-constants.js
index a819a8c28..8b56607cc 100644
--- a/ui/app/components/send/send-constants.js
+++ b/ui/app/components/send/send-constants.js
@@ -3,12 +3,19 @@ const { multiplyCurrencies } = require('../../conversion-util')
const MIN_GAS_PRICE_GWEI = '1'
const GWEI_FACTOR = '1e9'
-const MIN_GAS_PRICE = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
+const MIN_GAS_PRICE_HEX = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
multiplicandBase: 16,
multiplierBase: 16,
+ toNumericBase: 'hex',
+})
+const MIN_GAS_PRICE_DEC = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
+ multiplicandBase: 16,
+ multiplierBase: 16,
+ toNumericBase: 'dec',
})
-const MIN_GAS_LIMIT = (21000).toString(16)
-const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT, MIN_GAS_PRICE, {
+const MIN_GAS_LIMIT_HEX = (21000).toString(16)
+const MIN_GAS_LIMIT_DEC = 21000
+const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
@@ -16,8 +23,9 @@ const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT, MIN_GAS_PRICE, {
module.exports = {
MIN_GAS_PRICE_GWEI,
- GWEI_FACTOR,
- MIN_GAS_PRICE,
- MIN_GAS_LIMIT,
+ MIN_GAS_PRICE_HEX,
+ MIN_GAS_PRICE_DEC,
+ MIN_GAS_LIMIT_HEX,
+ MIN_GAS_LIMIT_DEC,
MIN_GAS_TOTAL,
}
diff --git a/ui/app/components/send/send-utils.js b/ui/app/components/send/send-utils.js
new file mode 100644
index 000000000..bf096d610
--- /dev/null
+++ b/ui/app/components/send/send-utils.js
@@ -0,0 +1,39 @@
+const { addCurrencies, conversionGreaterThan } = require('../../conversion-util')
+
+function isBalanceSufficient({
+ amount,
+ gasTotal,
+ balance,
+ primaryCurrency,
+ selectedToken,
+ amountConversionRate,
+ conversionRate,
+}) {
+ const totalAmount = addCurrencies(amount, gasTotal, {
+ aBase: 16,
+ bBase: 16,
+ toNumericBase: 'hex',
+ })
+
+ const balanceIsSufficient = conversionGreaterThan(
+ {
+ value: balance,
+ fromNumericBase: 'hex',
+ fromCurrency: primaryCurrency,
+ conversionRate,
+ },
+ {
+ value: totalAmount,
+ fromNumericBase: 'hex',
+ conversionRate: amountConversionRate,
+ fromCurrency: selectedToken || primaryCurrency,
+ conversionRate: amountConversionRate,
+ },
+ )
+
+ return balanceIsSufficient
+}
+
+module.exports = {
+ isBalanceSufficient,
+} \ No newline at end of file
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index c14865e9f..fb2634de2 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -17,6 +17,7 @@ const {
getAddressBook,
getSendFrom,
getCurrentCurrency,
+ getSelectedTokenToFiatRate,
} = require('../../selectors')
module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther)
@@ -26,7 +27,6 @@ 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;
@@ -40,11 +40,7 @@ function mapStateToProps (state) {
primaryCurrency = selectedToken.symbol
- tokenToFiatRate = multiplyCurrencies(
- conversionRate,
- selectedTokenExchangeRate,
- { toNumericBase: 'dec' }
- )
+ tokenToFiatRate = getSelectedTokenToFiatRate(state)
}
return {
@@ -80,5 +76,6 @@ function mapDispatchToProps (dispatch) {
updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)),
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
goHome: () => dispatch(actions.goHome()),
+ clearSend: () => dispatch(actions.clearSend())
}
}
diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js
index 686a7a23e..ab490155b 100644
--- a/ui/app/components/send/to-autocomplete.js
+++ b/ui/app/components/send/to-autocomplete.js
@@ -2,54 +2,118 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Identicon = require('../identicon')
+const AccountListItem = require('./account-list-item')
module.exports = ToAutoComplete
inherits(ToAutoComplete, Component)
function ToAutoComplete () {
Component.call(this)
+
+ this.state = { accountsToRender: [] }
+}
+
+ToAutoComplete.prototype.getListItemIcon = function (listItemAddress, toAddress) {
+ const listItemIcon = h(`i.fa.fa-check.fa-lg`, { style: { color: '#02c9b1' } })
+
+ return toAddress && listItemAddress === toAddress
+ ? listItemIcon
+ : null
+}
+
+ToAutoComplete.prototype.renderDropdown = function () {
+ const {
+ accounts,
+ closeDropdown,
+ onChange,
+ to,
+ } = this.props
+ const { accountsToRender } = this.state
+
+ return accountsToRender.length && h('div', {}, [
+
+ h('div.send-v2__from-dropdown__close-area', {
+ onClick: closeDropdown,
+ }),
+
+ h('div.send-v2__from-dropdown__list', {}, [
+
+ ...accountsToRender.map(account => h(AccountListItem, {
+ account,
+ handleClick: () => {
+ onChange(account.address)
+ closeDropdown()
+ },
+ icon: this.getListItemIcon(account.address, to),
+ displayBalance: false,
+ displayAddress: true,
+ }))
+
+ ]),
+
+ ])
+}
+
+ToAutoComplete.prototype.handleInputEvent = function (event = {}, cb) {
+ const {
+ to,
+ accounts,
+ closeDropdown,
+ openDropdown,
+ } = this.props
+
+ const matchingAccounts = accounts.filter(({ address }) => address.match(to || ''))
+ const matches = matchingAccounts.length
+
+ if (!matches || matchingAccounts[0].address === to) {
+ this.setState({ accountsToRender: [] })
+ event.target && event.target.select()
+ closeDropdown()
+ }
+ else {
+ this.setState({ accountsToRender: matchingAccounts })
+ openDropdown()
+ }
+ cb && cb(event.target.value)
+}
+
+ToAutoComplete.prototype.componentDidUpdate = function (nextProps, nextState) {
+ if (this.props.to !== nextProps.to) {
+ this.handleInputEvent()
+ }
}
ToAutoComplete.prototype.render = function () {
- const { to, accounts, onChange, inError } = this.props
+ const {
+ to,
+ accounts,
+ openDropdown,
+ closeDropdown,
+ dropdownOpen,
+ onChange,
+ inError,
+ } = this.props
- return h('div.send-v2__to-autocomplete', [
+ return h('div.to-autocomplete', {}, [
h('input.send-v2__to-autocomplete__input', {
- name: 'address',
- list: 'addresses',
placeholder: 'Recipient Address',
className: inError ? `send-v2__error-border` : '',
value: to,
- onChange,
- onFocus: event => {
- to && event.target.select()
- },
+ onChange: event => onChange(event.target.value),
+ onFocus: event => this.handleInputEvent(event),
style: {
borderColor: inError ? 'red' : null,
}
}),
- h('datalist#addresses', [
- // Corresponds to the addresses owned.
- ...Object.entries(accounts).map(([key, { address, name }]) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- // Corresponds to previously sent-to addresses.
- // ...addressBook.map(({ address, name }) => {
- // return h('option', {
- // value: address,
- // label: name,
- // key: address,
- // })
- // }),
- ]),
+ !to && h(`i.fa.fa-caret-down.fa-lg.send-v2__to-autocomplete__down-caret`, {
+ style: { color: '#dedede' },
+ onClick: () => this.handleInputEvent(),
+ }),
+
+ dropdownOpen && this.renderDropdown(),
])
-
}
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index 96a86d3b1..c5993e3d3 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -130,8 +130,8 @@ ShapeshiftForm.prototype.renderMain = function () {
alignItems: 'flex-start',
},
}, [
- this.props.warning
- ? this.props.warning &&
+ this.props.warning ?
+ this.props.warning &&
h('span.error.flex-center', {
style: {
textAlign: 'center',
diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js
new file mode 100644
index 000000000..a0ecbe8ec
--- /dev/null
+++ b/ui/app/components/signature-request.js
@@ -0,0 +1,255 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const Identicon = require('./identicon')
+const connect = require('react-redux').connect
+const ethUtil = require('ethereumjs-util')
+const PendingTxDetails = require('./pending-personal-msg-details')
+const AccountDropdownMini = require('./dropdowns/account-dropdown-mini')
+const BinaryRenderer = require('./binary-renderer')
+
+const actions = require('../actions')
+const { conversionUtil } = require('../conversion-util')
+
+const {
+ getSelectedAccount,
+ getCurrentAccountWithSendEtherInfo,
+ getSelectedAddress,
+ accountsWithSendEtherInfoSelector,
+ conversionRateSelector,
+} = require('../selectors.js')
+
+function mapStateToProps (state) {
+ return {
+ balance: getSelectedAccount(state).balance,
+ selectedAccount: getCurrentAccountWithSendEtherInfo(state),
+ selectedAddress: getSelectedAddress(state),
+ requester: null,
+ requesterAddress: null,
+ accounts: accountsWithSendEtherInfoSelector(state),
+ conversionRate: conversionRateSelector(state)
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ goHome: () => dispatch(actions.goHome())
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(SignatureRequest)
+
+inherits(SignatureRequest, Component)
+function SignatureRequest (props) {
+ Component.call(this)
+
+ this.state = {
+ selectedAccount: props.selectedAccount,
+ accountDropdownOpen: false,
+ }
+}
+
+SignatureRequest.prototype.renderHeader = function () {
+ return h('div.request-signature__header', [
+
+ h('div.request-signature__header-background'),
+
+ h('div.request-signature__header__text', 'Signature Request'),
+
+ h('div.request-signature__header__tip-container', [
+ h('div.request-signature__header__tip'),
+ ]),
+
+ ])
+}
+
+SignatureRequest.prototype.renderAccountDropdown = function () {
+ const {
+ selectedAccount,
+ accountDropdownOpen,
+ } = this.state
+
+ const {
+ accounts,
+ } = this.props
+
+ return h('div.request-signature__account', [
+
+ h('div.request-signature__account-text', ['Account:']),
+
+ h(AccountDropdownMini, {
+ selectedAccount,
+ accounts,
+ onSelect: selectedAccount => this.setState({ selectedAccount }),
+ dropdownOpen: accountDropdownOpen,
+ openDropdown: () => this.setState({ accountDropdownOpen: true }),
+ closeDropdown: () => this.setState({ accountDropdownOpen: false }),
+ })
+
+ ])
+}
+
+SignatureRequest.prototype.renderBalance = function () {
+ const { balance, conversionRate } = this.props
+
+ const balanceInEther = conversionUtil(balance, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ fromDenomination: 'WEI',
+ numberOfDecimals: 6,
+ conversionRate,
+ })
+
+ return h('div.request-signature__balance', [
+
+ h('div.request-signature__balance-text', ['Balance:']),
+
+ h('div.request-signature__balance-value', `${balanceInEther} ETH`),
+
+ ])
+}
+
+SignatureRequest.prototype.renderAccountInfo = function () {
+ return h('div.request-signature__account-info', [
+
+ this.renderAccountDropdown(),
+
+ this.renderRequestIcon(),
+
+ this.renderBalance(),
+
+ ])
+}
+
+SignatureRequest.prototype.renderRequestIcon = function () {
+ const { requesterAddress } = this.props
+
+ return h('div.request-signature__request-icon', [
+ h(Identicon, {
+ diameter: 40,
+ address: requesterAddress,
+ })
+ ])
+}
+
+SignatureRequest.prototype.renderRequestInfo = function () {
+ const { requester } = this.props
+
+ return h('div.request-signature__request-info', [
+
+ h('div.request-signature__headline', [
+ `Your signature is being requested`,
+ ])
+
+ ])
+}
+
+SignatureRequest.prototype.msgHexToText = function (hex) {
+ try {
+ const stripped = ethUtil.stripHexPrefix(hex)
+ const buff = Buffer.from(stripped, 'hex')
+ return buff.toString('utf8')
+ } catch (e) {
+ return hex
+ }
+}
+
+SignatureRequest.prototype.renderBody = function () {
+ let rows
+ let notice = 'You are signing:'
+
+ const { txData } = this.props
+ const { type, msgParams: { data } } = txData
+
+ if (type === 'personal_sign') {
+ rows = [{ name: 'Message', value: this.msgHexToText(data) }]
+ }
+ else if (type === 'eth_signTypedData') {
+ rows = data
+ }
+ else if (type === 'eth_sign') {
+ rows = [{ name: 'Message', value: data }]
+ notice = `Signing this message can have
+ dangerous side effects. Only sign messages from
+ sites you fully trust with your entire account.
+ This dangerous method will be removed in a future version. `
+ }
+
+ return h('div.request-signature__body', {}, [
+
+ this.renderAccountInfo(),
+
+ this.renderRequestInfo(),
+
+ h('div.request-signature__notice', [notice]),
+
+ h('div.request-signature__rows', [
+
+ ...rows.map(({ name, value }) => {
+ return h('div.request-signature__row', [
+ h('div.request-signature__row-title', [`${name}:`]),
+ h('div.request-signature__row-value', value),
+ ])
+ }),
+
+ ]),
+
+ ])
+}
+
+SignatureRequest.prototype.renderFooter = function () {
+ const {
+ goHome,
+ signPersonalMessage,
+ signTypedMessage,
+ cancelPersonalMessage,
+ cancelTypedMessage,
+ signMessage,
+ cancelMessage,
+ } = this.props
+
+ const { txData } = this.props
+ const { type } = txData
+
+ let cancel
+ let sign
+ if (type === 'personal_sign') {
+ cancel = cancelPersonalMessage
+ sign = signPersonalMessage
+ }
+ else if (type === 'eth_signTypedData') {
+ cancel = cancelTypedMessage
+ sign = signTypedMessage
+ }
+ else if (type === 'eth_sign') {
+ cancel = cancelMessage
+ sign = signMessage
+ }
+
+ return h('div.request-signature__footer', [
+ h('button.request-signature__footer__cancel-button', {
+ onClick: cancel,
+ }, 'CANCEL'),
+ h('button.request-signature__footer__sign-button', {
+ onClick: sign,
+ }, 'SIGN'),
+ ])
+}
+
+SignatureRequest.prototype.render = function () {
+ return (
+
+ h('div.request-signature__container', [
+
+ this.renderHeader(),
+
+ this.renderBody(),
+
+ this.renderFooter(),
+
+ ])
+
+ )
+
+}
+
diff --git a/ui/app/components/tab-bar.js b/ui/app/components/tab-bar.js
index bef444a48..fe4076ed0 100644
--- a/ui/app/components/tab-bar.js
+++ b/ui/app/components/tab-bar.js
@@ -1,37 +1,40 @@
-const Component = require('react').Component
+const { Component } = require('react')
const h = require('react-hyperscript')
-const inherits = require('util').inherits
+const classnames = require('classnames')
-module.exports = TabBar
+class TabBar extends Component {
+ constructor (props) {
+ super(props)
+ const { defaultTab, tabs } = props
-inherits(TabBar, Component)
-function TabBar () {
- Component.call(this)
-}
+ this.state = {
+ subview: defaultTab || tabs[0].key,
+ }
+ }
-TabBar.prototype.render = function () {
- const props = this.props
- const state = this.state || {}
- const { tabs = [], defaultTab, tabSelected } = props
- const { subview = defaultTab } = state
+ render () {
+ const { tabs = [], tabSelected } = this.props
+ const { subview } = this.state
- return (
- h('.flex-row.space-around.text-transform-uppercase', {
- style: {
- background: '#EBEBEB',
- color: '#AEAEAE',
- paddingTop: '4px',
- minHeight: '30px',
- },
- }, tabs.map((tab) => {
- const { key, content } = tab
- return h(subview === key ? '.activeForm' : '.inactiveForm.pointer', {
- onClick: () => {
- this.setState({ subview: key })
- tabSelected(key)
- },
- }, content)
- }))
- )
+ return (
+ h('.tab-bar', {}, [
+ tabs.map((tab) => {
+ const { key, content } = tab
+ return h('div', {
+ className: classnames('tab-bar__tab pointer', {
+ 'tab-bar__tab--active': subview === key,
+ }),
+ onClick: () => {
+ this.setState({ subview: key })
+ tabSelected(key)
+ },
+ key,
+ }, content)
+ }),
+ h('div.tab-bar__tab.tab-bar__grow-tab'),
+ ])
+ )
+ }
}
+module.exports = TabBar
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index 59f55d485..ebef22680 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -73,7 +73,7 @@ TxView.prototype.renderButtons = function () {
onClick: () => showModal({
name: 'BUY',
}),
- }, 'BUY'),
+ }, 'DEPOSIT'),
h('button.btn-clear', {
style: {
@@ -109,14 +109,15 @@ TxView.prototype.render = function () {
margin: '1em 0.9em',
alignItems: 'center',
},
- onClick: () => {
- this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar()
- },
}, [
h('div.fa.fa-bars', {
style: {
fontSize: '1.3em',
+ cursor: 'pointer',
+ },
+ onClick: () => {
+ this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar()
},
}, []),
diff --git a/ui/app/components/typed-message-renderer.js b/ui/app/components/typed-message-renderer.js
index a042b57be..d170d63b7 100644
--- a/ui/app/components/typed-message-renderer.js
+++ b/ui/app/components/typed-message-renderer.js
@@ -32,11 +32,11 @@ TypedMessageRenderer.prototype.render = function () {
)
}
-function renderTypedData(values) {
+function renderTypedData (values) {
return values.map(function (value) {
return h('div', {}, [
h('strong', {style: {display: 'block', fontWeight: 'bold'}}, String(value.name) + ':'),
h('div', {}, value.value),
])
})
-} \ No newline at end of file
+}
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index a870a24e3..3cb7a8b76 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -3,7 +3,8 @@ const connect = require('react-redux').connect
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Identicon = require('./identicon')
-const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns
+// const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns
+const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions')
const BalanceComponent = require('./balance-component')
const TokenList = require('./token-list')
@@ -19,6 +20,7 @@ function mapStateToProps (state) {
identities: state.metamask.identities,
accounts: state.metamask.accounts,
tokens: state.metamask.tokens,
+ keyrings: state.metamask.keyrings,
selectedAddress: selectors.getSelectedAddress(state),
selectedIdentity: selectors.getSelectedIdentity(state),
selectedAccount: selectors.getSelectedAccount(state),
@@ -28,15 +30,22 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) {
return {
- showSendPage: () => { dispatch(actions.showSendPage()) },
- hideSidebar: () => { dispatch(actions.hideSidebar()) },
+ showSendPage: () => dispatch(actions.showSendPage()),
+ hideSidebar: () => dispatch(actions.hideSidebar()),
unsetSelectedToken: () => dispatch(actions.setSelectedToken()),
+ showAccountDetailModal: () => {
+ dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
+ },
+ showAddTokenPage: () => dispatch(actions.showAddTokenPage()),
}
}
inherits(WalletView, Component)
function WalletView () {
Component.call(this)
+ this.state = {
+ hasCopied: false,
+ }
}
WalletView.prototype.renderWalletBalance = function () {
@@ -47,7 +56,7 @@ WalletView.prototype.renderWalletBalance = function () {
hideSidebar,
sidebarOpen,
} = this.props
- console.log({ selectedAccount })
+
const selectedClass = selectedTokenAddress
? ''
: 'wallet-balance-wrapper--active'
@@ -73,13 +82,25 @@ WalletView.prototype.renderWalletBalance = function () {
WalletView.prototype.render = function () {
const {
- network, responsiveDisplayClassname, identities,
- selectedAddress, accounts,
+ responsiveDisplayClassname,
+ selectedAddress,
selectedIdentity,
+ keyrings,
+ showAccountDetailModal,
+ hideSidebar,
+ showAddTokenPage,
} = this.props
// temporary logs + fake extra wallets
// console.log('walletview, selectedAccount:', selectedAccount)
+ const keyring = keyrings.find((kr) => {
+ return kr.accounts.includes(selectedAddress) ||
+ kr.accounts.includes(selectedIdentity.address)
+ })
+
+ const type = keyring.type
+ const isLoose = type !== 'HD Key Tree'
+
return h('div.wallet-view.flex-column' + (responsiveDisplayClassname || ''), {
style: {},
}, [
@@ -88,57 +109,16 @@ WalletView.prototype.render = function () {
h('div.flex-column.wallet-view-account-details', {
style: {},
}, [
+ h('div.wallet-view__sidebar-close', {
+ onClick: hideSidebar,
+ }),
- h('div.flex-row.account-options-menu', {
- style: {
- position: 'relative',
- },
- }, [
-
- h(AccountDropdowns, {
- selected: selectedAddress,
- network,
- identities,
- useCssTransition: true,
- enableAccountOptions: true,
- dropdownWrapperStyle: {
- padding: '1px 15px',
- marginLeft: '-25px',
- position: 'absolute',
- width: '122%', // TODO, refactor all of this component out into media queries
- },
- menuItemStyles: {
- padding: '0px 0px',
- margin: '22px 0px',
- },
- }, []),
-
- ]),
+ h('div.wallet-view__keyring-label', isLoose ? 'IMPORTED' : ''),
- h('div.flex-column.flex-center', {
+ h('div.flex-column.flex-center.wallet-view__name-container', {
+ style: { margin: '0 auto' },
+ onClick: showAccountDetailModal,
}, [
- h('div', {
- style: {
- position: 'relative',
- },
- }, [
- h(AccountDropdowns, {
- accounts,
- style: {
- position: 'absolute',
- left: 'calc(50% + 28px + 5.5px)',
- top: '14px',
- },
- innerStyle: {
- padding: '10px 16px',
- },
- useCssTransition: true,
- selected: selectedAddress,
- network,
- identities,
- }, []),
- ]),
-
h(Identicon, {
diameter: 54,
address: selectedAddress,
@@ -150,21 +130,33 @@ WalletView.prototype.render = function () {
selectedIdentity.name,
]),
+ h('button.wallet-view__details-button', 'DETAILS'),
]),
]),
- // 'Wallet' - Title
- // Not visible on mobile
- h('div.flex-column.wallet-view-title-wrapper', {}, [
- h('span.wallet-view-title', {}, [
- 'Wallet',
- ]),
+
+ h('div.wallet-view__address', {
+ onClick: () => {
+ copyToClipboard(selectedAddress)
+ this.setState({ hasCopied: true })
+ setTimeout(() => this.setState({ hasCopied: false }), 3000)
+ },
+ }, [
+ this.state.hasCopied && 'Copied to Clipboard',
+ !this.state.hasCopied && `${selectedAddress.slice(0, 4)}...${selectedAddress.slice(-4)}`,
+ h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }),
]),
this.renderWalletBalance(),
h(TokenList),
+ h('button.wallet-view__add-token-button', {
+ onClick: () => {
+ showAddTokenPage()
+ hideSidebar()
+ },
+ }, 'Add Token'),
])
}