aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/components')
-rw-r--r--ui/app/components/account-dropdowns.js19
-rw-r--r--ui/app/components/account-export.js15
-rw-r--r--ui/app/components/account-menu/index.js17
-rw-r--r--ui/app/components/balance.js89
-rw-r--r--ui/app/components/binary-renderer.js46
-rw-r--r--ui/app/components/bn-as-decimal-input.js9
-rw-r--r--ui/app/components/buy-button-subview.js15
-rw-r--r--ui/app/components/coinbase-form.js5
-rw-r--r--ui/app/components/copyButton.js3
-rw-r--r--ui/app/components/copyable.js3
-rw-r--r--ui/app/components/currency-input.js22
-rw-r--r--ui/app/components/customize-gas-modal/index.js62
-rw-r--r--ui/app/components/dropdowns/account-options-dropdown.js29
-rw-r--r--ui/app/components/dropdowns/account-selection-dropdown.js29
-rw-r--r--ui/app/components/dropdowns/components/account-dropdowns.js42
-rw-r--r--ui/app/components/dropdowns/components/dropdown.js2
-rw-r--r--ui/app/components/dropdowns/index.js6
-rw-r--r--ui/app/components/dropdowns/network-dropdown.js75
-rw-r--r--ui/app/components/dropdowns/simple-dropdown.js2
-rw-r--r--ui/app/components/dropdowns/token-menu-dropdown.js3
-rw-r--r--ui/app/components/ens-input.js46
-rw-r--r--ui/app/components/eth-balance.js2
-rw-r--r--ui/app/components/hex-as-decimal-input.js9
-rw-r--r--ui/app/components/input-number.js1
-rw-r--r--ui/app/components/loading.js2
-rw-r--r--ui/app/components/mini-account-panel.js74
-rw-r--r--ui/app/components/modals/account-details-modal.js9
-rw-r--r--ui/app/components/modals/account-modal-container.js3
-rw-r--r--ui/app/components/modals/buy-options-modal.js15
-rw-r--r--ui/app/components/modals/deposit-ether-modal.js138
-rw-r--r--ui/app/components/modals/edit-account-name-modal.js7
-rw-r--r--ui/app/components/modals/export-private-key-modal.js18
-rw-r--r--ui/app/components/modals/hide-token-confirmation-modal.js13
-rw-r--r--ui/app/components/modals/modal.js46
-rw-r--r--ui/app/components/modals/new-account-modal.js17
-rw-r--r--ui/app/components/modals/notification-modal.js36
-rw-r--r--ui/app/components/modals/notification-modals/confirm-reset-account.js46
-rw-r--r--ui/app/components/network-display.js51
-rw-r--r--ui/app/components/network.js25
-rw-r--r--ui/app/components/notice.js132
-rw-r--r--ui/app/components/pages/add-token.js256
-rw-r--r--ui/app/components/pages/create-account/import-account/index.js26
-rw-r--r--ui/app/components/pages/create-account/import-account/json.js189
-rw-r--r--ui/app/components/pages/create-account/import-account/private-key.js13
-rw-r--r--ui/app/components/pages/create-account/import-account/seed.js6
-rw-r--r--ui/app/components/pages/create-account/new-account.js13
-rw-r--r--ui/app/components/pages/settings/settings.js68
-rw-r--r--ui/app/components/pages/signature-request.js75
-rw-r--r--ui/app/components/pending-msg-details.js4
-rw-r--r--ui/app/components/pending-msg.js15
-rw-r--r--ui/app/components/pending-personal-msg-details.js60
-rw-r--r--ui/app/components/pending-tx/confirm-deploy-contract.js598
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js182
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js259
-rw-r--r--ui/app/components/pending-tx/index.js18
-rw-r--r--ui/app/components/pending-typed-msg-details.js59
-rw-r--r--ui/app/components/pending-typed-msg.js46
-rw-r--r--ui/app/components/range-slider.js58
-rw-r--r--ui/app/components/send-token/index.js439
-rw-r--r--ui/app/components/send/currency-toggle.js44
-rw-r--r--ui/app/components/send/eth-fee-display.js37
-rw-r--r--ui/app/components/send/gas-fee-display-v2.js13
-rw-r--r--ui/app/components/send/gas-fee-display.js62
-rw-r--r--ui/app/components/send/gas-tooltip.js4
-rw-r--r--ui/app/components/send/send-v2-container.js5
-rw-r--r--ui/app/components/send/to-autocomplete.js4
-rw-r--r--ui/app/components/send/usd-fee-display.js35
-rw-r--r--ui/app/components/sender-to-recipient.js66
-rw-r--r--ui/app/components/shapeshift-form.js42
-rw-r--r--ui/app/components/shift-list-item.js15
-rw-r--r--ui/app/components/signature-request.js250
-rw-r--r--ui/app/components/tab-bar.js2
-rw-r--r--ui/app/components/template.js18
-rw-r--r--ui/app/components/token-list.js7
-rw-r--r--ui/app/components/transaction-list-item-icon.js68
-rw-r--r--ui/app/components/transaction-list-item.js238
-rw-r--r--ui/app/components/transaction-list.js87
-rw-r--r--ui/app/components/tx-list-item.js100
-rw-r--r--ui/app/components/tx-list.js18
-rw-r--r--ui/app/components/tx-view.js23
-rw-r--r--ui/app/components/typed-message-renderer.js42
-rw-r--r--ui/app/components/wallet-content-display.js56
-rw-r--r--ui/app/components/wallet-view.js11
83 files changed, 2070 insertions, 2744 deletions
diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js
index 0c34a5154..1612d7b6a 100644
--- a/ui/app/components/account-dropdowns.js
+++ b/ui/app/components/account-dropdowns.js
@@ -1,5 +1,5 @@
const Component = require('react').Component
-const PropTypes = require('react').PropTypes
+const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const actions = require('../actions')
const genAccountLink = require('etherscan-link').createAccountLink
@@ -9,6 +9,7 @@ const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('./identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard')
+const t = require('../../i18n')
class AccountDropdowns extends Component {
constructor (props) {
@@ -79,7 +80,7 @@ class AccountDropdowns extends Component {
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.allcaps', t('loose')) : null
} catch (e) { return }
}
@@ -129,7 +130,7 @@ class AccountDropdowns extends Component {
diameter: 32,
},
),
- h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, 'Create Account'),
+ h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, t('createAccount')),
],
),
h(
@@ -154,7 +155,7 @@ class AccountDropdowns extends Component {
fontSize: '24px',
marginBottom: '5px',
},
- }, 'Import Account'),
+ }, t('importAccount')),
]
),
]
@@ -173,7 +174,7 @@ class AccountDropdowns extends Component {
minWidth: '180px',
},
isOpen: optionsMenuActive,
- onClickOutside: () => {
+ onClickOutside: (event) => {
const { classList } = event.target
const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName)
if (optionsMenuActive && isNotToggleElement) {
@@ -192,7 +193,7 @@ class AccountDropdowns extends Component {
global.platform.openWindow({ url })
},
},
- 'View account on Etherscan',
+ t('etherscanView'),
),
h(
DropdownMenuItem,
@@ -204,7 +205,7 @@ class AccountDropdowns extends Component {
actions.showQrView(selected, identity ? identity.name : '')
},
},
- 'Show QR Code',
+ t('showQRCode'),
),
h(
DropdownMenuItem,
@@ -216,7 +217,7 @@ class AccountDropdowns extends Component {
copyToClipboard(checkSumAddress)
},
},
- 'Copy Address to clipboard',
+ t('copyAddress'),
),
h(
DropdownMenuItem,
@@ -226,7 +227,7 @@ class AccountDropdowns extends Component {
actions.requestAccountExport()
},
},
- 'Export Private Key',
+ t('exportPrivateKey'),
),
]
)
diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js
index 32b103c86..5637bc8d0 100644
--- a/ui/app/components/account-export.js
+++ b/ui/app/components/account-export.js
@@ -6,6 +6,7 @@ const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions')
const ethUtil = require('ethereumjs-util')
const connect = require('react-redux').connect
+const t = require('../../i18n')
module.exports = connect(mapStateToProps)(ExportAccountView)
@@ -35,7 +36,7 @@ ExportAccountView.prototype.render = function () {
if (notExporting) return h('div')
if (exportRequested) {
- const warning = `Export private keys at your own risk.`
+ const warning = t('exportPrivateKeyWarning')
return (
h('div', {
style: {
@@ -53,7 +54,7 @@ ExportAccountView.prototype.render = function () {
h('p.error', warning),
h('input#exportAccount.sizing-input', {
type: 'password',
- placeholder: 'confirm password',
+ placeholder: t('confirmPassword').toLowerCase(),
onKeyPress: this.onExportKeyPress.bind(this),
style: {
position: 'relative',
@@ -74,10 +75,10 @@ ExportAccountView.prototype.render = function () {
style: {
marginRight: '10px',
},
- }, 'Submit'),
+ }, t('submit')),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
- }, 'Cancel'),
+ }, t('cancel')),
]),
(this.props.warning) && (
h('span.error', {
@@ -98,7 +99,7 @@ ExportAccountView.prototype.render = function () {
margin: '0 20px',
},
}, [
- h('label', 'Your private key (click to copy):'),
+ h('label', t('copyPrivateKey') + ':'),
h('p.error.cursor-pointer', {
style: {
textOverflow: 'ellipsis',
@@ -112,13 +113,13 @@ ExportAccountView.prototype.render = function () {
}, plainKey),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
- }, 'Done'),
+ }, t('done')),
h('button', {
style: {
marginLeft: '10px',
},
onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey),
- }, 'Save as File'),
+ }, t('saveAsFile')),
])
}
}
diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js
index 2b371eedf..994be6a15 100644
--- a/ui/app/components/account-menu/index.js
+++ b/ui/app/components/account-menu/index.js
@@ -8,6 +8,7 @@ const actions = require('../../actions')
const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu')
const Identicon = require('../identicon')
const { formatBalance } = require('../../util')
+const t = require('../../../i18n')
const {
SETTINGS_ROUTE,
INFO_ROUTE,
@@ -74,13 +75,13 @@ AccountMenu.prototype.render = function () {
h(Item, {
className: 'account-menu__header',
}, [
- 'My Accounts',
+ t('myAccounts'),
h('button.account-menu__logout-button', {
onClick: () => {
lockMetamask()
history.push(DEFAULT_ROUTE)
},
- }, 'Log out'),
+ }, t('logout')),
]),
h(Divider),
h('div.account-menu__accounts', this.renderAccounts()),
@@ -91,7 +92,7 @@ AccountMenu.prototype.render = function () {
history.push(NEW_ACCOUNT_ROUTE)
},
icon: h('img.account-menu__item-icon', { src: 'images/plus-btn-white.svg' }),
- text: 'Create Account',
+ text: t('createAccount'),
}),
h(Item, {
onClick: () => {
@@ -99,7 +100,7 @@ AccountMenu.prototype.render = function () {
history.push(IMPORT_ACCOUNT_ROUTE)
},
icon: h('img.account-menu__item-icon', { src: 'images/import-account.svg' }),
- text: 'Import Account',
+ text: t('importAccount'),
}),
h(Divider),
h(Item, {
@@ -107,8 +108,8 @@ AccountMenu.prototype.render = function () {
toggleAccountMenu()
history.push(INFO_ROUTE)
},
- icon: h('img.account-menu__item-icon', { src: 'images/mm-info-icon.svg' }),
- text: 'Info & Help',
+ icon: h('img', { src: 'images/mm-info-icon.svg' }),
+ text: t('infoHelp'),
}),
h(Item, {
onClick: () => {
@@ -116,7 +117,7 @@ AccountMenu.prototype.render = function () {
history.push(SETTINGS_ROUTE)
},
icon: h('img.account-menu__item-icon', { src: 'images/settings.svg' }),
- text: 'Settings',
+ text: t('settings'),
}),
])
}
@@ -174,6 +175,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', 'IMPORTED') : null
+ return isLoose ? h('.keyring-label.allcaps', t('imported')) : null
} catch (e) { return }
}
diff --git a/ui/app/components/balance.js b/ui/app/components/balance.js
deleted file mode 100644
index 57ca84564..000000000
--- a/ui/app/components/balance.js
+++ /dev/null
@@ -1,89 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const formatBalance = require('../util').formatBalance
-const generateBalanceObject = require('../util').generateBalanceObject
-const Tooltip = require('./tooltip.js')
-const FiatValue = require('./fiat-value.js')
-
-module.exports = EthBalanceComponent
-
-inherits(EthBalanceComponent, Component)
-function EthBalanceComponent () {
- Component.call(this)
-}
-
-EthBalanceComponent.prototype.render = function () {
- var props = this.props
- let { value } = props
- var style = props.style
- var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
- value = value ? formatBalance(value, 6, needsParse) : '...'
- var width = props.width
-
- return (
-
- h('.ether-balance.ether-balance-amount', {
- style: style,
- }, [
- h('div', {
- style: {
- display: 'inline',
- width: width,
- },
- }, this.renderBalance(value)),
- ])
-
- )
-}
-EthBalanceComponent.prototype.renderBalance = function (value) {
- var props = this.props
- if (value === 'None') return value
- if (value === '...') return value
- var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3)
- var balance
- var splitBalance = value.split(' ')
- var ethNumber = splitBalance[0]
- var ethSuffix = splitBalance[1]
- const showFiat = 'showFiat' in props ? props.showFiat : true
-
- if (props.shorten) {
- balance = balanceObj.shortBalance
- } else {
- balance = balanceObj.balance
- }
-
- var label = balanceObj.label
-
- return (
- h(Tooltip, {
- position: 'bottom',
- title: `${ethNumber} ${ethSuffix}`,
- }, h('div.flex-column', [
- h('.flex-row', {
- style: {
- alignItems: 'flex-end',
- lineHeight: '13px',
- fontFamily: 'Montserrat Light',
- textRendering: 'geometricPrecision',
- },
- }, [
- h('div', {
- style: {
- width: '100%',
- textAlign: 'right',
- },
- }, this.props.incoming ? `+${balance}` : balance),
- h('div', {
- style: {
- color: ' #AEAEAE',
- fontSize: '12px',
- marginLeft: '5px',
- },
- }, label),
- ]),
-
- showFiat ? h(FiatValue, { value: props.value }) : null,
- ]))
- )
-}
diff --git a/ui/app/components/binary-renderer.js b/ui/app/components/binary-renderer.js
deleted file mode 100644
index 0b6a1f5c2..000000000
--- a/ui/app/components/binary-renderer.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const ethUtil = require('ethereumjs-util')
-const extend = require('xtend')
-
-module.exports = BinaryRenderer
-
-inherits(BinaryRenderer, Component)
-function BinaryRenderer () {
- Component.call(this)
-}
-
-BinaryRenderer.prototype.render = function () {
- const props = this.props
- const { value, style } = props
- const text = this.hexToText(value)
-
- const defaultStyle = extend({
- width: '315px',
- maxHeight: '210px',
- resize: 'none',
- border: 'none',
- background: 'white',
- padding: '3px',
- }, style)
-
- return (
- h('textarea.font-small', {
- readOnly: true,
- style: defaultStyle,
- defaultValue: text,
- })
- )
-}
-
-BinaryRenderer.prototype.hexToText = function (hex) {
- try {
- const stripped = ethUtil.stripHexPrefix(hex)
- const buff = Buffer.from(stripped, 'hex')
- return buff.toString('utf8')
- } catch (e) {
- return hex
- }
-}
-
diff --git a/ui/app/components/bn-as-decimal-input.js b/ui/app/components/bn-as-decimal-input.js
index 22e37602e..70701b039 100644
--- a/ui/app/components/bn-as-decimal-input.js
+++ b/ui/app/components/bn-as-decimal-input.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
+const t = require('../../i18n')
module.exports = BnAsDecimalInput
@@ -136,13 +137,13 @@ BnAsDecimalInput.prototype.constructWarning = function () {
let message = name ? name + ' ' : ''
if (min && max) {
- message += `must be greater than or equal to ${newMin} ${suffix} and less than or equal to ${newMax} ${suffix}.`
+ message += t('betweenMinAndMax', [`${newMin} ${suffix}`, `${newMax} ${suffix}`])
} else if (min) {
- message += `must be greater than or equal to ${newMin} ${suffix}.`
+ message += t('greaterThanMin', [`${newMin} ${suffix}`])
} else if (max) {
- message += `must be less than or equal to ${newMax} ${suffix}.`
+ message += t('lessThanMax', [`${newMax} ${suffix}`])
} else {
- message += 'Invalid input.'
+ message += t('invalidInput')
}
return message
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
index d5958787b..1e277a94b 100644
--- a/ui/app/components/buy-button-subview.js
+++ b/ui/app/components/buy-button-subview.js
@@ -9,6 +9,7 @@ const Loading = require('./loading')
const AccountPanel = require('./account-panel')
const RadioList = require('./custom-radio-list')
const networkNames = require('../../../app/scripts/config.js').networkNames
+const t = require('../../i18n')
module.exports = connect(mapStateToProps)(BuyButtonSubview)
@@ -76,7 +77,7 @@ BuyButtonSubview.prototype.headerSubview = function () {
paddingTop: '4px',
paddingBottom: '4px',
},
- }, 'Deposit Eth'),
+ }, t('depositEth')),
]),
// loading indication
@@ -118,7 +119,7 @@ BuyButtonSubview.prototype.headerSubview = function () {
paddingTop: '4px',
paddingBottom: '4px',
},
- }, 'Select Service'),
+ }, t('selectService')),
]),
])
@@ -143,7 +144,7 @@ BuyButtonSubview.prototype.primarySubview = function () {
case '4':
case '42':
const networkName = networkNames[network]
- const label = `${networkName} Test Faucet`
+ const label = `${networkName} ${t('testFaucet')}`
return (
h('div.flex-column', {
style: {
@@ -164,14 +165,14 @@ BuyButtonSubview.prototype.primarySubview = function () {
style: {
marginTop: '15px',
},
- }, 'Borrow With Dharma (Beta)')
+ }, t('borrowDharma'))
) : null,
])
)
default:
return (
- h('h2.error', 'Unknown network ID')
+ h('h2.error', t('unknownNetworkId'))
)
}
@@ -203,8 +204,8 @@ BuyButtonSubview.prototype.mainnetSubview = function () {
'ShapeShift',
],
subtext: {
- 'Coinbase': 'Crypto/FIAT (USA only)',
- 'ShapeShift': 'Crypto',
+ 'Coinbase': `${t('crypto')}/${t('fiat')} (${t('usaOnly')})`,
+ 'ShapeShift': t('crypto'),
},
onClick: this.radioHandler.bind(this),
}),
diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js
index f70208625..e442b43d5 100644
--- a/ui/app/components/coinbase-form.js
+++ b/ui/app/components/coinbase-form.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../actions')
+const t = require('../../i18n')
module.exports = connect(mapStateToProps)(CoinbaseForm)
@@ -37,11 +38,11 @@ CoinbaseForm.prototype.render = function () {
}, [
h('button.btn-green', {
onClick: this.toCoinbase.bind(this),
- }, 'Continue to Coinbase'),
+ }, t('continueToCoinbase')),
h('button.btn-red', {
onClick: () => props.dispatch(actions.goHome()),
- }, 'Cancel'),
+ }, t('cancel')),
]),
])
}
diff --git a/ui/app/components/copyButton.js b/ui/app/components/copyButton.js
index a25d0719c..355f78d45 100644
--- a/ui/app/components/copyButton.js
+++ b/ui/app/components/copyButton.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const copyToClipboard = require('copy-to-clipboard')
+const t = require('../../i18n')
const Tooltip = require('./tooltip')
@@ -22,7 +23,7 @@ CopyButton.prototype.render = function () {
const value = props.value
const copied = state.copied
- const message = copied ? 'Copied' : props.title || ' Copy '
+ const message = copied ? t('copiedButton') : props.title || t('copyButton')
return h('.copy-button', {
style: {
diff --git a/ui/app/components/copyable.js b/ui/app/components/copyable.js
index a4f6f4bc6..fca7d3863 100644
--- a/ui/app/components/copyable.js
+++ b/ui/app/components/copyable.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const Tooltip = require('./tooltip')
const copyToClipboard = require('copy-to-clipboard')
+const t = require('../../i18n')
module.exports = Copyable
@@ -22,7 +23,7 @@ Copyable.prototype.render = function () {
const { copied } = state
return h(Tooltip, {
- title: copied ? 'Copied!' : 'Copy',
+ title: copied ? t('copiedExclamation') : t('copy'),
position: 'bottom',
}, h('span', {
style: {
diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js
index 6f7862e51..ece3eb43d 100644
--- a/ui/app/components/currency-input.js
+++ b/ui/app/components/currency-input.js
@@ -8,8 +8,12 @@ inherits(CurrencyInput, Component)
function CurrencyInput (props) {
Component.call(this)
+ const sanitizedValue = sanitizeValue(props.value)
+
this.state = {
- value: sanitizeValue(props.value),
+ value: sanitizedValue,
+ emptyState: false,
+ focused: false,
}
}
@@ -58,9 +62,11 @@ CurrencyInput.prototype.handleChange = function (newValue) {
if (value === '0' && newValue[newValueLastIndex] === '0') {
parsedValue = parsedValue.slice(0, newValueLastIndex)
}
-
const sanitizedValue = sanitizeValue(parsedValue)
- this.setState({ value: sanitizedValue })
+ this.setState({
+ value: sanitizedValue,
+ emptyState: newValue === '' && sanitizedValue === '0',
+ })
onInputChange(sanitizedValue)
}
@@ -85,18 +91,22 @@ CurrencyInput.prototype.render = function () {
placeholder,
readOnly,
inputRef,
+ type,
} = this.props
+ const { emptyState, focused } = this.state
const inputSizeMultiplier = readOnly ? 1 : 1.2
const valueToRender = this.getValueToRender()
-
return h('input', {
className,
- value: valueToRender,
- placeholder,
+ type,
+ value: emptyState ? '' : valueToRender,
+ placeholder: focused ? '' : placeholder,
size: valueToRender.length * inputSizeMultiplier,
readOnly,
+ onFocus: () => this.setState({ focused: true, emptyState: valueToRender === '0' }),
+ onBlur: () => this.setState({ focused: false, emptyState: false }),
onChange: e => this.handleChange(e.target.value),
ref: inputRef,
})
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index 826d2cd4b..22ad98ce4 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
+const t = require('../../../i18n')
const GasModalCard = require('./gas-modal-card')
const ethUtil = require('ethereumjs-util')
@@ -21,12 +22,14 @@ const {
conversionUtil,
multiplyCurrencies,
conversionGreaterThan,
+ conversionMax,
subtractCurrencies,
} = require('../../conversion-util')
const {
getGasPrice,
getGasLimit,
+ getForceGasMin,
conversionRateSelector,
getSendAmount,
getSelectedToken,
@@ -44,6 +47,7 @@ function mapStateToProps (state) {
return {
gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state),
+ forceGasMin: getForceGasMin(state),
conversionRate,
amount: getSendAmount(state),
maxModeOn: getSendMaxModeState(state),
@@ -114,9 +118,9 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
updateSendAmount(maxAmount)
}
- updateGasPrice(gasPrice)
- updateGasLimit(gasLimit)
- updateGasTotal(gasTotal)
+ updateGasPrice(ethUtil.addHexPrefix(gasPrice))
+ updateGasLimit(ethUtil.addHexPrefix(gasLimit))
+ updateGasTotal(ethUtil.addHexPrefix(gasTotal))
hideModal()
}
@@ -146,7 +150,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
})
if (!balanceIsSufficient) {
- error = 'Insufficient balance for current gas total'
+ error = t('balanceIsInsufficientGas')
}
const gasLimitTooLow = gasLimit && conversionGreaterThan(
@@ -162,7 +166,7 @@ CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
)
if (gasLimitTooLow) {
- error = 'Gas limit must be at least 21000'
+ error = t('gasLimitTooLow')
}
this.setState({ error })
@@ -217,7 +221,7 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
}
CustomizeGasModal.prototype.render = function () {
- const { hideModal } = this.props
+ const { hideModal, forceGasMin } = this.props
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
let convertedGasPrice = conversionUtil(gasPrice, {
@@ -229,6 +233,22 @@ CustomizeGasModal.prototype.render = function () {
convertedGasPrice += convertedGasPrice.match(/[.]/) ? priceSigZeros : `${priceSigDec}${priceSigZeros}`
+ let newGasPrice = gasPrice
+ if (forceGasMin) {
+ const convertedMinPrice = conversionUtil(forceGasMin, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ })
+ convertedGasPrice = conversionMax(
+ { value: convertedMinPrice, fromNumericBase: 'dec' },
+ { value: convertedGasPrice, fromNumericBase: 'dec' }
+ )
+ newGasPrice = conversionMax(
+ { value: gasPrice, fromNumericBase: 'hex' },
+ { value: forceGasMin, fromNumericBase: 'hex' }
+ )
+ }
+
const convertedGasLimit = conversionUtil(gasLimit, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
@@ -239,7 +259,7 @@ CustomizeGasModal.prototype.render = function () {
}, [
h('div.send-v2__customize-gas__header', {}, [
- h('div.send-v2__customize-gas__title', 'Customize Gas'),
+ h('div.send-v2__customize-gas__title', t('customGas')),
h('div.send-v2__customize-gas__close', {
onClick: hideModal,
@@ -251,12 +271,12 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, {
value: convertedGasPrice,
- min: MIN_GAS_PRICE_GWEI,
+ min: forceGasMin || MIN_GAS_PRICE_GWEI,
// max: 1000,
step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10),
onChange: value => this.convertAndSetGasPrice(value),
- title: 'Gas Price (GWEI)',
- copy: 'We calculate the suggested gas prices based on network success rates.',
+ title: t('gasPrice'),
+ copy: t('gasPriceCalculation'),
}),
h(GasModalCard, {
@@ -265,8 +285,8 @@ CustomizeGasModal.prototype.render = function () {
// max: 100000,
step: 1,
onChange: value => this.convertAndSetGasLimit(value),
- title: 'Gas Limit',
- copy: 'We calculate the suggested gas limit based on network success rates.',
+ title: t('gasLimit'),
+ copy: t('gasLimitCalculation'),
}),
]),
@@ -279,16 +299,20 @@ CustomizeGasModal.prototype.render = function () {
h('div.send-v2__customize-gas__revert', {
onClick: () => this.revert(),
- }, ['Revert']),
+ }, [t('revert')]),
h('div.send-v2__customize-gas__buttons', [
- h('div.send-v2__customize-gas__cancel', {
+ h('button.btn-secondary.send-v2__customize-gas__cancel', {
onClick: this.props.hideModal,
- }, ['CANCEL']),
-
- h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, {
- onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal),
- }, ['SAVE']),
+ style: {
+ marginRight: '10px',
+ },
+ }, [t('cancel')]),
+
+ h('button.btn-primary.send-v2__customize-gas__save', {
+ onClick: () => !error && this.save(newGasPrice, gasLimit, gasTotal),
+ className: error && 'btn-primary--disabled',
+ }, [t('save')]),
]),
]),
diff --git a/ui/app/components/dropdowns/account-options-dropdown.js b/ui/app/components/dropdowns/account-options-dropdown.js
deleted file mode 100644
index f74c0a2d4..000000000
--- a/ui/app/components/dropdowns/account-options-dropdown.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const AccountDropdowns = require('./components/account-dropdowns')
-
-inherits(AccountOptionsDropdown, Component)
-function AccountOptionsDropdown () {
- Component.call(this)
-}
-
-module.exports = AccountOptionsDropdown
-
-// TODO: specify default props and proptypes
-// TODO: hook up to state, connect to redux to clean up API
-// TODO: selectedAddress is not defined... should we use selected?
-AccountOptionsDropdown.prototype.render = function () {
- const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props
-
- return h(AccountDropdowns, {
- enableAccountOptions: true,
- enableAccountsSelector: false,
- selected,
- network,
- identities,
- style: style || {},
- dropdownWrapperStyle: dropdownWrapperStyle || {},
- menuItemStyles: menuItemStyles || {},
- }, [])
-}
diff --git a/ui/app/components/dropdowns/account-selection-dropdown.js b/ui/app/components/dropdowns/account-selection-dropdown.js
deleted file mode 100644
index 2f6452b15..000000000
--- a/ui/app/components/dropdowns/account-selection-dropdown.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const AccountDropdowns = require('./components/account-dropdowns')
-
-inherits(AccountSelectionDropdown, Component)
-function AccountSelectionDropdown () {
- Component.call(this)
-}
-
-module.exports = AccountSelectionDropdown
-
-// TODO: specify default props and proptypes
-// TODO: hook up to state, connect to redux to clean up API
-// TODO: selectedAddress is not defined... should we use selected?
-AccountSelectionDropdown.prototype.render = function () {
- const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props
-
- return h(AccountDropdowns, {
- enableAccountOptions: false,
- enableAccountsSelector: true,
- selected,
- network,
- identities,
- style: style || {},
- dropdownWrapperStyle: dropdownWrapperStyle || {},
- menuItemStyles: menuItemStyles || {},
- }, [])
-}
diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js
index f97ac0691..e5359c1d6 100644
--- a/ui/app/components/dropdowns/components/account-dropdowns.js
+++ b/ui/app/components/dropdowns/components/account-dropdowns.js
@@ -1,5 +1,5 @@
const Component = require('react').Component
-const PropTypes = require('react').PropTypes
+const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const actions = require('../../../actions')
const genAccountLink = require('../../../../lib/account-link.js')
@@ -10,6 +10,7 @@ const Identicon = require('../../identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard')
const { formatBalance } = require('../../../util')
+const t = require('../../../../i18n')
class AccountDropdowns extends Component {
constructor (props) {
@@ -121,7 +122,7 @@ class AccountDropdowns extends Component {
flex: '3 3 auto',
},
}, [
- h('span.account-dropdown-edit-button', {
+ h('span.account-dropdown-edit-button.allcaps', {
style: {
fontSize: '16px',
},
@@ -129,27 +130,11 @@ class AccountDropdowns extends Component {
actions.showEditAccountModal(identity)
},
}, [
- 'Edit',
+ t('edit'),
]),
]),
]),
-// =======
-// },
-// ),
-// this.indicateIfLoose(keyring),
-// h('span', {
-// style: {
-// marginLeft: '20px',
-// fontSize: '24px',
-// maxWidth: '145px',
-// whiteSpace: 'nowrap',
-// overflow: 'hidden',
-// textOverflow: 'ellipsis',
-// },
-// }, identity.name || ''),
-// h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null),
-// >>>>>>> master:ui/app/components/account-dropdowns.js
]
)
})
@@ -159,7 +144,7 @@ class AccountDropdowns extends Component {
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.allcaps', t('loose')) : null
} catch (e) { return }
}
@@ -217,7 +202,7 @@ class AccountDropdowns extends Component {
fontSize: '16px',
lineHeight: '23px',
},
- }, 'Create Account'),
+ }, t('createAccount')),
],
),
h(
@@ -251,7 +236,7 @@ class AccountDropdowns extends Component {
fontSize: '16px',
lineHeight: '23px',
},
- }, 'Import Account'),
+ }, t('importAccount')),
]
),
]
@@ -281,7 +266,7 @@ class AccountDropdowns extends Component {
dropdownWrapperStyle,
),
isOpen: optionsMenuActive,
- onClickOutside: () => {
+ onClickOutside: (event) => {
const { classList } = event.target
const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName)
if (optionsMenuActive && isNotToggleElement) {
@@ -302,7 +287,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'Account Details',
+ t('accountDetails'),
),
h(
DropdownMenuItem,
@@ -318,7 +303,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'View account on Etherscan',
+ t('etherscanView'),
),
h(
DropdownMenuItem,
@@ -334,7 +319,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'Copy Address to clipboard',
+ t('copyAddress'),
),
h(
DropdownMenuItem,
@@ -346,7 +331,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'Export Private Key',
+ t('exportPrivateKey'),
),
h(
DropdownMenuItem,
@@ -361,7 +346,7 @@ class AccountDropdowns extends Component {
menuItemStyles,
),
},
- 'Add Token',
+ t('addToken'),
),
]
@@ -479,4 +464,3 @@ function mapStateToProps (state) {
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDropdowns)
-
diff --git a/ui/app/components/dropdowns/components/dropdown.js b/ui/app/components/dropdowns/components/dropdown.js
index 15d064be8..0336dbb8b 100644
--- a/ui/app/components/dropdowns/components/dropdown.js
+++ b/ui/app/components/dropdowns/components/dropdown.js
@@ -1,5 +1,5 @@
const Component = require('react').Component
-const PropTypes = require('react').PropTypes
+const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const MenuDroppo = require('../../menu-droppo')
const extend = require('xtend')
diff --git a/ui/app/components/dropdowns/index.js b/ui/app/components/dropdowns/index.js
index fa66f5000..605507058 100644
--- a/ui/app/components/dropdowns/index.js
+++ b/ui/app/components/dropdowns/index.js
@@ -1,17 +1,11 @@
// Reusable Dropdown Components
// TODO: Refactor into separate components
const Dropdown = require('./components/dropdown').Dropdown
-const AccountDropdowns = require('./components/account-dropdowns')
// App-Specific Instances
-const AccountSelectionDropdown = require('./account-selection-dropdown')
-const AccountOptionsDropdown = require('./account-options-dropdown')
const NetworkDropdown = require('./network-dropdown').default
module.exports = {
- AccountSelectionDropdown,
- AccountOptionsDropdown,
NetworkDropdown,
Dropdown,
- AccountDropdowns,
}
diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js
index dfaa6b22c..5afe730c1 100644
--- a/ui/app/components/dropdowns/network-dropdown.js
+++ b/ui/app/components/dropdowns/network-dropdown.js
@@ -6,6 +6,7 @@ const actions = require('../../actions')
const Dropdown = require('./components/dropdown').Dropdown
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
const NetworkDropdownIcon = require('./components/network-dropdown-icon')
+const t = require('../../../i18n')
const R = require('ramda')
// classes from nodes of the toggle element.
@@ -84,7 +85,7 @@ NetworkDropdown.prototype.render = function () {
style: {
position: 'absolute',
top: '58px',
- minWidth: '309px',
+ width: '309px',
zIndex: '55px',
},
innerStyle: {
@@ -93,13 +94,13 @@ NetworkDropdown.prototype.render = function () {
}, [
h('div.network-dropdown-header', {}, [
- h('div.network-dropdown-title', {}, 'Networks'),
+ h('div.network-dropdown-title', {}, t('networks')),
h('div.network-dropdown-divider'),
h('div.network-dropdown-content',
{},
- 'The default network for Ether transactions is Main Net.'
+ t('defaultNetwork')
),
]),
@@ -114,14 +115,14 @@ NetworkDropdown.prototype.render = function () {
[
providerType === 'mainnet' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
h(NetworkDropdownIcon, {
- backgroundColor: '#038789', // $blue-lagoon
+ backgroundColor: '#29B6AF', // $java
isSelected: providerType === 'mainnet',
}),
h('span.network-name-item', {
style: {
color: providerType === 'mainnet' ? '#ffffff' : '#9b9b9b',
},
- }, 'Main Ethereum Network'),
+ }, t('mainnet')),
]
),
@@ -136,14 +137,14 @@ NetworkDropdown.prototype.render = function () {
[
providerType === 'ropsten' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
h(NetworkDropdownIcon, {
- backgroundColor: '#e91550', // $crimson
+ backgroundColor: '#ff4a8d', // $wild-strawberry
isSelected: providerType === 'ropsten',
}),
h('span.network-name-item', {
style: {
color: providerType === 'ropsten' ? '#ffffff' : '#9b9b9b',
},
- }, 'Ropsten Test Network'),
+ }, t('ropsten')),
]
),
@@ -158,14 +159,14 @@ NetworkDropdown.prototype.render = function () {
[
providerType === 'kovan' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
h(NetworkDropdownIcon, {
- backgroundColor: '#690496', // $purple
+ backgroundColor: '#7057ff', // $cornflower-blue
isSelected: providerType === 'kovan',
}),
h('span.network-name-item', {
style: {
color: providerType === 'kovan' ? '#ffffff' : '#9b9b9b',
},
- }, 'Kovan Test Network'),
+ }, t('kovan')),
]
),
@@ -180,14 +181,14 @@ NetworkDropdown.prototype.render = function () {
[
providerType === 'rinkeby' ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
h(NetworkDropdownIcon, {
- backgroundColor: '#ebb33f', // $tulip-tree
+ backgroundColor: '#f6c343', // $saffron
isSelected: providerType === 'rinkeby',
}),
h('span.network-name-item', {
style: {
color: providerType === 'rinkeby' ? '#ffffff' : '#9b9b9b',
},
- }, 'Rinkeby Test Network'),
+ }, t('rinkeby')),
]
),
@@ -209,7 +210,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: activeNetwork === 'http://localhost:8545' ? '#ffffff' : '#9b9b9b',
},
- }, 'Localhost 8545'),
+ }, t('localhost')),
]
),
@@ -233,7 +234,7 @@ NetworkDropdown.prototype.render = function () {
style: {
color: activeNetwork === 'custom' ? '#ffffff' : '#9b9b9b',
},
- }, 'Custom RPC'),
+ }, t('customRPC')),
]
),
@@ -248,15 +249,15 @@ NetworkDropdown.prototype.getNetworkName = function () {
let name
if (providerName === 'mainnet') {
- name = 'Main Ethereum Network'
+ name = t('mainnet')
} else if (providerName === 'ropsten') {
- name = 'Ropsten Test Network'
+ name = t('ropsten')
} else if (providerName === 'kovan') {
- name = 'Kovan Test Network'
+ name = t('kovan')
} else if (providerName === 'rinkeby') {
- name = 'Rinkeby Test Network'
+ name = t('rinkeby')
} else {
- name = 'Unknown Private Network'
+ name = t('unknownNetwork')
}
return name
@@ -276,11 +277,21 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) {
key: `common${rpc}`,
closeMenu: () => this.props.hideNetworkDropdown(),
onClick: () => props.setRpcTarget(rpc),
+ style: {
+ fontFamily: 'DIN OT',
+ fontSize: '16px',
+ lineHeight: '20px',
+ padding: '12px 0',
+ },
},
[
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- rpc,
- rpcTarget === rpc ? h('.check', '✓') : null,
+ rpcTarget === rpc ? h('i.fa.fa-check') : h('.network-check__transparent', '✓'),
+ h('i.fa.fa-question-circle.fa-med.menu-icon-circle'),
+ h('span.network-name-item', {
+ style: {
+ color: rpcTarget === rpc ? '#ffffff' : '#9b9b9b',
+ },
+ }, rpc),
]
)
}
@@ -293,12 +304,6 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) {
if (type !== 'rpc') return null
- // Concatenate long URLs
- let label = rpcTarget
- if (rpcTarget.length > 31) {
- label = label.substr(0, 34) + '...'
- }
-
switch (rpcTarget) {
case 'http://localhost:8545':
@@ -311,11 +316,21 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) {
key: rpcTarget,
onClick: () => props.setRpcTarget(rpcTarget),
closeMenu: () => this.props.hideNetworkDropdown(),
+ style: {
+ fontFamily: 'DIN OT',
+ fontSize: '16px',
+ lineHeight: '20px',
+ padding: '12px 0',
+ },
},
[
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- label,
- h('.check', '✓'),
+ h('i.fa.fa-check'),
+ h('i.fa.fa-question-circle.fa-med.menu-icon-circle'),
+ h('span.network-name-item', {
+ style: {
+ color: '#ffffff',
+ },
+ }, rpcTarget),
]
)
}
diff --git a/ui/app/components/dropdowns/simple-dropdown.js b/ui/app/components/dropdowns/simple-dropdown.js
index 7bb48e57b..bba088ed1 100644
--- a/ui/app/components/dropdowns/simple-dropdown.js
+++ b/ui/app/components/dropdowns/simple-dropdown.js
@@ -1,5 +1,5 @@
const { Component } = require('react')
-const PropTypes = require('react').PropTypes
+const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const classnames = require('classnames')
const R = require('ramda')
diff --git a/ui/app/components/dropdowns/token-menu-dropdown.js b/ui/app/components/dropdowns/token-menu-dropdown.js
index dc7a985e3..a4f93b505 100644
--- a/ui/app/components/dropdowns/token-menu-dropdown.js
+++ b/ui/app/components/dropdowns/token-menu-dropdown.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
+const t = require('../../../i18n')
module.exports = connect(null, mapDispatchToProps)(TokenMenuDropdown)
@@ -43,7 +44,7 @@ TokenMenuDropdown.prototype.render = function () {
showHideTokenConfirmationModal(this.props.token)
this.props.onClose()
},
- }, 'Hide Token'),
+ }, t('hideToken')),
]),
]),
diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js
index 6553053f7..1b2d4009d 100644
--- a/ui/app/components/ens-input.js
+++ b/ui/app/components/ens-input.js
@@ -8,6 +8,8 @@ const ENS = require('ethjs-ens')
const networkMap = require('ethjs-ens/lib/network-map.json')
const ensRE = /.+\..+$/
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
+const t = require('../../i18n')
+const ToAutoComplete = require('./send/to-autocomplete')
module.exports = EnsInput
@@ -21,12 +23,14 @@ EnsInput.prototype.render = function () {
const props = this.props
const opts = extend(props, {
list: 'addresses',
- onChange: () => {
+ onChange: (recipient) => {
const network = this.props.network
const networkHasEnsSupport = getNetworkEnsSupport(network)
+
if (!networkHasEnsSupport) return
- const recipient = document.querySelector('input[name="address"]').value
+ props.onChange(recipient)
+
if (recipient.match(ensRE) === null) {
return this.setState({
loadingEns: false,
@@ -38,34 +42,13 @@ EnsInput.prototype.render = function () {
this.setState({
loadingEns: true,
})
- this.checkName()
+ this.checkName(recipient)
},
})
return h('div', {
- style: { width: '100%' },
+ style: { width: '100%', position: 'relative' },
}, [
- h('input.large-input.send-screen-input', opts),
- // The address book functionality.
- h('datalist#addresses',
- [
- // Corresponds to the addresses owned.
- Object.keys(props.identities).map((key) => {
- const identity = props.identities[key]
- return h('option', {
- value: identity.address,
- label: identity.name,
- key: identity.address,
- })
- }),
- // Corresponds to previously sent-to addresses.
- props.addressBook.map((identity) => {
- return h('option', {
- value: identity.address,
- label: identity.name,
- key: identity.address,
- })
- }),
- ]),
+ h(ToAutoComplete, { ...opts }),
this.ensIcon(),
])
}
@@ -82,20 +65,19 @@ EnsInput.prototype.componentDidMount = function () {
}
}
-EnsInput.prototype.lookupEnsName = function () {
- const recipient = document.querySelector('input[name="address"]').value
+EnsInput.prototype.lookupEnsName = function (recipient) {
const { ensResolution } = this.state
log.info(`ENS attempting to resolve name: ${recipient}`)
this.ens.lookup(recipient.trim())
.then((address) => {
- if (address === ZERO_ADDRESS) throw new Error('No address has been set for this name.')
+ if (address === ZERO_ADDRESS) throw new Error(t('noAddressForName'))
if (address !== ensResolution) {
this.setState({
loadingEns: false,
ensResolution: address,
nickname: recipient.trim(),
- hoverText: address + '\nClick to Copy',
+ hoverText: address + '\n' + t('clickCopy'),
ensFailure: false,
})
}
@@ -129,8 +111,8 @@ EnsInput.prototype.ensIcon = function (recipient) {
title: hoverText,
style: {
position: 'absolute',
- padding: '9px',
- transform: 'translatex(-40px)',
+ top: '16px',
+ left: '-25px',
},
}, this.ensIconContents(recipient))
}
diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js
index 1be8c9731..c3d084bdc 100644
--- a/ui/app/components/eth-balance.js
+++ b/ui/app/components/eth-balance.js
@@ -46,7 +46,7 @@ EthBalanceComponent.prototype.renderBalance = function (value) {
incoming,
currentCurrency,
hideTooltip,
- styleOveride,
+ styleOveride = {},
showFiat = true,
} = this.props
const { fontSize, color, fontFamily, lineHeight } = styleOveride
diff --git a/ui/app/components/hex-as-decimal-input.js b/ui/app/components/hex-as-decimal-input.js
index 4a71e9585..a43d44f89 100644
--- a/ui/app/components/hex-as-decimal-input.js
+++ b/ui/app/components/hex-as-decimal-input.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
+const t = require('../../i18n')
module.exports = HexAsDecimalInput
@@ -126,13 +127,13 @@ HexAsDecimalInput.prototype.constructWarning = function () {
let message = name ? name + ' ' : ''
if (min && max) {
- message += `must be greater than or equal to ${min} and less than or equal to ${max}.`
+ message += t('betweenMinAndMax', [min, max])
} else if (min) {
- message += `must be greater than or equal to ${min}.`
+ message += t('greaterThanMin', [min])
} else if (max) {
- message += `must be less than or equal to ${max}.`
+ message += t('lessThanMax', [max])
} else {
- message += 'Invalid input.'
+ message += t('invalidInput')
}
return message
diff --git a/ui/app/components/input-number.js b/ui/app/components/input-number.js
index fd8c5c309..5600e35ee 100644
--- a/ui/app/components/input-number.js
+++ b/ui/app/components/input-number.js
@@ -55,6 +55,7 @@ InputNumber.prototype.render = function () {
className: 'customize-gas-input',
value,
placeholder,
+ type: 'number',
onInputChange: newValue => {
this.setValue(newValue)
},
diff --git a/ui/app/components/loading.js b/ui/app/components/loading.js
index 9442121fe..cb6fa51fb 100644
--- a/ui/app/components/loading.js
+++ b/ui/app/components/loading.js
@@ -1,6 +1,6 @@
const { Component } = require('react')
const h = require('react-hyperscript')
-const PropTypes = require('react').PropTypes
+const PropTypes = require('prop-types')
class LoadingIndicator extends Component {
renderMessage () {
diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js
deleted file mode 100644
index c09cf5b7a..000000000
--- a/ui/app/components/mini-account-panel.js
+++ /dev/null
@@ -1,74 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const Identicon = require('./identicon')
-
-module.exports = AccountPanel
-
-
-inherits(AccountPanel, Component)
-function AccountPanel () {
- Component.call(this)
-}
-
-AccountPanel.prototype.render = function () {
- var props = this.props
- var picOrder = props.picOrder || 'left'
- const { imageSeed } = props
-
- return (
-
- h('.identity-panel.flex-row.flex-left', {
- style: {
- cursor: props.onClick ? 'pointer' : undefined,
- },
- onClick: props.onClick,
- }, [
-
- this.genIcon(imageSeed, picOrder),
-
- h('div.flex-column.flex-justify-center', {
- style: {
- lineHeight: '15px',
- order: 2,
- display: 'flex',
- alignItems: picOrder === 'left' ? 'flex-begin' : 'flex-end',
- },
- }, this.props.children),
- ])
- )
-}
-
-AccountPanel.prototype.genIcon = function (seed, picOrder) {
- const props = this.props
-
- // When there is no seed value, this is a contract creation.
- // We then show the contract icon.
- if (!seed) {
- return h('.identicon-wrapper.flex-column.select-none', {
- style: {
- order: picOrder === 'left' ? 1 : 3,
- },
- }, [
- h('i.fa.fa-file-text-o.fa-lg', {
- style: {
- fontSize: '42px',
- transform: 'translate(0px, -16px)',
- },
- }),
- ])
- }
-
- // If there was a seed, we return an identicon for that address.
- return h('.identicon-wrapper.flex-column.select-none', {
- style: {
- order: picOrder === 'left' ? 1 : 3,
- },
- }, [
- h(Identicon, {
- address: seed,
- imageify: props.imageifyIdenticons,
- }),
- ])
-}
-
diff --git a/ui/app/components/modals/account-details-modal.js b/ui/app/components/modals/account-details-modal.js
index c1f7a3236..927d73482 100644
--- a/ui/app/components/modals/account-details-modal.js
+++ b/ui/app/components/modals/account-details-modal.js
@@ -8,6 +8,7 @@ const { getSelectedIdentity } = require('../../selectors')
const genAccountLink = require('../../../lib/account-link.js')
const QrView = require('../qr-code')
const EditableLabel = require('../editable-label')
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -62,14 +63,14 @@ AccountDetailsModal.prototype.render = function () {
h('div.account-modal-divider'),
- h('button.btn-clear.account-modal__button', {
+ h('button.btn-primary.account-modal__button', {
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
- }, 'View account on Etherscan'),
+ }, t('etherscanView')),
// Holding on redesign for Export Private Key functionality
- h('button.btn-clear.account-modal__button', {
+ h('button.btn-primary.account-modal__button', {
onClick: () => showExportPrivateKeyModal(),
- }, 'Export private key'),
+ }, t('exportPrivateKey')),
])
}
diff --git a/ui/app/components/modals/account-modal-container.js b/ui/app/components/modals/account-modal-container.js
index c548fb7b3..08540aa76 100644
--- a/ui/app/components/modals/account-modal-container.js
+++ b/ui/app/components/modals/account-modal-container.js
@@ -5,6 +5,7 @@ const connect = require('react-redux').connect
const actions = require('../../actions')
const { getSelectedIdentity } = require('../../selectors')
const Identicon = require('../identicon')
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -59,7 +60,7 @@ AccountModalContainer.prototype.render = function () {
h('i.fa.fa-angle-left.fa-lg'),
- h('span.account-modal-back__text', ' Back'),
+ h('span.account-modal-back__text', ' ' + t('back')),
]),
diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js
index 74a7a847e..7eb73c3a6 100644
--- a/ui/app/components/modals/buy-options-modal.js
+++ b/ui/app/components/modals/buy-options-modal.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const networkNames = require('../../../../app/scripts/config.js').networkNames
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -56,15 +57,15 @@ BuyOptions.prototype.render = function () {
}, [
h('div.buy-modal-content-title', {
style: {},
- }, 'Transfers'),
- h('div', {}, 'How would you like to deposit Ether?'),
+ }, t('transfers')),
+ h('div', {}, t('howToDeposit')),
]),
h('div.buy-modal-content-options.flex-column.flex-center', {}, [
isTestNetwork
- ? this.renderModalContentOption(networkName, 'Test Faucet', () => toFaucet(network))
- : this.renderModalContentOption('Coinbase', 'Deposit with Fiat', () => toCoinbase(address)),
+ ? this.renderModalContentOption(networkName, t('testFaucet'), () => toFaucet(network))
+ : this.renderModalContentOption('Coinbase', t('depositFiat'), () => toCoinbase(address)),
// h('div.buy-modal-content-option', {}, [
// h('div.buy-modal-content-option-title', {}, 'Shapeshift'),
@@ -72,8 +73,8 @@ BuyOptions.prototype.render = function () {
// ]),,
this.renderModalContentOption(
- 'Direct Deposit',
- 'Deposit from another account',
+ t('directDeposit'),
+ t('depositFromAccount'),
() => this.goToAccountDetailsModal()
),
@@ -84,7 +85,7 @@ BuyOptions.prototype.render = function () {
background: 'white',
},
onClick: () => { this.props.hideModal() },
- }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, 'Cancel')),
+ }, h('div.buy-modal-content-footer#buy-modal-content-footer-text', {}, t('cancel'))),
]),
])
}
diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js
index 532d66653..2de1240fc 100644
--- a/ui/app/components/modals/deposit-ether-modal.js
+++ b/ui/app/components/modals/deposit-ether-modal.js
@@ -5,18 +5,18 @@ const connect = require('react-redux').connect
const actions = require('../../actions')
const networkNames = require('../../../../app/scripts/config.js').networkNames
const ShapeshiftForm = require('../shapeshift-form')
-
-const DIRECT_DEPOSIT_ROW_TITLE = 'Directly Deposit Ether'
-const DIRECT_DEPOSIT_ROW_TEXT = `If you already have some Ether, the quickest way to get Ether in
-your new wallet by direct deposit.`
-const COINBASE_ROW_TITLE = 'Buy on Coinbase'
-const COINBASE_ROW_TEXT = `Coinbase is the world’s most popular way to buy and sell bitcoin,
-ethereum, and litecoin.`
-const SHAPESHIFT_ROW_TITLE = 'Deposit with ShapeShift'
-const SHAPESHIFT_ROW_TEXT = `If you own other cryptocurrencies, you can trade and deposit Ether
-directly into your MetaMask wallet. No Account Needed.`
-const FAUCET_ROW_TITLE = 'Test Faucet'
-const facuetRowText = networkName => `Get Ether from a faucet for the ${networkName}`
+const t = require('../../../i18n')
+
+const DIRECT_DEPOSIT_ROW_TITLE = t('directDepositEther')
+const DIRECT_DEPOSIT_ROW_TEXT = t('directDepositEtherExplainer')
+const COINBASE_ROW_TITLE = t('buyCoinbase')
+const COINBASE_ROW_TEXT = t('buyCoinbaseExplainer')
+const SHAPESHIFT_ROW_TITLE = t('depositShapeShift')
+const SHAPESHIFT_ROW_TEXT = t('depositShapeShiftExplainer')
+const FAUCET_ROW_TITLE = t('testFaucet')
+const facuetRowText = (networkName) => {
+ return t('getEtherFromFaucet', [networkName])
+}
function mapStateToProps (state) {
return {
@@ -33,6 +33,9 @@ function mapDispatchToProps (dispatch) {
hideModal: () => {
dispatch(actions.hideModal())
},
+ hideWarning: () => {
+ dispatch(actions.hideWarning())
+ },
showAccountDetailModal: () => {
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
},
@@ -80,7 +83,7 @@ DepositEtherModal.prototype.renderRow = function ({
]),
- h('div.deposit-ether-modal__buy-row__logo', [logo]),
+ h('div.deposit-ether-modal__buy-row__logo-container', [logo]),
h('div.deposit-ether-modal__buy-row__description', [
@@ -91,7 +94,7 @@ DepositEtherModal.prototype.renderRow = function ({
]),
!hideButton && h('div.deposit-ether-modal__buy-row__button', [
- h('button.deposit-ether-modal__deposit-button', {
+ h('button.btn-primary--lg.deposit-ether-modal__deposit-button', {
onClick: onButtonClick,
}, [buttonLabel]),
]),
@@ -106,79 +109,92 @@ DepositEtherModal.prototype.render = function () {
const isTestNetwork = ['3', '4', '42'].find(n => n === network)
const networkName = networkNames[network]
- return h('div.deposit-ether-modal', {}, [
+ return h('div.page-container.page-container--full-width.page-container--full-height', {}, [
- h('div.deposit-ether-modal__header', [
+ h('div.page-container__header', [
- h('div.deposit-ether-modal__header__title', ['Deposit Ether']),
+ h('div.page-container__title', [t('depositEther')]),
- h('div.deposit-ether-modal__header__description', [
- 'To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet.',
+ h('div.page-container__subtitle', [
+ t('needEtherInWallet'),
]),
- h('div.deposit-ether-modal__header__close', {
+ h('div.page-container__header-close', {
onClick: () => {
this.setState({ buyingWithShapeshift: false })
+ this.props.hideWarning()
this.props.hideModal()
},
}),
]),
- h('div.deposit-ether-modal__buy-rows', [
+ h('.page-container__content', {}, [
- this.renderRow({
- logo: h('img.deposit-ether-modal__buy-row__eth-logo', { src: '../../../images/eth_logo.svg' }),
- title: DIRECT_DEPOSIT_ROW_TITLE,
- text: DIRECT_DEPOSIT_ROW_TEXT,
- buttonLabel: 'View Account',
- onButtonClick: () => this.goToAccountDetailsModal(),
- hide: buyingWithShapeshift,
- }),
+ h('div.deposit-ether-modal__buy-rows', [
- this.renderRow({
- logo: h('i.fa.fa-tint.fa-2x'),
- title: FAUCET_ROW_TITLE,
- text: facuetRowText(networkName),
- buttonLabel: 'Get Ether',
- onButtonClick: () => toFaucet(network),
- hide: !isTestNetwork || buyingWithShapeshift,
- }),
+ this.renderRow({
+ logo: h('img.deposit-ether-modal__logo', {
+ src: '../../../images/deposit-eth.svg',
+ }),
+ title: DIRECT_DEPOSIT_ROW_TITLE,
+ text: DIRECT_DEPOSIT_ROW_TEXT,
+ buttonLabel: t('viewAccount'),
+ onButtonClick: () => this.goToAccountDetailsModal(),
+ hide: buyingWithShapeshift,
+ }),
- this.renderRow({
- logo: h('img.deposit-ether-modal__buy-row__coinbase-logo', {
- src: '../../../images/coinbase logo.png',
+ this.renderRow({
+ logo: h('i.fa.fa-tint.fa-2x'),
+ title: FAUCET_ROW_TITLE,
+ text: facuetRowText(networkName),
+ buttonLabel: t('getEther'),
+ onButtonClick: () => toFaucet(network),
+ hide: !isTestNetwork || buyingWithShapeshift,
}),
- title: COINBASE_ROW_TITLE,
- text: COINBASE_ROW_TEXT,
- buttonLabel: 'Continue to Coinbase',
- onButtonClick: () => toCoinbase(address),
- hide: isTestNetwork || buyingWithShapeshift,
- }),
- this.renderRow({
- logo: h('img.deposit-ether-modal__buy-row__shapeshift-logo', {
- src: '../../../images/shapeshift logo.png',
+ this.renderRow({
+ logo: h('div.deposit-ether-modal__logo', {
+ style: {
+ backgroundImage: 'url(\'../../../images/coinbase logo.png\')',
+ height: '40px',
+ },
+ }),
+ title: COINBASE_ROW_TITLE,
+ text: COINBASE_ROW_TEXT,
+ buttonLabel: t('continueToCoinbase'),
+ onButtonClick: () => toCoinbase(address),
+ hide: isTestNetwork || buyingWithShapeshift,
}),
- title: SHAPESHIFT_ROW_TITLE,
- text: SHAPESHIFT_ROW_TEXT,
- buttonLabel: 'Buy with Shapeshift',
- onButtonClick: () => this.setState({ buyingWithShapeshift: true }),
- hide: isTestNetwork,
- hideButton: buyingWithShapeshift,
- hideTitle: buyingWithShapeshift,
- onBackClick: () => this.setState({ buyingWithShapeshift: false }),
- showBackButton: this.state.buyingWithShapeshift,
- className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy',
- }),
- buyingWithShapeshift && h(ShapeshiftForm),
+ this.renderRow({
+ logo: h('div.deposit-ether-modal__logo', {
+ style: {
+ backgroundImage: 'url(\'../../../images/shapeshift logo.png\')',
+ },
+ }),
+ title: SHAPESHIFT_ROW_TITLE,
+ text: SHAPESHIFT_ROW_TEXT,
+ buttonLabel: t('shapeshiftBuy'),
+ onButtonClick: () => this.setState({ buyingWithShapeshift: true }),
+ hide: isTestNetwork,
+ hideButton: buyingWithShapeshift,
+ hideTitle: buyingWithShapeshift,
+ onBackClick: () => this.setState({ buyingWithShapeshift: false }),
+ showBackButton: this.state.buyingWithShapeshift,
+ className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy',
+ }),
+
+ buyingWithShapeshift && h(ShapeshiftForm),
+
+ ]),
]),
])
}
DepositEtherModal.prototype.goToAccountDetailsModal = function () {
+ this.props.hideWarning()
this.props.hideModal()
this.props.showAccountDetailModal()
}
diff --git a/ui/app/components/modals/edit-account-name-modal.js b/ui/app/components/modals/edit-account-name-modal.js
index e2361140d..6efa8d476 100644
--- a/ui/app/components/modals/edit-account-name-modal.js
+++ b/ui/app/components/modals/edit-account-name-modal.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const { getSelectedAccount } = require('../../selectors')
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -50,7 +51,7 @@ EditAccountNameModal.prototype.render = function () {
]),
h('div.edit-account-name-modal-title', {
- }, ['Edit Account Name']),
+ }, [t('editAccountName')]),
h('input.edit-account-name-modal-input', {
placeholder: identity.name,
@@ -60,7 +61,7 @@ EditAccountNameModal.prototype.render = function () {
value: this.state.inputText,
}, []),
- h('button.btn-clear.edit-account-name-modal-save-button', {
+ h('button.btn-clear.edit-account-name-modal-save-button.allcaps', {
onClick: () => {
if (this.state.inputText.length !== 0) {
saveAccountLabel(identity.address, this.state.inputText)
@@ -69,7 +70,7 @@ EditAccountNameModal.prototype.render = function () {
},
disabled: this.state.inputText.length === 0,
}, [
- 'SAVE',
+ t('save'),
]),
]),
diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js
index 422f23f44..cf42e4fa2 100644
--- a/ui/app/components/modals/export-private-key-modal.js
+++ b/ui/app/components/modals/export-private-key-modal.js
@@ -7,6 +7,7 @@ const actions = require('../../actions')
const AccountModalContainer = require('./account-modal-container')
const { getSelectedIdentity } = require('../../selectors')
const ReadOnlyInput = require('../readonly-input')
+const t = require('../../../i18n')
const copyToClipboard = require('copy-to-clipboard')
function mapStateToProps (state) {
@@ -48,8 +49,8 @@ ExportPrivateKeyModal.prototype.exportAccountAndGetPrivateKey = function (passwo
ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) {
return h('span.private-key-password-label', privateKey
- ? 'This is your private key (click to copy)'
- : 'Type Your Password'
+ ? t('copyPrivateKey')
+ : t('typePassword')
)
}
@@ -80,14 +81,14 @@ ExportPrivateKeyModal.prototype.renderButton = function (className, onClick, lab
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) {
return h('div.export-private-key-buttons', {}, [
!privateKey && this.renderButton(
- 'btn-cancel export-private-key__button export-private-key__button--cancel',
+ 'btn-secondary--lg export-private-key__button export-private-key__button--cancel',
() => hideModal(),
'Cancel'
),
(privateKey
- ? this.renderButton('btn-clear export-private-key__button', () => hideModal(), 'Done')
- : this.renderButton('btn-clear export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), 'Confirm')
+ ? this.renderButton('btn-primary--lg export-private-key__button', () => hideModal(), t('done'))
+ : this.renderButton('btn-primary--lg export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), t('confirm'))
),
])
@@ -120,7 +121,7 @@ ExportPrivateKeyModal.prototype.render = function () {
h('div.account-modal-divider'),
- h('span.modal-body-title', 'Show Private Keys'),
+ h('span.modal-body-title', t('showPrivateKeys')),
h('div.private-key-password', {}, [
this.renderPasswordLabel(privateKey),
@@ -130,10 +131,7 @@ ExportPrivateKeyModal.prototype.render = function () {
!warning ? null : h('span.private-key-password-error', warning),
]),
- h('div.private-key-password-warning', `Warning: Never disclose this key.
- Anyone with your private keys can take steal any assets held in your
- account.`
- ),
+ h('div.private-key-password-warning', t('privateKeyWarning')),
this.renderButtons(privateKey, this.state.password, address, hideModal),
diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js
index 56c7ba299..33d8062c6 100644
--- a/ui/app/components/modals/hide-token-confirmation-modal.js
+++ b/ui/app/components/modals/hide-token-confirmation-modal.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../actions')
const Identicon = require('../identicon')
+const t = require('../../../i18n')
function mapStateToProps (state) {
return {
@@ -41,7 +42,7 @@ HideTokenConfirmationModal.prototype.render = function () {
h('div.hide-token-confirmation__container', {
}, [
h('div.hide-token-confirmation__title', {}, [
- 'Hide Token?',
+ t('hideTokenPrompt'),
]),
h(Identicon, {
@@ -54,19 +55,19 @@ HideTokenConfirmationModal.prototype.render = function () {
h('div.hide-token-confirmation__symbol', {}, symbol),
h('div.hide-token-confirmation__copy', {}, [
- 'You can add this token back in the future by going go to “Add token” in your accounts options menu.',
+ t('readdToken'),
]),
h('div.hide-token-confirmation__buttons', {}, [
- h('button.btn-cancel.hide-token-confirmation__button', {
+ h('button.btn-cancel.hide-token-confirmation__button.allcaps', {
onClick: () => hideModal(),
}, [
- 'CANCEL',
+ t('cancel'),
]),
- h('button.btn-clear.hide-token-confirmation__button', {
+ h('button.btn-clear.hide-token-confirmation__button.allcaps', {
onClick: () => hideToken(address),
}, [
- 'HIDE',
+ t('hide'),
]),
]),
]),
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index afb2a2175..501b83430 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -6,6 +6,7 @@ const FadeModal = require('boron').FadeModal
const actions = require('../../actions')
const isMobileView = require('../../../lib/is-mobile-view')
const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-notification')
+const t = require('../../../i18n')
// Modal Components
const BuyOptions = require('./buy-options-modal')
@@ -18,6 +19,7 @@ const ShapeshiftDepositTxModal = require('./shapeshift-deposit-tx-modal.js')
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal')
const CustomizeGasModal = require('../customize-gas-modal')
const NotifcationModal = require('./notification-modal')
+const ConfirmResetAccount = require('./notification-modals/confirm-reset-account')
const accountModalStyle = {
mobileModalStyle: {
@@ -78,6 +80,7 @@ const MODALS = {
contents: [
h(DepositEtherModal, {}, []),
],
+ onHide: (props) => props.hideWarning(),
mobileModalStyle: {
width: '100%',
height: '100%',
@@ -90,18 +93,20 @@ const MODALS = {
display: 'flex',
},
laptopModalStyle: {
- width: '900px',
- maxWidth: '900px',
+ width: '850px',
top: 'calc(10% + 10px)',
left: '0',
right: '0',
margin: '0 auto',
boxShadow: '0 0 6px 0 rgba(0,0,0,0.3)',
- borderRadius: '8px',
+ borderRadius: '7px',
transform: 'none',
+ height: 'calc(80% - 20px)',
+ overflowY: 'hidden',
},
contentStyle: {
- borderRadius: '8px',
+ borderRadius: '7px',
+ height: '100%',
},
},
@@ -169,9 +174,8 @@ const MODALS = {
BETA_UI_NOTIFICATION_MODAL: {
contents: [
h(NotifcationModal, {
- header: 'Welcome to the New UI (Beta)',
- message: `You are now using the new Metamask UI. Take a look around, try out new features like sending tokens,
- and let us know if you have any issues.`,
+ header: t('uiWelcome'),
+ message: t('uiWelcomeMessage'),
}),
],
mobileModalStyle: {
@@ -187,9 +191,8 @@ const MODALS = {
OLD_UI_NOTIFICATION_MODAL: {
contents: [
h(NotifcationModal, {
- header: 'Old UI',
- message: `You have returned to the old UI. You can switch back to the New UI through the option in the top
- right dropdown menu.`,
+ header: t('oldUI'),
+ message: t('oldUIMessage'),
}),
],
mobileModalStyle: {
@@ -202,6 +205,18 @@ const MODALS = {
},
},
+ CONFIRM_RESET_ACCOUNT: {
+ contents: h(ConfirmResetAccount),
+ mobileModalStyle: {
+ width: '95%',
+ top: isPopupOrNotification() === 'popup' ? '52vh' : '36.5vh',
+ },
+ laptopModalStyle: {
+ width: '473px',
+ top: 'calc(33% + 45px)',
+ },
+ },
+
NEW_ACCOUNT: {
contents: [
h(NewAccountModal, {}, []),
@@ -273,6 +288,10 @@ function mapDispatchToProps (dispatch) {
hideModal: () => {
dispatch(actions.hideModal())
},
+ hideWarning: () => {
+ dispatch(actions.hideWarning())
+ },
+
}
}
@@ -295,7 +314,12 @@ Modal.prototype.render = function () {
{
className: 'modal',
keyboard: false,
- onHide: () => { this.onHide() },
+ onHide: () => {
+ if (modal.onHide) {
+ modal.onHide(this.props)
+ }
+ this.onHide()
+ },
ref: (ref) => {
this.modalRef = ref
},
diff --git a/ui/app/components/modals/new-account-modal.js b/ui/app/components/modals/new-account-modal.js
index fc1fd413d..298b76af4 100644
--- a/ui/app/components/modals/new-account-modal.js
+++ b/ui/app/components/modals/new-account-modal.js
@@ -3,6 +3,7 @@ const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const { connect } = require('react-redux')
const actions = require('../../actions')
+const t = require('../../../i18n')
class NewAccountModal extends Component {
constructor (props) {
@@ -11,7 +12,7 @@ class NewAccountModal extends Component {
const newAccountNumber = numberOfExistingAccounts + 1
this.state = {
- newAccountName: `Account ${newAccountNumber}`,
+ newAccountName: `${t('account')} ${newAccountNumber}`,
}
}
@@ -22,7 +23,7 @@ class NewAccountModal extends Component {
h('div.new-account-modal-wrapper', {
}, [
h('div.new-account-modal-header', {}, [
- 'New Account',
+ t('newAccount'),
]),
h('div.modal-close-x', {
@@ -30,19 +31,19 @@ class NewAccountModal extends Component {
}),
h('div.new-account-modal-content', {}, [
- 'Account Name',
+ t('accountName'),
]),
h('div.new-account-input-wrapper', {}, [
h('input.new-account-input', {
value: this.state.newAccountName,
- placeholder: 'E.g. My new account',
+ placeholder: t('sampleAccountName'),
onChange: event => this.setState({ newAccountName: event.target.value }),
}, []),
]),
h('div.new-account-modal-content.after-input', {}, [
- 'or',
+ t('or'),
]),
h('div.new-account-modal-content.after-input.pointer', {
@@ -50,13 +51,13 @@ class NewAccountModal extends Component {
this.props.hideModal()
this.props.showImportPage()
},
- }, 'Import an account'),
+ }, t('importAnAccount')),
- h('div.new-account-modal-content.button', {}, [
+ h('div.new-account-modal-content.button.allcaps', {}, [
h('button.btn-clear', {
onClick: () => this.props.createAccount(newAccountName),
}, [
- 'SAVE',
+ t('save'),
]),
]),
]),
diff --git a/ui/app/components/modals/notification-modal.js b/ui/app/components/modals/notification-modal.js
index 239144b0c..621a974d0 100644
--- a/ui/app/components/modals/notification-modal.js
+++ b/ui/app/components/modals/notification-modal.js
@@ -9,26 +9,47 @@ class NotificationModal extends Component {
const {
header,
message,
+ showCancelButton = false,
+ showConfirmButton = false,
+ hideModal,
+ onConfirm,
} = this.props
+ const showButtons = showCancelButton || showConfirmButton
+
return h('div', [
- h('div.notification-modal-wrapper', {
+ h('div.notification-modal__wrapper', {
}, [
- h('div.notification-modal-header', {}, [
+ h('div.notification-modal__header', {}, [
header,
]),
- h('div.notification-modal-message-wrapper', {}, [
- h('div.notification-modal-message', {}, [
+ h('div.notification-modal__message-wrapper', {}, [
+ h('div.notification-modal__message', {}, [
message,
]),
]),
h('div.modal-close-x', {
- onClick: this.props.hideModal,
+ onClick: hideModal,
}),
+ showButtons && h('div.notification-modal__buttons', [
+
+ showCancelButton && h('div.btn-cancel.notification-modal__buttons__btn', {
+ onClick: hideModal,
+ }, 'Cancel'),
+
+ showConfirmButton && h('div.btn-clear.notification-modal__buttons__btn', {
+ onClick: () => {
+ onConfirm()
+ hideModal()
+ },
+ }, 'Confirm'),
+
+ ]),
+
]),
])
}
@@ -37,7 +58,10 @@ class NotificationModal extends Component {
NotificationModal.propTypes = {
hideModal: PropTypes.func,
header: PropTypes.string,
- message: PropTypes.string,
+ message: PropTypes.node,
+ showCancelButton: PropTypes.bool,
+ showConfirmButton: PropTypes.bool,
+ onConfirm: PropTypes.func,
}
const mapDispatchToProps = dispatch => {
diff --git a/ui/app/components/modals/notification-modals/confirm-reset-account.js b/ui/app/components/modals/notification-modals/confirm-reset-account.js
new file mode 100644
index 000000000..e1bc91b24
--- /dev/null
+++ b/ui/app/components/modals/notification-modals/confirm-reset-account.js
@@ -0,0 +1,46 @@
+const { Component } = require('react')
+const PropTypes = require('prop-types')
+const h = require('react-hyperscript')
+const { connect } = require('react-redux')
+const actions = require('../../../actions')
+const NotifcationModal = require('../notification-modal')
+
+class ConfirmResetAccount extends Component {
+ render () {
+ const { resetAccount } = this.props
+
+ return h(NotifcationModal, {
+ header: 'Are you sure you want to reset account?',
+ message: h('div', [
+
+ h('span', `Resetting is for developer use only. This button wipes the current account's transaction history,
+ which is used to calculate the current account nonce. `),
+
+ h('a.notification-modal__link', {
+ href: 'http://metamask.helpscoutdocs.com/article/36-resetting-an-account',
+ target: '_blank',
+ onClick (event) { global.platform.openWindow({ url: event.target.href }) },
+ }, 'Read more.'),
+
+ ]),
+ showCancelButton: true,
+ showConfirmButton: true,
+ onConfirm: resetAccount,
+
+ })
+ }
+}
+
+ConfirmResetAccount.propTypes = {
+ resetAccount: PropTypes.func,
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ resetAccount: () => {
+ dispatch(actions.resetAccount())
+ },
+ }
+}
+
+module.exports = connect(null, mapDispatchToProps)(ConfirmResetAccount)
diff --git a/ui/app/components/network-display.js b/ui/app/components/network-display.js
new file mode 100644
index 000000000..9dc31b5c7
--- /dev/null
+++ b/ui/app/components/network-display.js
@@ -0,0 +1,51 @@
+const { Component } = require('react')
+const h = require('react-hyperscript')
+const PropTypes = require('prop-types')
+const { connect } = require('react-redux')
+const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon')
+const t = require('../../i18n')
+
+const networkToColorHash = {
+ 1: '#038789',
+ 3: '#e91550',
+ 42: '#690496',
+ 4: '#ebb33f',
+}
+
+class NetworkDisplay extends Component {
+ renderNetworkIcon () {
+ const { network } = this.props
+ const networkColor = networkToColorHash[network]
+
+ return networkColor
+ ? h(NetworkDropdownIcon, { backgroundColor: networkColor })
+ : h('i.fa.fa-question-circle.fa-med', {
+ style: {
+ margin: '0 4px',
+ color: 'rgb(125, 128, 130)',
+ },
+ })
+ }
+
+ render () {
+ const { provider: { type } } = this.props
+ return h('.network-display__container', [
+ this.renderNetworkIcon(),
+ h('.network-name', t(type)),
+ ])
+ }
+}
+
+NetworkDisplay.propTypes = {
+ network: PropTypes.string,
+ provider: PropTypes.object,
+}
+
+const mapStateToProps = ({ metamask: { network, provider } }) => {
+ return {
+ network,
+ provider,
+ }
+}
+
+module.exports = connect(mapStateToProps)(NetworkDisplay)
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index 3e91fa807..f3df2242a 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const classnames = require('classnames')
const inherits = require('util').inherits
const NetworkDropdownIcon = require('./dropdowns/components/network-dropdown-icon')
+const t = require('../../i18n')
module.exports = Network
@@ -33,7 +34,7 @@ Network.prototype.render = function () {
onClick: (event) => this.props.onClick(event),
}, [
h('img', {
- title: 'Attempting to connect to blockchain.',
+ title: t('attemptingConnect'),
style: {
width: '27px',
},
@@ -41,22 +42,22 @@ Network.prototype.render = function () {
}),
])
} else if (providerName === 'mainnet') {
- hoverText = 'Main Ethereum Network'
+ hoverText = t('mainnet')
iconName = 'ethereum-network'
} else if (providerName === 'ropsten') {
- hoverText = 'Ropsten Test Network'
+ hoverText = t('ropsten')
iconName = 'ropsten-test-network'
} else if (parseInt(networkNumber) === 3) {
- hoverText = 'Ropsten Test Network'
+ hoverText = t('ropsten')
iconName = 'ropsten-test-network'
} else if (providerName === 'kovan') {
- hoverText = 'Kovan Test Network'
+ hoverText = t('kovan')
iconName = 'kovan-test-network'
} else if (providerName === 'rinkeby') {
- hoverText = 'Rinkeby Test Network'
+ hoverText = t('rinkeby')
iconName = 'rinkeby-test-network'
} else {
- hoverText = 'Unknown Private Network'
+ hoverText = t('unknownNetwork')
iconName = 'unknown-private-network'
}
@@ -84,7 +85,7 @@ Network.prototype.render = function () {
backgroundColor: '#038789', // $blue-lagoon
nonSelectBackgroundColor: '#15afb2',
}),
- h('.network-name', 'Main Network'),
+ h('.network-name', t('mainnet')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'ropsten-test-network':
@@ -93,7 +94,7 @@ Network.prototype.render = function () {
backgroundColor: '#e91550', // $crimson
nonSelectBackgroundColor: '#ec2c50',
}),
- h('.network-name', 'Ropsten Test Net'),
+ h('.network-name', t('ropsten')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'kovan-test-network':
@@ -102,7 +103,7 @@ Network.prototype.render = function () {
backgroundColor: '#690496', // $purple
nonSelectBackgroundColor: '#b039f3',
}),
- h('.network-name', 'Kovan Test Net'),
+ h('.network-name', t('kovan')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
case 'rinkeby-test-network':
@@ -111,7 +112,7 @@ Network.prototype.render = function () {
backgroundColor: '#ebb33f', // $tulip-tree
nonSelectBackgroundColor: '#ecb23e',
}),
- h('.network-name', 'Rinkeby Test Net'),
+ h('.network-name', t('rinkeby')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
default:
@@ -123,7 +124,7 @@ Network.prototype.render = function () {
},
}),
- h('.network-name', 'Private Network'),
+ h('.network-name', t('privateNetwork')),
h('i.fa.fa-chevron-down.fa-lg.network-caret'),
])
}
diff --git a/ui/app/components/notice.js b/ui/app/components/notice.js
new file mode 100644
index 000000000..8b0ce1e8b
--- /dev/null
+++ b/ui/app/components/notice.js
@@ -0,0 +1,132 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const ReactMarkdown = require('react-markdown')
+const linker = require('extension-link-enabler')
+const findDOMNode = require('react-dom').findDOMNode
+const t = require('../../i18n')
+
+module.exports = Notice
+
+inherits(Notice, Component)
+function Notice () {
+ Component.call(this)
+}
+
+Notice.prototype.render = function () {
+ const { notice, onConfirm } = this.props
+ const { title, date, body } = notice
+ const state = this.state || { disclaimerDisabled: true }
+ const disabled = state.disclaimerDisabled
+
+ return (
+ h('.flex-column.flex-center.flex-grow', {
+ style: {
+ width: '100%',
+ },
+ }, [
+ h('h3.flex-center.text-transform-uppercase.terms-header', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ width: '100%',
+ fontSize: '20px',
+ textAlign: 'center',
+ padding: 6,
+ },
+ }, [
+ title,
+ ]),
+
+ h('h5.flex-center.text-transform-uppercase.terms-header', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: 24,
+ width: '100%',
+ fontSize: '20px',
+ textAlign: 'center',
+ padding: 6,
+ },
+ }, [
+ date,
+ ]),
+
+ h('style', `
+
+ .markdown {
+ overflow-x: hidden;
+ }
+
+ .markdown h1, .markdown h2, .markdown h3 {
+ margin: 10px 0;
+ font-weight: bold;
+ }
+
+ .markdown strong {
+ font-weight: bold;
+ }
+ .markdown em {
+ font-style: italic;
+ }
+
+ .markdown p {
+ margin: 10px 0;
+ }
+
+ .markdown a {
+ color: #df6b0e;
+ }
+
+ `),
+
+ h('div.markdown', {
+ onScroll: (e) => {
+ var object = e.currentTarget
+ if (object.offsetHeight + object.scrollTop + 100 >= object.scrollHeight) {
+ this.setState({disclaimerDisabled: false})
+ }
+ },
+ style: {
+ background: 'rgb(235, 235, 235)',
+ height: '310px',
+ padding: '6px',
+ width: '90%',
+ overflowY: 'scroll',
+ scroll: 'auto',
+ },
+ }, [
+ h(ReactMarkdown, {
+ className: 'notice-box',
+ source: body,
+ skipHtml: true,
+ }),
+ ]),
+
+ h('button.primary', {
+ disabled,
+ onClick: () => {
+ this.setState({disclaimerDisabled: true}, () => onConfirm())
+ },
+ style: {
+ marginTop: '18px',
+ },
+ }, t('accept')),
+ ])
+ )
+}
+
+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) {
+ this.setState({disclaimerDisabled: false})
+ }
+}
+
+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/pages/add-token.js b/ui/app/components/pages/add-token.js
index 40cfedafd..782ce79ae 100644
--- a/ui/app/components/pages/add-token.js
+++ b/ui/app/components/pages/add-token.js
@@ -23,9 +23,9 @@ const fuse = new Fuse(contractList, {
{ name: 'symbol', weight: 0.5 },
],
})
-// const actions = require('./actions')
const actions = require('../../actions')
const ethUtil = require('ethereumjs-util')
+const t = require('../i18n')
const { tokenInfoGetter } = require('../../token-util')
const { DEFAULT_ROUTE } = require('../../routes')
@@ -53,13 +53,16 @@ function AddTokenScreen () {
isShowingConfirmation: false,
customAddress: '',
customSymbol: '',
- customDecimals: 0,
+ customDecimals: '',
searchQuery: '',
- isCollapsed: true,
selectedTokens: {},
errors: {},
+ autoFilled: false,
+ displayedTab: 'SEARCH',
}
this.tokenAddressDidChange = this.tokenAddressDidChange.bind(this)
+ this.tokenSymbolDidChange = this.tokenSymbolDidChange.bind(this)
+ this.tokenDecimalsDidChange = this.tokenDecimalsDidChange.bind(this)
this.onNext = this.onNext.bind(this)
Component.call(this)
}
@@ -69,13 +72,17 @@ AddTokenScreen.prototype.componentWillMount = function () {
}
AddTokenScreen.prototype.toggleToken = function (address, token) {
- const { selectedTokens, errors } = this.state
- const { [address]: selectedToken } = selectedTokens
+ const { selectedTokens = {}, errors } = this.state
+ const selectedTokensCopy = { ...selectedTokens }
+
+ if (address in selectedTokensCopy) {
+ delete selectedTokensCopy[address]
+ } else {
+ selectedTokensCopy[address] = token
+ }
+
this.setState({
- selectedTokens: {
- ...selectedTokens,
- [address]: selectedToken ? null : token,
- },
+ selectedTokens: selectedTokensCopy,
errors: {
...errors,
tokenSelector: null,
@@ -104,6 +111,16 @@ AddTokenScreen.prototype.tokenAddressDidChange = function (e) {
}
}
+AddTokenScreen.prototype.tokenSymbolDidChange = function (e) {
+ const customSymbol = e.target.value.trim()
+ this.setState({ customSymbol })
+}
+
+AddTokenScreen.prototype.tokenDecimalsDidChange = function (e) {
+ const customDecimals = e.target.value.trim()
+ this.setState({ customDecimals })
+}
+
AddTokenScreen.prototype.checkExistingAddresses = function (address) {
if (!address) return false
const tokensList = this.props.tokens
@@ -123,28 +140,28 @@ AddTokenScreen.prototype.validate = function () {
if (customAddress) {
const validAddress = ethUtil.isValidAddress(customAddress)
if (!validAddress) {
- errors.customAddress = 'Address is invalid. '
+ errors.customAddress = t('invalidAddress')
}
- const validDecimals = customDecimals >= 0 && customDecimals < 36
+ const validDecimals = customDecimals !== null && customDecimals >= 0 && customDecimals < 36
if (!validDecimals) {
- errors.customDecimals = 'Decimals must be at least 0, and not over 36.'
+ errors.customDecimals = t('decimalsMustZerotoTen')
}
const symbolLen = customSymbol.trim().length
const validSymbol = symbolLen > 0 && symbolLen < 10
if (!validSymbol) {
- errors.customSymbol = 'Symbol must be between 0 and 10 characters.'
+ errors.customSymbol = t('symbolBetweenZeroTen')
}
const ownAddress = identitiesList.includes(standardAddress)
if (ownAddress) {
- errors.customAddress = 'Personal address detected. Input the token contract address.'
+ errors.customAddress = t('personalAddressDetected')
}
const tokenAlreadyAdded = this.checkExistingAddresses(customAddress)
if (tokenAlreadyAdded) {
- errors.customAddress = 'Token has already been added.'
+ errors.customAddress = t('tokenAlreadyAdded')
}
} else if (
Object.entries(selectedTokens)
@@ -152,7 +169,7 @@ AddTokenScreen.prototype.validate = function () {
isEmpty && !isSelected
), true)
) {
- errors.tokenSelector = 'Must select at least 1 token.'
+ errors.tokenSelector = t('mustSelectOne')
}
return {
@@ -167,21 +184,22 @@ AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address)
this.setState({
customSymbol: symbol,
customDecimals: decimals.toString(),
+ autoFilled: true,
})
}
}
AddTokenScreen.prototype.renderCustomForm = function () {
- const { customAddress, customSymbol, customDecimals, errors } = this.state
+ const { autoFilled, customAddress, customSymbol, customDecimals, errors } = this.state
- return !this.state.isCollapsed && (
+ return (
h('div.add-token__add-custom-form', [
h('div', {
className: classnames('add-token__add-custom-field', {
'add-token__add-custom-field--error': errors.customAddress,
}),
}, [
- h('div.add-token__add-custom-label', 'Token Address'),
+ h('div.add-token__add-custom-label', t('tokenAddress')),
h('input.add-token__add-custom-input', {
type: 'text',
onChange: this.tokenAddressDidChange,
@@ -194,11 +212,12 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customSymbol,
}),
}, [
- h('div.add-token__add-custom-label', 'Token Symbol'),
+ h('div.add-token__add-custom-label', t('tokenSymbol')),
h('input.add-token__add-custom-input', {
type: 'text',
+ onChange: this.tokenSymbolDidChange,
value: customSymbol,
- disabled: true,
+ disabled: autoFilled,
}),
h('div.add-token__add-custom-error-message', errors.customSymbol),
]),
@@ -207,11 +226,12 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customDecimals,
}),
}, [
- h('div.add-token__add-custom-label', 'Decimals of Precision'),
+ h('div.add-token__add-custom-label', t('decimal')),
h('input.add-token__add-custom-input', {
type: 'number',
+ onChange: this.tokenDecimalsDidChange,
value: customDecimals,
- disabled: true,
+ disabled: autoFilled,
}),
h('div.add-token__add-custom-error-message', errors.customDecimals),
]),
@@ -227,33 +247,36 @@ AddTokenScreen.prototype.renderTokenList = function () {
})
const results = [...addressSearchResult, ...fuseSearchResult]
- return Array(6).fill(undefined)
- .map((_, i) => {
- const { logo, symbol, name, address } = results[i] || {}
- const tokenAlreadyAdded = this.checkExistingAddresses(address)
- return Boolean(logo || symbol || name) && (
- h('div.add-token__token-wrapper', {
- className: classnames({
- 'add-token__token-wrapper--selected': selectedTokens[address],
- 'add-token__token-wrapper--disabled': tokenAlreadyAdded,
- }),
- onClick: () => !tokenAlreadyAdded && this.toggleToken(address, results[i]),
- }, [
- h('div.add-token__token-icon', {
- style: {
- backgroundImage: `url(images/contract/${logo})`,
- },
- }),
- h('div.add-token__token-data', [
- h('div.add-token__token-symbol', symbol),
- h('div.add-token__token-name', name),
- ]),
- // tokenAlreadyAdded && (
- // h('div.add-token__token-message', 'Already added')
- // ),
- ])
- )
- })
+ return h('div', [
+ results.length > 0 && h('div.add-token__token-icons-title', t('popularTokens')),
+ h('div.add-token__token-icons-container', Array(6).fill(undefined)
+ .map((_, i) => {
+ const { logo, symbol, name, address } = results[i] || {}
+ const tokenAlreadyAdded = this.checkExistingAddresses(address)
+ return Boolean(logo || symbol || name) && (
+ h('div.add-token__token-wrapper', {
+ className: classnames({
+ 'add-token__token-wrapper--selected': selectedTokens[address],
+ 'add-token__token-wrapper--disabled': tokenAlreadyAdded,
+ }),
+ onClick: () => !tokenAlreadyAdded && this.toggleToken(address, results[i]),
+ }, [
+ h('div.add-token__token-icon', {
+ style: {
+ backgroundImage: logo && `url(images/contract/${logo})`,
+ },
+ }),
+ h('div.add-token__token-data', [
+ h('div.add-token__token-symbol', symbol),
+ h('div.add-token__token-name', name),
+ ]),
+ // tokenAlreadyAdded && (
+ // h('div.add-token__token-message', 'Already added')
+ // ),
+ ])
+ )
+ })),
+ ])
}
AddTokenScreen.prototype.renderConfirmation = function () {
@@ -280,11 +303,10 @@ AddTokenScreen.prototype.renderConfirmation = function () {
h('div.add-token', [
h('div.add-token__wrapper', [
h('div.add-token__title-container.add-token__confirmation-title', [
- h('div.add-token__title', 'Add Token'),
- h('div.add-token__description', 'Would you like to add these tokens?'),
+ h('div.add-token__description', t('likeToAddTokens')),
]),
h('div.add-token__content-container.add-token__confirmation-content', [
- h('div.add-token__description.add-token__confirmation-description', 'Your balances'),
+ h('div.add-token__description.add-token__confirmation-description', t('balances')),
h('div.add-token__confirmation-token-list',
Object.entries(tokens)
.map(([ address, token ]) => (
@@ -301,63 +323,97 @@ AddTokenScreen.prototype.renderConfirmation = function () {
]),
]),
h('div.add-token__buttons', [
- h('button.btn-cancel.add-token__button', {
+ h('button.btn-secondary--lg.add-token__cancel-button', {
onClick: () => this.setState({ isShowingConfirmation: false }),
- }, 'Back'),
- h('button.btn-clear.add-token__button', {
+ }, t('back')),
+ h('button.btn-primary--lg', {
onClick: () => addTokens(tokens).then(() => history.push(DEFAULT_ROUTE)),
- }, 'Add Tokens'),
+ }, t('addTokens')),
]),
])
)
}
-AddTokenScreen.prototype.render = function () {
- const { isCollapsed, errors, isShowingConfirmation } = this.state
- const { history } = this.props
+AddTokenScreen.prototype.displayTab = function (selectedTab) {
+ this.setState({ displayedTab: selectedTab })
+}
- return isShowingConfirmation
- ? this.renderConfirmation()
- : (
- h('div.add-token', [
- h('div.add-token__wrapper', [
- h('div.add-token__title-container', [
- h('div.add-token__title', 'Add Token'),
- h('div.add-token__description', 'Keep track of the tokens you’ve bought with your MetaMask account. If you bought tokens using a different account, those tokens will not appear here.'),
- h('div.add-token__description', 'Search for tokens or select from our list of popular tokens.'),
- ]),
- h('div.add-token__content-container', [
- h('div.add-token__input-container', [
- h('input.add-token__input', {
- type: 'text',
- placeholder: 'Search',
- onChange: e => this.setState({ searchQuery: e.target.value }),
- }),
- h('div.add-token__search-input-error-message', errors.tokenSelector),
- ]),
- h(
- 'div.add-token__token-icons-container',
- this.renderTokenList(),
- ),
+AddTokenScreen.prototype.renderTabs = function () {
+ const { displayedTab, errors } = this.state
+
+ return displayedTab === 'CUSTOM_TOKEN'
+ ? this.renderCustomForm()
+ : h('div', [
+ h('div.add-token__wrapper', [
+ h('div.add-token__content-container', [
+ h('div.add-token__info-box', [
+ h('div.add-token__info-box__close'),
+ h('div.add-token__info-box__title', t('whatsThis')),
+ h('div.add-token__info-box__copy', t('keepTrackTokens')),
+ h('div.add-token__info-box__copy--blue', t('learnMore')),
]),
- h('div.add-token__footers', [
- h('div.add-token__add-custom', {
- onClick: () => this.setState({ isCollapsed: !isCollapsed }),
- }, [
- 'Add custom token',
- h(`i.fa.fa-angle-${isCollapsed ? 'down' : 'up'}`),
- ]),
- this.renderCustomForm(),
+ h('div.add-token__input-container', [
+ h('input.add-token__input', {
+ type: 'text',
+ placeholder: t('searchTokens'),
+ onChange: e => this.setState({ searchQuery: e.target.value }),
+ }),
+ h('div.add-token__search-input-error-message', errors.tokenSelector),
]),
+ this.renderTokenList(),
]),
- h('div.add-token__buttons', [
- h('button.btn-cancel.add-token__button', {
- onClick: () => history.goBack(),
- }, 'Cancel'),
- h('button.btn-clear.add-token__button', {
- onClick: this.onNext,
- }, 'Next'),
+ ]),
+ ])
+}
+
+AddTokenScreen.prototype.render = function () {
+ const {
+ isShowingConfirmation,
+ displayedTab,
+ } = this.state
+ const { history } = this.props
+
+ return h('div.add-token', [
+ h('div.add-token__header', [
+ h('div.add-token__header__cancel', {
+ onClick: () => history.goBack(),
+ }, [
+ h('i.fa.fa-angle-left.fa-lg'),
+ h('span', t('cancel')),
]),
- ])
- )
+ h('div.add-token__header__title', t('addTokens')),
+ !isShowingConfirmation && h('div.add-token__header__tabs', [
+
+ h('div.add-token__header__tabs__tab', {
+ className: classnames('add-token__header__tabs__tab', {
+ 'add-token__header__tabs__selected': displayedTab === 'SEARCH',
+ 'add-token__header__tabs__unselected cursor-pointer': displayedTab !== 'SEARCH',
+ }),
+ onClick: () => this.displayTab('SEARCH'),
+ }, t('search')),
+
+ h('div.add-token__header__tabs__tab', {
+ className: classnames('add-token__header__tabs__tab', {
+ 'add-token__header__tabs__selected': displayedTab === 'CUSTOM_TOKEN',
+ 'add-token__header__tabs__unselected cursor-pointer': displayedTab !== 'CUSTOM_TOKEN',
+ }),
+ onClick: () => this.displayTab('CUSTOM_TOKEN'),
+ }, t('customToken')),
+
+ ]),
+ ]),
+//
+ isShowingConfirmation
+ ? this.renderConfirmation()
+ : this.renderTabs(),
+
+ !isShowingConfirmation && h('div.add-token__buttons', [
+ h('button.btn-secondary--lg.add-token__cancel-button', {
+ onClick: history.goBack(),
+ }, t('cancel')),
+ h('button.btn-primary--lg.add-token__confirm-button', {
+ onClick: this.onNext,
+ }, t('next')),
+ ]),
+ ])
}
diff --git a/ui/app/components/pages/create-account/import-account/index.js b/ui/app/components/pages/create-account/import-account/index.js
index 71eb9ae23..fc9031a65 100644
--- a/ui/app/components/pages/create-account/import-account/index.js
+++ b/ui/app/components/pages/create-account/import-account/index.js
@@ -2,6 +2,7 @@ const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
+const t = require('../../../i18n')
import Select from 'react-select'
// Subviews
@@ -9,8 +10,8 @@ const JsonImportView = require('./json.js')
const PrivateKeyImportView = require('./private-key.js')
const menuItems = [
- 'Private Key',
- 'JSON File',
+ t('privateKey'),
+ t('jsonFile'),
]
module.exports = connect(mapStateToProps)(AccountImportSubview)
@@ -35,9 +36,24 @@ AccountImportSubview.prototype.render = function () {
return (
h('div.new-account-import-form', [
+ h('.new-account-import-disclaimer', [
+ h('span', t('importAccountMsg')),
+ h('span', {
+ style: {
+ cursor: 'pointer',
+ textDecoration: 'underline',
+ },
+ onClick: () => {
+ global.platform.openWindow({
+ url: 'https://metamask.helpscoutdocs.com/article/17-what-are-loose-accounts',
+ })
+ },
+ }, t('here')),
+ ]),
+
h('div.new-account-import-form__select-section', [
- h('div.new-account-import-form__select-label', 'Select Type'),
+ h('div.new-account-import-form__select-label', t('selectType')),
h(Select, {
className: 'new-account-import-form__select',
@@ -70,9 +86,9 @@ AccountImportSubview.prototype.renderImportView = function () {
const current = type || menuItems[0]
switch (current) {
- case 'Private Key':
+ case t('privateKey'):
return h(PrivateKeyImportView)
- case 'JSON File':
+ case t('jsonFile'):
return h(JsonImportView)
default:
return h(JsonImportView)
diff --git a/ui/app/components/pages/create-account/import-account/json.js b/ui/app/components/pages/create-account/import-account/json.js
index 703dbc1f4..ef056b1b1 100644
--- a/ui/app/components/pages/create-account/import-account/json.js
+++ b/ui/app/components/pages/create-account/import-account/json.js
@@ -1,112 +1,137 @@
-const inherits = require('util').inherits
const Component = require('react').Component
+const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const { withRouter } = require('react-router-dom')
const { compose } = require('recompose')
const { connect } = require('react-redux')
const actions = require('../../../../actions')
const FileInput = require('react-simple-file-input').default
+const t = require('../../../i18n')
const { DEFAULT_ROUTE } = require('../../../../routes')
-
const HELP_LINK = 'https://support.metamask.io/kb/article/7-importing-accounts'
-module.exports = compose(
- withRouter,
- connect(mapStateToProps)
-)(JsonImportSubview)
+class JsonImportSubview extends Component {
+ constructor (props) {
+ super(props)
-function mapStateToProps (state) {
- return {
- error: state.appState.warning,
+ this.state = {
+ file: null,
+ fileContents: '',
+ }
}
-}
-inherits(JsonImportSubview, Component)
-function JsonImportSubview () {
- Component.call(this)
-}
+ render () {
+ const { error } = this.props
+
+ return (
+ h('div.new-account-import-form__json', [
+
+ h('p', t('usedByClients')),
+ h('a.warning', {
+ href: HELP_LINK,
+ target: '_blank',
+ }, t('fileImportFail')),
+
+ h(FileInput, {
+ readAs: 'text',
+ onLoad: this.onLoad.bind(this),
+ style: {
+ margin: '20px 0px 12px 34%',
+ fontSize: '15px',
+ display: 'flex',
+ justifyContent: 'center',
+ },
+ }),
+
+ h('input.new-account-import-form__input-password', {
+ type: 'password',
+ placeholder: t('enterPassword'),
+ id: 'json-password-box',
+ onKeyPress: this.createKeyringOnEnter.bind(this),
+ }),
+
+ h('div.new-account-create-form__buttons', {}, [
+
+ h('button.btn-secondary.new-account-create-form__button', {
+ onClick: () => this.props.history.push(DEFAULT_ROUTE),
+ }, [
+ t('cancel'),
+ ]),
+
+ h('button.btn-primary.new-account-create-form__button', {
+ onClick: () => this.createNewKeychain(),
+ }, [
+ t('import'),
+ ]),
-JsonImportSubview.prototype.render = function () {
- const { error } = this.props
-
- return (
- h('div.new-account-import-form__json', [
-
- h('p', 'Used by a variety of different clients'),
- h('a.warning', { href: HELP_LINK, target: '_blank' }, 'File import not working? Click here!'),
-
- h(FileInput, {
- readAs: 'text',
- onLoad: this.onLoad.bind(this),
- style: {
- margin: '20px 0px 12px 34%',
- fontSize: '15px',
- display: 'flex',
- justifyContent: 'center',
- },
- }),
-
- h('input.new-account-import-form__input-password', {
- type: 'password',
- placeholder: 'Enter password',
- id: 'json-password-box',
- onKeyPress: this.createKeyringOnEnter.bind(this),
- }),
-
- h('div.new-account-create-form__buttons', {}, [
-
- h('button.new-account-create-form__button-cancel', {
- onClick: () => this.props.history.push(DEFAULT_ROUTE),
- }, [
- 'CANCEL',
]),
- h('button.new-account-create-form__button-create', {
- onClick: () => this.createNewKeychain.bind(this),
- }, [
- 'IMPORT',
- ]),
+ error ? h('span.error', error) : null,
+ ])
+ )
+ }
- ]),
+ onLoad (event, file) {
+ this.setState({file: file, fileContents: event.target.result})
+ }
- error ? h('span.error', error) : null,
- ])
- )
-}
+ createKeyringOnEnter (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ this.createNewKeychain()
+ }
+ }
-JsonImportSubview.prototype.onLoad = function (event, file) {
- this.setState({file: file, fileContents: event.target.result})
-}
+ createNewKeychain () {
+ const state = this.state
-JsonImportSubview.prototype.createKeyringOnEnter = function (event) {
- if (event.key === 'Enter') {
- event.preventDefault()
- this.createNewKeychain()
- }
-}
+ if (!state) {
+ const message = t('validFileImport')
+ return this.props.displayWarning(message)
+ }
-JsonImportSubview.prototype.createNewKeychain = function () {
- const state = this.state
+ const { fileContents } = state
- if (!state) {
- const message = 'You must select a valid file to import.'
- return this.props.dispatch(actions.displayWarning(message))
- }
+ if (!fileContents) {
+ const message = t('needImportFile')
+ return this.props.displayWarning(message)
+ }
- const { fileContents } = state
+ const passwordInput = document.getElementById('json-password-box')
+ const password = passwordInput.value
- if (!fileContents) {
- const message = 'You must select a file to import.'
- return this.props.dispatch(actions.displayWarning(message))
+ if (!password) {
+ const message = t('needImportPassword')
+ return this.props.displayWarning(message)
+ }
+
+ this.props.importNewJsonAccount([ fileContents, password ])
}
+}
- const passwordInput = document.getElementById('json-password-box')
- const password = passwordInput.value
+JsonImportSubview.propTypes = {
+ error: PropTypes.string,
+ goHome: PropTypes.func,
+ displayWarning: PropTypes.func,
+ importNewJsonAccount: PropTypes.func,
+ history: PropTypes.object,
+}
- if (!password) {
- const message = 'You must enter a password for the selected file.'
- return this.props.dispatch(actions.displayWarning(message))
+const mapStateToProps = state => {
+ return {
+ error: state.appState.warning,
}
+}
- this.props.dispatch(actions.importNewAccount('JSON File', [ fileContents, password ]))
+const mapDispatchToProps = dispatch => {
+ return {
+ goHome: () => dispatch(actions.goHome()),
+ displayWarning: warning => dispatch(actions.displayWarning(warning)),
+ importNewJsonAccount: options => dispatch(actions.importNewAccount('JSON File', options)),
+ }
}
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(JsonImportSubview)
diff --git a/ui/app/components/pages/create-account/import-account/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js
index fb10bdb48..f48feeb0e 100644
--- a/ui/app/components/pages/create-account/import-account/private-key.js
+++ b/ui/app/components/pages/create-account/import-account/private-key.js
@@ -6,6 +6,7 @@ const { compose } = require('recompose')
const { connect } = require('react-redux')
const actions = require('../../../../actions')
const { DEFAULT_ROUTE } = require('../../../../routes')
+const t = require('../../../i18n')
module.exports = compose(
withRouter,
@@ -38,9 +39,9 @@ PrivateKeyImportView.prototype.render = function () {
return (
h('div.new-account-import-form__private-key', [
- h('div.new-account-import-form__private-key-password-container', [
+ h('span.new-account-create-form__instruction', t('pastePrivateKey')),
- h('span.new-account-import-form__instruction', 'Paste your private key string here:'),
+ h('div.new-account-import-form__private-key-password-container', [
h('input.new-account-import-form__input-password', {
type: 'password',
@@ -52,16 +53,16 @@ PrivateKeyImportView.prototype.render = function () {
h('div.new-account-import-form__buttons', {}, [
- h('button.new-account-create-form__button-cancel', {
+ h('button.btn-secondary--lg.new-account-create-form__button', {
onClick: () => this.props.history.push(DEFAULT_ROUTE),
}, [
- 'CANCEL',
+ t('cancel'),
]),
- h('button.new-account-create-form__button-create', {
+ h('button.btn-primary--lg.new-account-create-form__button', {
onClick: () => this.createNewKeychain(),
}, [
- 'IMPORT',
+ t('import'),
]),
]),
diff --git a/ui/app/components/pages/create-account/import-account/seed.js b/ui/app/components/pages/create-account/import-account/seed.js
index b4a7c0afa..9ffc669a2 100644
--- a/ui/app/components/pages/create-account/import-account/seed.js
+++ b/ui/app/components/pages/create-account/import-account/seed.js
@@ -2,6 +2,7 @@ const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
+const t = require('../../../i18n')
module.exports = connect(mapStateToProps)(SeedImportSubview)
@@ -20,11 +21,10 @@ SeedImportSubview.prototype.render = function () {
style: {
},
}, [
- `Paste your seed phrase here!`,
+ t('pasteSeed'),
h('textarea'),
h('br'),
- h('button', 'Submit'),
+ h('button', t('submit')),
])
)
}
-
diff --git a/ui/app/components/pages/create-account/new-account.js b/ui/app/components/pages/create-account/new-account.js
index fbd456a75..889ae9206 100644
--- a/ui/app/components/pages/create-account/new-account.js
+++ b/ui/app/components/pages/create-account/new-account.js
@@ -4,6 +4,7 @@ const h = require('react-hyperscript')
const { connect } = require('react-redux')
const actions = require('../../../actions')
const { DEFAULT_ROUTE } = require('../../../routes')
+const t = require('../../../i18n')
class NewAccountCreateForm extends Component {
constructor (props) {
@@ -14,7 +15,7 @@ class NewAccountCreateForm extends Component {
this.state = {
newAccountName: '',
- defaultAccountName: `Account ${newAccountNumber}`,
+ defaultAccountName: t('newAccountNumberName', [newAccountNumber]),
}
}
@@ -25,7 +26,7 @@ class NewAccountCreateForm extends Component {
return h('div.new-account-create-form', [
h('div.new-account-create-form__input-label', {}, [
- 'Account Name',
+ t('accountName'),
]),
h('div.new-account-create-form__input-wrapper', {}, [
@@ -38,19 +39,19 @@ class NewAccountCreateForm extends Component {
h('div.new-account-create-form__buttons', {}, [
- h('button.new-account-create-form__button-cancel', {
+ h('button.btn-secondary--lg.new-account-create-form__button', {
onClick: () => history.push(DEFAULT_ROUTE),
}, [
- 'CANCEL',
+ t('cancel'),
]),
- h('button.new-account-create-form__button-create', {
+ h('button.btn-primary--lg.new-account-create-form__button', {
onClick: () => {
createAccount(newAccountName || defaultAccountName)
.then(() => history.push(DEFAULT_ROUTE))
},
}, [
- 'CREATE',
+ t('create'),
]),
]),
diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js
index 5506df1ae..6ce0556db 100644
--- a/ui/app/components/pages/settings/settings.js
+++ b/ui/app/components/pages/settings/settings.js
@@ -12,6 +12,7 @@ const SimpleDropdown = require('../../dropdowns/simple-dropdown')
const ToggleButton = require('react-toggle-button')
const { REVEAL_SEED_ROUTE } = require('../../../routes')
const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/config').enums
+const t = require('../i18n')
const getInfuraCurrencyOptions = () => {
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
@@ -41,7 +42,7 @@ class Settings extends Component {
return h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', 'Use Blockies Identicon'),
+ h('span', t('blockiesIdenticon')),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
@@ -61,13 +62,13 @@ class Settings extends Component {
return h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', 'Current Conversion'),
+ h('span', t('currentConversion')),
h('span.settings__content-description', `Updated ${Date(conversionDate)}`),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h(SimpleDropdown, {
- placeholder: 'Select Currency',
+ placeholder: t('selectCurrency'),
options: getInfuraCurrencyOptions(),
selectedOption: currentCurrency,
onSelect: newCurrency => setCurrentCurrency(newCurrency),
@@ -84,31 +85,31 @@ class Settings extends Component {
switch (provider.type) {
case 'mainnet':
- title = 'Current Network'
- value = 'Main Ethereum Network'
+ title = t('currentNetwork')
+ value = t('mainnet')
color = '#038789'
break
case 'ropsten':
- title = 'Current Network'
- value = 'Ropsten Test Network'
+ title = t('currentNetwork')
+ value = t('ropsten')
color = '#e91550'
break
case 'kovan':
- title = 'Current Network'
- value = 'Kovan Test Network'
+ title = t('currentNetwork')
+ value = t('kovan')
color = '#690496'
break
case 'rinkeby':
- title = 'Current Network'
- value = 'Rinkeby Test Network'
+ title = t('currentNetwork')
+ value = t('rinkeby')
color = '#ebb33f'
break
default:
- title = 'Current RPC'
+ title = t('currentRpc')
value = provider.rpcTarget
}
@@ -129,12 +130,12 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
h('div.settings__content-item', [
- h('span', 'New RPC URL'),
+ h('span', t('newRPC')),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
h('input.settings__input', {
- placeholder: 'New RPC URL',
+ placeholder: t('newRPC'),
onChange: event => this.setState({ newRpc: event.target.value }),
onKeyPress: event => {
if (event.key === 'Enter') {
@@ -147,7 +148,7 @@ class Settings extends Component {
event.preventDefault()
this.validateRpc(this.state.newRpc)
},
- }, 'Save'),
+ }, t('save')),
]),
]),
])
@@ -163,9 +164,9 @@ class Settings extends Component {
const appendedRpc = `http://${newRpc}`
if (validUrl.isWebUri(appendedRpc)) {
- displayWarning('URIs require the appropriate HTTP/HTTPS prefix.')
+ displayWarning(t('uriErrorMsg'))
} else {
- displayWarning('Invalid RPC URI')
+ displayWarning(t('invalidRPC'))
}
}
}
@@ -174,19 +175,25 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
h('div.settings__content-item', [
- h('div', 'State Logs'),
+ h('div', t('stateLogs')),
h(
'div.settings__content-description',
- 'State logs contain your public account addresses and sent transactions.'
+ t('stateLogsDescription')
),
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.settings__clear-button', {
+ h('button.btn-primary--lg.settings__button', {
onClick (event) {
- exportAsFile('MetaMask State Logs', window.logState())
+ window.logStateString((err, result) => {
+ if (err) {
+ this.state.dispatch(actions.displayWarning(t('stateLogError')))
+ } else {
+ exportAsFile('MetaMask State Logs.json', result)
+ }
+ })
},
- }, 'Download State Logs'),
+ }, t('downloadStateLogs')),
]),
]),
])
@@ -198,12 +205,12 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
- h('div.settings__content-item', 'Reveal Seed Words'),
+ h('div.settings__content-item', t('revealSeedWords')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.settings__clear-button.settings__clear-button--red', {
+ h('button.btn-primary--lg.settings__button--red', {
onClick: () => history.push(REVEAL_SEED_ROUTE),
- }, 'Reveal Seed Words'),
+ }, t('revealSeedWords')),
]),
]),
])
@@ -215,15 +222,15 @@ class Settings extends Component {
return (
h('div.settings__content-row', [
- h('div.settings__content-item', 'Use old UI'),
+ h('div.settings__content-item', t('useOldUI')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.settings__clear-button.settings__clear-button--orange', {
+ h('button.btn-primary--lg.settings__button--orange', {
onClick (event) {
event.preventDefault()
setFeatureFlagToBeta()
},
- }, 'Use old UI'),
+ }, t('useOldUI')),
]),
]),
])
@@ -242,6 +249,7 @@ class Settings extends Component {
this.renderStateLogs(),
this.renderSeedWords(),
!isMascara && this.renderOldUI(),
+ this.renderResetAccount(),
this.renderBlockieOptIn(),
])
)
@@ -256,6 +264,7 @@ Settings.propTypes = {
displayWarning: PropTypes.func,
revealSeedConfirmation: PropTypes.func,
setFeatureFlagToBeta: PropTypes.func,
+ showResetAccountConfirmationModal: PropTypes.func,
warning: PropTypes.string,
history: PropTypes.object,
isMascara: PropTypes.bool,
@@ -280,6 +289,9 @@ const mapDispatchToProps = dispatch => {
return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
.then(() => dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
},
+ showResetAccountConfirmationModal: () => {
+ return dispatch(actions.showModal({ name: 'CONFIRM_RESET_ACCOUNT' }))
+ },
}
}
diff --git a/ui/app/components/pages/signature-request.js b/ui/app/components/pages/signature-request.js
index 0c9f4a091..2e7f3ea20 100644
--- a/ui/app/components/pages/signature-request.js
+++ b/ui/app/components/pages/signature-request.js
@@ -8,9 +8,8 @@ const classnames = require('classnames')
const AccountDropdownMini = require('../dropdowns/account-dropdown-mini')
-const actions = require('../../actions')
+const t = require('../../i18n')
const { conversionUtil } = require('../../conversion-util')
-const txHelper = require('../../../lib/tx-helper')
const { DEFAULT_ROUTE } = require('../../routes')
const {
@@ -48,7 +47,7 @@ class SignatureRequest extends Component {
h('div.request-signature__header-background'),
- h('div.request-signature__header__text', 'Signature Request'),
+ h('div.request-signature__header__text', t('sigRequest')),
h('div.request-signature__header__tip-container', [
h('div.request-signature__header__tip'),
@@ -67,7 +66,7 @@ class SignatureRequest extends Component {
return h('div.request-signature__account', [
- h('div.request-signature__account-text', ['Account:']),
+ h('div.request-signature__account-text', [t('account') + ':']),
h(AccountDropdownMini, {
selectedAccount,
@@ -94,7 +93,7 @@ class SignatureRequest extends Component {
return h('div.request-signature__balance', [
- h('div.request-signature__balance-text', ['Balance:']),
+ h('div.request-signature__balance-text', [t('balance')]),
h('div.request-signature__balance-value', `${balanceInEther} ETH`),
@@ -128,7 +127,7 @@ class SignatureRequest extends Component {
return h('div.request-signature__request-info', [
h('div.request-signature__headline', [
- `Your signature is being requested`,
+ t('yourSigRequested'),
]),
])
@@ -145,23 +144,19 @@ class SignatureRequest extends Component {
}
renderBody () {
- let rows = []
- let notice = 'You are signing:'
+ let rows
+ let notice = t('youSign') + ':'
- const { txData = {} } = this.props
- const { type, msgParams = {} } = txData
- const { data } = msgParams
+ const { txData } = this.props
+ const { type, msgParams: { data } } = txData
if (type === 'personal_sign') {
- rows = [{ name: 'Message', value: this.msgHexToText(data) }]
+ rows = [{ name: t('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. `
+ rows = [{ name: t('message'), value: data }]
+ notice = t('signNotice')
}
return h('div.request-signature__body', {}, [
@@ -227,16 +222,16 @@ class SignatureRequest extends Component {
}
return h('div.request-signature__footer', [
- h('button.request-signature__footer__cancel-button', {
+ h('button.btn-secondary--lg.request-signature__footer__cancel-button', {
onClick: () => {
cancel().then(() => history.push(DEFAULT_ROUTE))
},
- }, 'CANCEL'),
- h('button.request-signature__footer__sign-button', {
+ }, t('cancel')),
+ h('button.btn-primary--lg', {
onClick: () => {
sign().then(() => history.push(DEFAULT_ROUTE))
},
- }, 'SIGN'),
+ }, t('sign')),
])
}
@@ -275,47 +270,15 @@ SignatureRequest.propTypes = {
}
const mapStateToProps = state => {
- const { metamask } = state
- const {
- unapprovedTxs,
- unapprovedMsgs,
- unapprovedPersonalMsgs,
- unapprovedTypedMessages,
- network,
- unapprovedMsgCount,
- unapprovedPersonalMsgCount,
- unapprovedTypedMessagesCount,
- } = metamask
- const unconfTxList = txHelper(
- unapprovedTxs,
- unapprovedMsgs,
- unapprovedPersonalMsgs,
- unapprovedTypedMessages,
- network
- ) || []
-
return {
balance: getSelectedAccount(state).balance,
selectedAccount: getCurrentAccountWithSendEtherInfo(state),
selectedAddress: getSelectedAddress(state),
+ requester: null,
+ requesterAddress: null,
accounts: accountsWithSendEtherInfoSelector(state),
conversionRate: conversionRateSelector(state),
- unapprovedMsgCount,
- unapprovedPersonalMsgCount,
- unapprovedTypedMessagesCount,
- txData: unconfTxList[0] || {},
- }
-}
-
-const mapDispatchToProps = dispatch => {
- return {
- signPersonalMessage: params => dispatch(actions.signPersonalMsg(params)),
- cancelPersonalMessage: params => dispatch(actions.cancelPersonalMsg(params)),
- signTypedMessage: params => dispatch(actions.signTypedMsg(params)),
- cancelTypedMessage: params => dispatch(actions.cancelTypedMsg(params)),
- signMessage: params => dispatch(actions.signMsg(params)),
- cancelMessage: params => dispatch(actions.cancelMsg(params)),
}
}
-module.exports = connect(mapStateToProps, mapDispatchToProps)(SignatureRequest)
+module.exports = connect(mapStateToProps)(SignatureRequest)
diff --git a/ui/app/components/pending-msg-details.js b/ui/app/components/pending-msg-details.js
index 718a22de0..87e66855d 100644
--- a/ui/app/components/pending-msg-details.js
+++ b/ui/app/components/pending-msg-details.js
@@ -1,6 +1,7 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
+const t = require('../../i18n')
const AccountPanel = require('./account-panel')
@@ -39,7 +40,7 @@ PendingMsgDetails.prototype.render = function () {
// message data
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-column.flex-space-between', [
- h('label.font-small', 'MESSAGE'),
+ h('label.font-small.allcaps', t('message')),
h('span.font-small', msgParams.data),
]),
]),
@@ -47,4 +48,3 @@ PendingMsgDetails.prototype.render = function () {
])
)
}
-
diff --git a/ui/app/components/pending-msg.js b/ui/app/components/pending-msg.js
index 834719c53..dc406b955 100644
--- a/ui/app/components/pending-msg.js
+++ b/ui/app/components/pending-msg.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const PendingTxDetails = require('./pending-msg-details')
+const t = require('../../i18n')
module.exports = PendingMsg
@@ -29,17 +30,14 @@ PendingMsg.prototype.render = function () {
fontWeight: 'bold',
textAlign: 'center',
},
- }, 'Sign Message'),
+ }, t('signMessage')),
h('.error', {
style: {
margin: '10px',
},
}, [
- `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. `,
+ t('signNotice'),
h('a', {
href: 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527',
style: { color: 'rgb(247, 134, 28)' },
@@ -48,7 +46,7 @@ PendingMsg.prototype.render = function () {
const url = 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527'
global.platform.openWindow({ url })
},
- }, 'Read more here.'),
+ }, t('readMore')),
]),
// message details
@@ -58,13 +56,12 @@ PendingMsg.prototype.render = function () {
h('.flex-row.flex-space-around', [
h('button', {
onClick: state.cancelMessage,
- }, 'Cancel'),
+ }, t('cancel')),
h('button', {
onClick: state.signMessage,
- }, 'Sign'),
+ }, t('sign')),
]),
])
)
}
-
diff --git a/ui/app/components/pending-personal-msg-details.js b/ui/app/components/pending-personal-msg-details.js
deleted file mode 100644
index 1050513f2..000000000
--- a/ui/app/components/pending-personal-msg-details.js
+++ /dev/null
@@ -1,60 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-const AccountPanel = require('./account-panel')
-const BinaryRenderer = require('./binary-renderer')
-
-module.exports = PendingMsgDetails
-
-inherits(PendingMsgDetails, Component)
-function PendingMsgDetails () {
- Component.call(this)
-}
-
-PendingMsgDetails.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- var msgParams = msgData.msgParams || {}
- var address = msgParams.from || state.selectedAddress
- var identity = state.identities[address] || { address: address }
- var account = state.accounts[address] || { address: address }
-
- var { data } = msgParams
-
- return (
- h('div', {
- key: msgData.id,
- style: {
- margin: '10px 20px',
- },
- }, [
-
- // account that will sign
- h(AccountPanel, {
- showFullAddress: true,
- identity: identity,
- account: account,
- imageifyIdenticons: state.imageifyIdenticons,
- }),
-
- // message data
- h('div', {
- style: {
- height: '260px',
- },
- }, [
- h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'),
- h(BinaryRenderer, {
- value: data,
- style: {
- height: '215px',
- },
- }),
- ]),
-
- ])
- )
-}
-
diff --git a/ui/app/components/pending-tx/confirm-deploy-contract.js b/ui/app/components/pending-tx/confirm-deploy-contract.js
index ae6c6ef7b..b75f3a964 100644
--- a/ui/app/components/pending-tx/confirm-deploy-contract.js
+++ b/ui/app/components/pending-tx/confirm-deploy-contract.js
@@ -1,348 +1,354 @@
-const Component = require('react').Component
+const { Component } = require('react')
const { connect } = require('react-redux')
const h = require('react-hyperscript')
-const inherits = require('util').inherits
+const PropTypes = require('prop-types')
const actions = require('../../actions')
const clone = require('clone')
-const Identicon = require('../identicon')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
const { conversionUtil } = require('../../conversion-util')
+const t = require('../../../i18n')
+const SenderToRecipient = require('../sender-to-recipient')
+const NetworkDisplay = require('../network-display')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
+class ConfirmDeployContract extends Component {
+ constructor (props) {
+ super(props)
-module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract)
-
-function mapStateToProps (state) {
- const {
- conversionRate,
- identities,
- currentCurrency,
- } = state.metamask
- const accounts = state.metamask.accounts
- const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
- return {
- currentCurrency,
- conversionRate,
- identities,
- selectedAddress,
+ this.state = {
+ valid: false,
+ submitting: false,
+ }
}
-}
-function mapDispatchToProps (dispatch) {
- return {
- backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
- cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
+ onSubmit (event) {
+ event.preventDefault()
+ const txMeta = this.gatherTxMeta()
+ const valid = this.checkValidity()
+ this.setState({ valid, submitting: true })
+
+ if (valid && this.verifyGasParams()) {
+ this.props.sendTransaction(txMeta, event)
+ } else {
+ this.props.displayWarning('invalidGasParams')
+ this.setState({ submitting: false })
+ }
}
-}
-
-inherits(ConfirmDeployContract, Component)
-function ConfirmDeployContract () {
- Component.call(this)
- this.state = {}
- this.onSubmit = this.onSubmit.bind(this)
-}
+ cancel (event, txMeta) {
+ event.preventDefault()
+ this.props.cancelTransaction(txMeta)
+ }
-ConfirmDeployContract.prototype.onSubmit = function (event) {
- event.preventDefault()
- const txMeta = this.gatherTxMeta()
- const valid = this.checkValidity()
- this.setState({ valid, submitting: true })
-
- if (valid && this.verifyGasParams()) {
- this.props.sendTransaction(txMeta, event)
- } else {
- this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
- this.setState({ submitting: false })
+ checkValidity () {
+ const form = this.getFormEl()
+ const valid = form.checkValidity()
+ return valid
}
-}
-ConfirmDeployContract.prototype.cancel = function (event, txMeta) {
- event.preventDefault()
- this.props.cancelTransaction(txMeta)
-}
+ getFormEl () {
+ const form = document.querySelector('form#pending-tx-form')
+ // Stub out form for unit tests:
+ if (!form) {
+ return { checkValidity () { return true } }
+ }
+ return form
+ }
-ConfirmDeployContract.prototype.checkValidity = function () {
- const form = this.getFormEl()
- const valid = form.checkValidity()
- return valid
-}
+ // After a customizable state value has been updated,
+ gatherTxMeta () {
+ const props = this.props
+ const state = this.state
+ const txData = clone(state.txData) || clone(props.txData)
-ConfirmDeployContract.prototype.getFormEl = function () {
- const form = document.querySelector('form#pending-tx-form')
- // Stub out form for unit tests:
- if (!form) {
- return { checkValidity () { return true } }
+ // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
+ return txData
}
- return form
-}
-
-// After a customizable state value has been updated,
-ConfirmDeployContract.prototype.gatherTxMeta = function () {
- const props = this.props
- const state = this.state
- const txData = clone(state.txData) || clone(props.txData)
- // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
- return txData
-}
+ verifyGasParams () {
+ // We call this in case the gas has not been modified at all
+ if (!this.state) { return true }
+ return (
+ this._notZeroOrEmptyString(this.state.gas) &&
+ this._notZeroOrEmptyString(this.state.gasPrice)
+ )
+ }
-ConfirmDeployContract.prototype.verifyGasParams = function () {
- // We call this in case the gas has not been modified at all
- if (!this.state) { return true }
- return (
- this._notZeroOrEmptyString(this.state.gas) &&
- this._notZeroOrEmptyString(this.state.gasPrice)
- )
-}
+ _notZeroOrEmptyString (obj) {
+ return obj !== '' && obj !== '0x0'
+ }
-ConfirmDeployContract.prototype._notZeroOrEmptyString = function (obj) {
- return obj !== '' && obj !== '0x0'
-}
+ bnMultiplyByFraction (targetBN, numerator, denominator) {
+ const numBN = new BN(numerator)
+ const denomBN = new BN(denominator)
+ return targetBN.mul(numBN).div(denomBN)
+ }
-ConfirmDeployContract.prototype.bnMultiplyByFraction = function (targetBN, numerator, denominator) {
- const numBN = new BN(numerator)
- const denomBN = new BN(denominator)
- return targetBN.mul(numBN).div(denomBN)
-}
+ getData () {
+ const { identities } = this.props
+ const txMeta = this.gatherTxMeta()
+ const txParams = txMeta.txParams || {}
+
+ return {
+ from: {
+ address: txParams.from,
+ name: identities[txParams.from].name,
+ },
+ memo: txParams.memo || '',
+ }
+ }
-ConfirmDeployContract.prototype.getData = function () {
- const { identities } = this.props
- const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
+ getAmount () {
+ const { conversionRate, currentCurrency } = this.props
+ const txMeta = this.gatherTxMeta()
+ const txParams = txMeta.txParams || {}
+
+ const FIAT = conversionUtil(txParams.value, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ fromCurrency: 'ETH',
+ toCurrency: currentCurrency,
+ numberOfDecimals: 2,
+ fromDenomination: 'WEI',
+ conversionRate,
+ })
+ const ETH = conversionUtil(txParams.value, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ fromCurrency: 'ETH',
+ toCurrency: 'ETH',
+ fromDenomination: 'WEI',
+ conversionRate,
+ numberOfDecimals: 6,
+ })
+
+ return {
+ fiat: Number(FIAT),
+ token: Number(ETH),
+ }
- return {
- from: {
- address: txParams.from,
- name: identities[txParams.from].name,
- },
- memo: txParams.memo || '',
}
-}
-
-ConfirmDeployContract.prototype.getAmount = function () {
- const { conversionRate, currentCurrency } = this.props
- const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
-
- const FIAT = conversionUtil(txParams.value, {
- fromNumericBase: 'hex',
- toNumericBase: 'dec',
- fromCurrency: 'ETH',
- toCurrency: currentCurrency,
- numberOfDecimals: 2,
- fromDenomination: 'WEI',
- conversionRate,
- })
- const ETH = conversionUtil(txParams.value, {
- fromNumericBase: 'hex',
- toNumericBase: 'dec',
- fromCurrency: 'ETH',
- toCurrency: 'ETH',
- fromDenomination: 'WEI',
- conversionRate,
- numberOfDecimals: 6,
- })
- return {
- fiat: Number(FIAT),
- token: Number(ETH),
+ getGasFee () {
+ const { conversionRate, currentCurrency } = this.props
+ const txMeta = this.gatherTxMeta()
+ const txParams = txMeta.txParams || {}
+
+ // Gas
+ const gas = txParams.gas
+ const gasBn = hexToBn(gas)
+
+ // Gas Price
+ const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX
+ const gasPriceBn = hexToBn(gasPrice)
+
+ const txFeeBn = gasBn.mul(gasPriceBn)
+
+ const FIAT = conversionUtil(txFeeBn, {
+ fromNumericBase: 'BN',
+ toNumericBase: 'dec',
+ fromDenomination: 'WEI',
+ fromCurrency: 'ETH',
+ toCurrency: currentCurrency,
+ numberOfDecimals: 2,
+ conversionRate,
+ })
+ const ETH = conversionUtil(txFeeBn, {
+ fromNumericBase: 'BN',
+ toNumericBase: 'dec',
+ fromDenomination: 'WEI',
+ fromCurrency: 'ETH',
+ toCurrency: 'ETH',
+ numberOfDecimals: 6,
+ conversionRate,
+ })
+
+ return {
+ fiat: Number(FIAT),
+ eth: Number(ETH),
+ }
}
-}
+ renderGasFee () {
+ const { currentCurrency } = this.props
+ const { fiat: fiatGas, eth: ethGas } = this.getGasFee()
-ConfirmDeployContract.prototype.getGasFee = function () {
- const { conversionRate, currentCurrency } = this.props
- const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
+ return (
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency.toUpperCase()}`),
- // Gas
- const gas = txParams.gas
- const gasBn = hexToBn(gas)
+ h(
+ 'div.confirm-screen-row-detail',
+ `${ethGas} ETH`
+ ),
+ ]),
+ ])
+ )
+ }
- // Gas Price
- const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_HEX
- const gasPriceBn = hexToBn(gasPrice)
+ renderHeroAmount () {
+ const { currentCurrency } = this.props
+ const { fiat: fiatAmount } = this.getAmount()
+ const txMeta = this.gatherTxMeta()
+ const txParams = txMeta.txParams || {}
+ const { memo = '' } = txParams
+
+ return (
+ h('div.confirm-send-token__hero-amount-wrapper', [
+ h('h3.flex-center.confirm-screen-send-amount', `${fiatAmount}`),
+ h('h3.flex-center.confirm-screen-send-amount-currency', currentCurrency.toUpperCase()),
+ h('div.flex-center.confirm-memo-wrapper', [
+ h('h3.confirm-screen-send-memo', memo),
+ ]),
+ ])
+ )
+ }
- const txFeeBn = gasBn.mul(gasPriceBn)
+ renderTotalPlusGas () {
+ const { currentCurrency } = this.props
+ const { fiat: fiatAmount, token: tokenAmount } = this.getAmount()
+ const { fiat: fiatGas, eth: ethGas } = this.getGasFee()
- const FIAT = conversionUtil(txFeeBn, {
- fromNumericBase: 'BN',
- toNumericBase: 'dec',
- fromDenomination: 'WEI',
- fromCurrency: 'ETH',
- toCurrency: currentCurrency,
- numberOfDecimals: 2,
- conversionRate,
- })
- const ETH = conversionUtil(txFeeBn, {
- fromNumericBase: 'BN',
- toNumericBase: 'dec',
- fromDenomination: 'WEI',
- fromCurrency: 'ETH',
- toCurrency: 'ETH',
- numberOfDecimals: 6,
- conversionRate,
- })
+ return (
+ h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
+ h('div.confirm-screen-section-column', [
+ h('span.confirm-screen-label', [ t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
+ ]),
- return {
- fiat: Number(FIAT),
- eth: Number(ETH),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', `${fiatAmount + fiatGas} ${currentCurrency.toUpperCase()}`),
+ h('div.confirm-screen-row-detail', `${tokenAmount + ethGas} ETH`),
+ ]),
+ ])
+ )
}
-}
-ConfirmDeployContract.prototype.renderGasFee = function () {
- const { currentCurrency } = this.props
- const { fiat: fiatGas, eth: ethGas } = this.getGasFee()
-
- return (
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency.toUpperCase()}`),
-
- h(
- 'div.confirm-screen-row-detail',
- `${ethGas} ETH`
- ),
- ]),
- ])
- )
-}
+ render () {
+ const { backToAccountDetail, selectedAddress } = this.props
+ const txMeta = this.gatherTxMeta()
+
+ const {
+ from: {
+ address: fromAddress,
+ name: fromName,
+ },
+ } = this.getData()
+
+ this.inputs = []
+
+ return (
+ h('.page-container', [
+ h('.page-container__header', [
+ h('.page-container__header-row', [
+ h('span.page-container__back-button', {
+ onClick: () => backToAccountDetail(selectedAddress),
+ }, t('back')),
+ window.METAMASK_UI_TYPE === 'notification' && h(NetworkDisplay),
+ ]),
+ h('.page-container__title', t('confirmContract')),
+ h('.page-container__subtitle', t('pleaseReviewTransaction')),
+ ]),
+ // Main Send token Card
+ h('.page-container__content', [
+
+ h(SenderToRecipient, {
+ senderName: fromName,
+ senderAddress: fromAddress,
+ }),
+
+ // h('h3.flex-center.confirm-screen-sending-to-message', {
+ // style: {
+ // textAlign: 'center',
+ // fontSize: '16px',
+ // },
+ // }, [
+ // `You're deploying a new contract.`,
+ // ]),
+
+ this.renderHeroAmount(),
+
+ h('div.confirm-screen-rows', [
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', fromName),
+ h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ ]),
+ ]),
-ConfirmDeployContract.prototype.renderHeroAmount = function () {
- const { currentCurrency } = this.props
- const { fiat: fiatAmount } = this.getAmount()
- const txMeta = this.gatherTxMeta()
- const txParams = txMeta.txParams || {}
- const { memo = '' } = txParams
-
- return (
- h('div.confirm-send-token__hero-amount-wrapper', [
- h('h3.flex-center.confirm-screen-send-amount', `${fiatAmount}`),
- h('h3.flex-center.confirm-screen-send-amount-currency', currentCurrency.toUpperCase()),
- h('div.flex-center.confirm-memo-wrapper', [
- h('h3.confirm-screen-send-memo', memo),
- ]),
- ])
- )
-}
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', t('newContract')),
+ ]),
+ ]),
-ConfirmDeployContract.prototype.renderTotalPlusGas = function () {
- const { currentCurrency } = this.props
- const { fiat: fiatAmount, token: tokenAmount } = this.getAmount()
- const { fiat: fiatGas, eth: ethGas } = this.getGasFee()
-
- return (
- h('section.flex-row.flex-center.confirm-screen-total-box ', [
- h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ 'Total ' ]),
- h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]),
- ]),
-
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${fiatAmount + fiatGas} ${currentCurrency.toUpperCase()}`),
- h('div.confirm-screen-row-detail', `${tokenAmount + ethGas} ETH`),
- ]),
- ])
- )
-}
+ this.renderGasFee(),
-ConfirmDeployContract.prototype.render = function () {
- const { backToAccountDetail, selectedAddress } = this.props
- const txMeta = this.gatherTxMeta()
+ this.renderTotalPlusGas(),
- const {
- from: {
- address: fromAddress,
- name: fromName,
- },
- } = this.getData()
-
- this.inputs = []
-
- return (
- h('div.flex-column.flex-grow.confirm-screen-container', {
- style: { minWidth: '355px' },
- }, [
- // Main Send token Card
- h('div.confirm-screen-wrapper.flex-column.flex-grow', [
- h('h3.flex-center.confirm-screen-header', [
- h('button.confirm-screen-back-button', {
- onClick: () => backToAccountDetail(selectedAddress),
- }, 'BACK'),
- h('div.confirm-screen-title', 'Confirm Contract'),
- h('div.confirm-screen-header-tip'),
- ]),
- h('div.flex-row.flex-center.confirm-screen-identicons', [
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: fromAddress,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', fromName),
- // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
- ]),
- h('i.fa.fa-arrow-right.fa-lg'),
- h('div.confirm-screen-account-wrapper', [
- h('i.fa.fa-file-text-o'),
- h('span.confirm-screen-account-name', 'New Contract'),
- h('span.confirm-screen-account-number', ' '),
]),
]),
- // h('h3.flex-center.confirm-screen-sending-to-message', {
- // style: {
- // textAlign: 'center',
- // fontSize: '16px',
- // },
- // }, [
- // `You're deploying a new contract.`,
- // ]),
-
- this.renderHeroAmount(),
-
- h('div.confirm-screen-rows', [
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', fromName),
- h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
- ]),
- ]),
-
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', 'New Contract'),
- ]),
+ h('form#pending-tx-form', {
+ onSubmit: event => this.onSubmit(event),
+ }, [
+ h('.page-container__footer', [
+ // Cancel Button
+ h('button.btn-cancel.page-container__footer-button.allcaps', {
+ onClick: event => this.cancel(event, txMeta),
+ }, t('cancel')),
+
+ // Accept Button
+ h('button.btn-confirm.page-container__footer-button.allcaps', {
+ onClick: event => this.onSubmit(event),
+ }, t('confirm')),
]),
+ ]),
+ ])
+ )
+ }
+}
- this.renderGasFee(),
+ConfirmDeployContract.propTypes = {
+ sendTransaction: PropTypes.func,
+ cancelTransaction: PropTypes.func,
+ backToAccountDetail: PropTypes.func,
+ displayWarning: PropTypes.func,
+ identities: PropTypes.object,
+ conversionRate: PropTypes.number,
+ currentCurrency: PropTypes.string,
+ selectedAddress: PropTypes.string,
+}
- this.renderTotalPlusGas(),
+const mapStateToProps = state => {
+ const {
+ conversionRate,
+ identities,
+ currentCurrency,
+ } = state.metamask
+ const accounts = state.metamask.accounts
+ const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
+ return {
+ currentCurrency,
+ conversionRate,
+ identities,
+ selectedAddress,
+ }
+}
- ]),
- ]),
-
- h('form#pending-tx-form', {
- onSubmit: this.onSubmit,
- }, [
- // Cancel Button
- h('div.cancel.btn-light.confirm-screen-cancel-button', {
- onClick: (event) => this.cancel(event, txMeta),
- }, 'CANCEL'),
-
- // Accept Button
- h('button.confirm-screen-confirm-button', ['CONFIRM']),
-
- ]),
- ])
- )
+const mapDispatchToProps = dispatch => {
+ return {
+ backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
+ cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
+ displayWarning: warning => actions.displayWarning(t(warning)),
+ }
}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmDeployContract)
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index a2d6adcd0..f71b089ec 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -6,11 +6,18 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const actions = require('../../actions')
const clone = require('clone')
-const Identicon = require('../identicon')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
-const { conversionUtil, addCurrencies } = require('../../conversion-util')
+const {
+ conversionUtil,
+ addCurrencies,
+ multiplyCurrencies,
+} = require('../../conversion-util')
+const GasFeeDisplay = require('../send/gas-fee-display-v2')
+const t = require('../../../i18n')
+const SenderToRecipient = require('../sender-to-recipient')
+const NetworkDisplay = require('../network-display')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
@@ -61,6 +68,29 @@ function mapDispatchToProps (dispatch) {
}))
},
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
+ showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => {
+ const { id, txParams, lastGasPrice } = txMeta
+ const { gas: txGasLimit, gasPrice: txGasPrice } = txParams
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ fromDenomination: 'WEI',
+ }))
+ }
+
+ dispatch(actions.updateSend({
+ gasLimit: sendGasLimit || txGasLimit,
+ gasPrice: sendGasPrice || txGasPrice,
+ editingTransactionId: id,
+ gasTotal: sendGasTotal,
+ forceGasMin,
+ }))
+ dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' }))
+ },
}
}
@@ -145,6 +175,7 @@ ConfirmSendEther.prototype.getGasFee = function () {
return {
FIAT,
ETH,
+ gasFeeInHex: txFeeBn.toString(16),
}
}
@@ -152,7 +183,7 @@ ConfirmSendEther.prototype.getData = function () {
const { identities } = this.props
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
- const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH } = this.getGasFee()
+ const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH, gasFeeInHex } = this.getGasFee()
const { FIAT: amountInFIAT, ETH: amountInETH } = this.getAmount()
const totalInFIAT = addCurrencies(gasFeeInFIAT, amountInFIAT, {
@@ -171,7 +202,7 @@ ConfirmSendEther.prototype.getData = function () {
},
to: {
address: txParams.to,
- name: identities[txParams.to] ? identities[txParams.to].name : 'New Recipient',
+ name: identities[txParams.to] ? identities[txParams.to].name : t('newRecipient'),
},
memo: txParams.memo || '',
gasFeeInFIAT,
@@ -180,6 +211,7 @@ ConfirmSendEther.prototype.getData = function () {
amountInETH,
totalInFIAT,
totalInETH,
+ gasFeeInHex,
}
}
@@ -190,7 +222,15 @@ ConfirmSendEther.prototype.editTransaction = function (txMeta) {
}
ConfirmSendEther.prototype.render = function () {
- const { currentCurrency, clearSend } = this.props
+ const {
+ editTransaction,
+ currentCurrency,
+ clearSend,
+ conversionRate,
+ currentCurrency: convertedCurrency,
+ showCustomizeGasModal,
+ send: { gasTotal, gasLimit: sendGasLimit, gasPrice: sendGasPrice },
+ } = this.props
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
@@ -204,13 +244,17 @@ ConfirmSendEther.prototype.render = function () {
name: toName,
},
memo,
- gasFeeInFIAT,
- gasFeeInETH,
+ gasFeeInHex,
amountInFIAT,
totalInFIAT,
totalInETH,
} = this.getData()
+ const title = txMeta.lastGasPrice ? 'Reprice Transaction' : 'Confirm'
+ const subtitle = txMeta.lastGasPrice
+ ? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
+ : 'Please review your transaction.'
+
// This is from the latest master
// It handles some of the errors that we are not currently handling
// Leaving as comments fo reference
@@ -225,43 +269,28 @@ ConfirmSendEther.prototype.render = function () {
this.inputs = []
return (
- h('div.confirm-screen-container.confirm-send-ether', {
- style: { minWidth: '355px' },
- }, [
- // Main Send token Card
- h('div.confirm-screen-wrapper.flex-column.flex-grow', [
- h('h3.flex-center.confirm-screen-header', [
- h('button.btn-clear.confirm-screen-back-button', {
- onClick: () => this.editTransaction(txMeta),
- }, 'EDIT'),
- h('div.confirm-screen-title', 'Confirm Transaction'),
- h('div.confirm-screen-header-tip'),
- ]),
- h('div.flex-row.flex-center.confirm-screen-identicons', [
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: fromAddress,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', fromName),
- // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
- ]),
- h('i.fa.fa-arrow-right.fa-lg'),
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: txParams.to,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', toName),
- // h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)),
- ]),
+ // Main Send token Card
+ h('.page-container', [
+ h('.page-container__header', [
+ h('.page-container__header-row', [
+ h('span.page-container__back-button', {
+ onClick: () => editTransaction(txMeta),
+ style: {
+ visibility: !txMeta.lastGasPrice ? 'initial' : 'hidden',
+ },
+ }, 'Edit'),
+ window.METAMASK_UI_TYPE === 'notification' && h(NetworkDisplay),
]),
+ h('.page-container__title', title),
+ h('.page-container__subtitle', subtitle),
+ ]),
+ h('.page-container__content', [
+ h(SenderToRecipient, {
+ senderName: fromName,
+ senderAddress: fromAddress,
+ recipientName: toName,
+ recipientAddress: txParams.to,
+ }),
// h('h3.flex-center.confirm-screen-sending-to-message', {
// style: {
@@ -280,7 +309,7 @@ ConfirmSendEther.prototype.render = function () {
h('div.confirm-screen-rows', [
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', fromName),
h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
@@ -288,7 +317,7 @@ ConfirmSendEther.prototype.render = function () {
]),
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', toName),
h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
@@ -296,19 +325,21 @@ ConfirmSendEther.prototype.render = function () {
]),
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${gasFeeInFIAT} ${currentCurrency.toUpperCase()}`),
-
- h('div.confirm-screen-row-detail', `${gasFeeInETH} ETH`),
+ h(GasFeeDisplay, {
+ gasTotal: gasTotal || gasFeeInHex,
+ conversionRate,
+ convertedCurrency,
+ onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal),
+ }),
]),
]),
-
- h('section.flex-row.flex-center.confirm-screen-total-box ', [
+ h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ 'Total ' ]),
- h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]),
+ h('span.confirm-screen-label', [ t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
@@ -402,16 +433,18 @@ ConfirmSendEther.prototype.render = function () {
h('form#pending-tx-form', {
onSubmit: this.onSubmit,
}, [
- // Cancel Button
- h('div.cancel.btn-light.confirm-screen-cancel-button', {
- onClick: (event) => {
- clearSend()
- this.cancel(event, txMeta)
- },
- }, 'CANCEL'),
-
- // Accept Button
- h('button.confirm-screen-confirm-button', ['CONFIRM']),
+ h('.page-container__footer', [
+ // Cancel Button
+ h('button.btn-cancel.page-container__footer-button.allcaps', {
+ onClick: (event) => {
+ clearSend()
+ this.cancel(event, txMeta)
+ },
+ }, t('cancel')),
+
+ // Accept Button
+ h('button.btn-confirm.page-container__footer-button.allcaps', [t('confirm')]),
+ ]),
]),
])
)
@@ -426,7 +459,7 @@ ConfirmSendEther.prototype.onSubmit = function (event) {
if (valid && this.verifyGasParams()) {
this.props.sendTransaction(txMeta, event)
} else {
- this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
+ this.props.dispatch(actions.displayWarning(t('invalidGasParams')))
this.setState({ submitting: false })
}
}
@@ -460,6 +493,27 @@ ConfirmSendEther.prototype.gatherTxMeta = function () {
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
+ const { gasPrice: sendGasPrice, gas: sendGasLimit } = props.send
+ const {
+ lastGasPrice,
+ txParams: {
+ gasPrice: txGasPrice,
+ gas: txGasLimit,
+ },
+ } = txData
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ }))
+ }
+
+ txData.txParams.gasPrice = sendGasPrice || forceGasMin || txGasPrice
+ txData.txParams.gas = sendGasLimit || txGasLimit
+
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index f52fd01da..c8e51ccd2 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -8,8 +8,10 @@ const tokenAbi = require('human-standard-token-abi')
const abiDecoder = require('abi-decoder')
abiDecoder.addABI(tokenAbi)
const actions = require('../../actions')
+const t = require('../../../i18n')
const clone = require('clone')
const Identicon = require('../identicon')
+const GasFeeDisplay = require('../send/gas-fee-display-v2.js')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const {
@@ -69,8 +71,8 @@ function mapDispatchToProps (dispatch, ownProps) {
updateTokenExchangeRate: () => dispatch(actions.updateTokenExchangeRate(symbol)),
editTransaction: txMeta => {
const { token: { address } } = ownProps
- const { txParams, id } = txMeta
- const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data)
+ const { txParams = {}, id } = txMeta
+ const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data) || {}
const { params = [] } = tokenData
const { value: to } = params[0] || {}
const { value: tokenAmountInDec } = params[1] || {}
@@ -91,9 +93,43 @@ function mapDispatchToProps (dispatch, ownProps) {
amount: tokenAmountInHex,
errors: { to: null, amount: null },
editingTransactionId: id,
+ token: ownProps.token,
}))
dispatch(actions.showSendTokenPage())
},
+ showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => {
+ const { id, txParams, lastGasPrice } = txMeta
+ const { gas: txGasLimit, gasPrice: txGasPrice } = txParams
+ const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data)
+ const { params = [] } = tokenData
+ const { value: to } = params[0] || {}
+ const { value: tokenAmountInDec } = params[1] || {}
+ const tokenAmountInHex = conversionUtil(tokenAmountInDec, {
+ fromNumericBase: 'dec',
+ toNumericBase: 'hex',
+ })
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ fromDenomination: 'WEI',
+ }))
+ }
+
+ dispatch(actions.updateSend({
+ gasLimit: sendGasLimit || txGasLimit,
+ gasPrice: sendGasPrice || txGasPrice,
+ editingTransactionId: id,
+ gasTotal: sendGasTotal,
+ to,
+ amount: tokenAmountInHex,
+ forceGasMin,
+ }))
+ dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' }))
+ },
}
}
@@ -145,7 +181,7 @@ ConfirmSendToken.prototype.getAmount = function () {
? +(sendTokenAmount * tokenExchangeRate * conversionRate).toFixed(2)
: null,
token: typeof value === 'undefined'
- ? 'Unknown'
+ ? t('unknown')
: +sendTokenAmount.toFixed(decimals),
}
@@ -199,6 +235,7 @@ ConfirmSendToken.prototype.getGasFee = function () {
token: tokenExchangeRate
? tokenGas
: null,
+ gasFeeInHex: gasTotal.toString(16),
}
}
@@ -216,7 +253,7 @@ ConfirmSendToken.prototype.getData = function () {
},
to: {
address: value,
- name: identities[value] ? identities[value].name : 'New Recipient',
+ name: identities[value] ? identities[value].name : t('newRecipient'),
},
memo: txParams.memo || '',
}
@@ -251,19 +288,25 @@ ConfirmSendToken.prototype.renderHeroAmount = function () {
}
ConfirmSendToken.prototype.renderGasFee = function () {
- const { token: { symbol }, currentCurrency } = this.props
- const { fiat: fiatGas, token: tokenGas, eth: ethGas } = this.getGasFee()
+ const {
+ currentCurrency: convertedCurrency,
+ conversionRate,
+ send: { gasTotal, gasLimit: sendGasLimit, gasPrice: sendGasPrice },
+ showCustomizeGasModal,
+ } = this.props
+ const txMeta = this.gatherTxMeta()
+ const { gasFeeInHex } = this.getGasFee()
return (
h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'Gas Fee' ]),
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency}`),
-
- h(
- 'div.confirm-screen-row-detail',
- tokenGas ? `${tokenGas} ${symbol}` : `${ethGas} ETH`
- ),
+ h(GasFeeDisplay, {
+ gasTotal: gasTotal || gasFeeInHex,
+ conversionRate,
+ convertedCurrency,
+ onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal),
+ }),
]),
])
)
@@ -276,10 +319,10 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
return fiatAmount && fiatGas
? (
- h('section.flex-row.flex-center.confirm-screen-total-box ', [
+ h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ 'Total ' ]),
- h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]),
+ h('span.confirm-screen-label', [ t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
@@ -289,15 +332,15 @@ ConfirmSendToken.prototype.renderTotalPlusGas = function () {
])
)
: (
- h('section.flex-row.flex-center.confirm-screen-total-box ', [
+ h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
- h('span.confirm-screen-label', [ 'Total ' ]),
- h('div.confirm-screen-total-box__subtitle', [ 'Amount + Gas' ]),
+ h('span.confirm-screen-label', [ t('total') + ' ' ]),
+ h('div.confirm-screen-total-box__subtitle', [ t('amountPlusGas') ]),
]),
h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${tokenAmount} ${symbol}`),
- h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} Gas`),
+ h('div.confirm-screen-row-detail', `+ ${fiatGas} ${currentCurrency} ${t('gas')}`),
]),
])
)
@@ -319,93 +362,98 @@ ConfirmSendToken.prototype.render = function () {
this.inputs = []
+ const title = txMeta.lastGasPrice ? 'Reprice Transaction' : t('confirm')
+ const subtitle = txMeta.lastGasPrice
+ ? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
+ : t('pleaseReviewTransaction')
+
return (
- h('div.confirm-screen-container.confirm-send-token', {
- style: { minWidth: '355px' },
- }, [
+ h('div.confirm-screen-container.confirm-send-token', [
// Main Send token Card
- h('div.confirm-screen-wrapper.flex-column.flex-grow', [
- h('h3.flex-center.confirm-screen-header', [
- h('button.btn-clear.confirm-screen-back-button', {
- onClick: () => this.editTransaction(txMeta),
- }, 'EDIT'),
- h('div.confirm-screen-title', 'Confirm Transaction'),
- h('div.confirm-screen-header-tip'),
- ]),
- h('div.flex-row.flex-center.confirm-screen-identicons', [
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: fromAddress,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', fromName),
- // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
- ]),
- h('i.fa.fa-arrow-right.fa-lg'),
- h('div.confirm-screen-account-wrapper', [
- h(
- Identicon,
- {
- address: toAddress,
- diameter: 60,
- },
- ),
- h('span.confirm-screen-account-name', toName),
- // h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)),
- ]),
+ h('div.page-container', [
+ h('div.page-container__header', [
+ !txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
+ onClick: () => editTransaction(txMeta),
+ }, t('edit')),
+ h('div.page-container__title', title),
+ h('div.page-container__subtitle', subtitle),
]),
-
- // h('h3.flex-center.confirm-screen-sending-to-message', {
- // style: {
- // textAlign: 'center',
- // fontSize: '16px',
- // },
- // }, [
- // `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`,
- // ]),
-
- this.renderHeroAmount(),
-
- h('div.confirm-screen-rows', [
- h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'From' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', fromName),
- h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ h('.page-container__content', [
+ h('div.flex-row.flex-center.confirm-screen-identicons', [
+ h('div.confirm-screen-account-wrapper', [
+ h(
+ Identicon,
+ {
+ address: fromAddress,
+ diameter: 60,
+ },
+ ),
+ h('span.confirm-screen-account-name', fromName),
+ // h('span.confirm-screen-account-number', fromAddress.slice(fromAddress.length - 4)),
+ ]),
+ h('i.fa.fa-arrow-right.fa-lg'),
+ h('div.confirm-screen-account-wrapper', [
+ h(
+ Identicon,
+ {
+ address: toAddress,
+ diameter: 60,
+ },
+ ),
+ h('span.confirm-screen-account-name', toName),
+ // h('span.confirm-screen-account-number', toAddress.slice(toAddress.length - 4)),
]),
]),
- toAddress && h('section.flex-row.flex-center.confirm-screen-row', [
- h('span.confirm-screen-label.confirm-screen-section-column', [ 'To' ]),
- h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', toName),
- h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
+ // h('h3.flex-center.confirm-screen-sending-to-message', {
+ // style: {
+ // textAlign: 'center',
+ // fontSize: '16px',
+ // },
+ // }, [
+ // `You're sending to Recipient ...${toAddress.slice(toAddress.length - 4)}`,
+ // ]),
+
+ this.renderHeroAmount(),
+
+ h('div.confirm-screen-rows', [
+ h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('from') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', fromName),
+ h('div.confirm-screen-row-detail', `...${fromAddress.slice(fromAddress.length - 4)}`),
+ ]),
]),
- ]),
- this.renderGasFee(),
+ toAddress && h('section.flex-row.flex-center.confirm-screen-row', [
+ h('span.confirm-screen-label.confirm-screen-section-column', [ t('to') ]),
+ h('div.confirm-screen-section-column', [
+ h('div.confirm-screen-row-info', toName),
+ h('div.confirm-screen-row-detail', `...${toAddress.slice(toAddress.length - 4)}`),
+ ]),
+ ]),
- this.renderTotalPlusGas(),
+ this.renderGasFee(),
- ]),
- ]),
+ this.renderTotalPlusGas(),
- h('form#pending-tx-form', {
- onSubmit: this.onSubmit,
- }, [
- // Cancel Button
- h('div.cancel.btn-light.confirm-screen-cancel-button', {
- onClick: (event) => this.cancel(event, txMeta),
- }, 'CANCEL'),
+ ]),
- // Accept Button
- h('button.confirm-screen-confirm-button', ['CONFIRM']),
+ ]),
+ h('form#pending-tx-form', {
+ onSubmit: this.onSubmit,
+ }, [
+ h('.page-container__footer', [
+ // Cancel Button
+ h('button.btn-cancel.page-container__footer-button.allcaps', {
+ onClick: (event) => this.cancel(event, txMeta),
+ }, t('cancel')),
+
+ // Accept Button
+ h('button.btn-confirm.page-container__footer-button.allcaps', [t('confirm')]),
+ ]),
+ ]),
]),
-
-
])
)
}
@@ -419,7 +467,7 @@ ConfirmSendToken.prototype.onSubmit = function (event) {
if (valid && this.verifyGasParams()) {
this.props.sendTransaction(txMeta, event)
} else {
- this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
+ this.props.dispatch(actions.displayWarning(t('invalidGasParams')))
this.setState({ submitting: false })
}
}
@@ -453,6 +501,27 @@ ConfirmSendToken.prototype.gatherTxMeta = function () {
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
+ const { gasPrice: sendGasPrice, gas: sendGasLimit } = props.send
+ const {
+ lastGasPrice,
+ txParams: {
+ gasPrice: txGasPrice,
+ gas: txGasLimit,
+ },
+ } = txData
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ }))
+ }
+
+ txData.txParams.gasPrice = sendGasPrice || forceGasMin || txGasPrice
+ txData.txParams.gas = sendGasLimit || txGasLimit
+
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
diff --git a/ui/app/components/pending-tx/index.js b/ui/app/components/pending-tx/index.js
index f4f6afb8f..9c0453a3b 100644
--- a/ui/app/components/pending-tx/index.js
+++ b/ui/app/components/pending-tx/index.js
@@ -64,13 +64,20 @@ PendingTx.prototype.componentWillMount = async function () {
})
}
- try {
+ // inspect tx data for supported special confirmation screens
+ let isTokenTransaction = false
+ if (txParams.data) {
+ const tokenData = abiDecoder.decodeMethod(txParams.data)
+ const { name: tokenMethodName } = tokenData || {}
+ isTokenTransaction = (tokenMethodName === 'transfer')
+ }
+
+ if (isTokenTransaction) {
const token = util.getContractAtAddress(txParams.to)
const results = await Promise.all([
token.symbol(),
token.decimals(),
])
-
const [ symbol, decimals ] = results
if (symbol[0] && decimals[0]) {
@@ -83,11 +90,14 @@ PendingTx.prototype.componentWillMount = async function () {
})
} else {
this.setState({
- transactionType: TX_TYPES.SEND_ETHER,
+ transactionType: TX_TYPES.SEND_TOKEN,
+ tokenAddress: txParams.to,
+ tokenSymbol: null,
+ tokenDecimals: null,
isFetching: false,
})
}
- } catch (e) {
+ } else {
this.setState({
transactionType: TX_TYPES.SEND_ETHER,
isFetching: false,
diff --git a/ui/app/components/pending-typed-msg-details.js b/ui/app/components/pending-typed-msg-details.js
deleted file mode 100644
index b5fd29f71..000000000
--- a/ui/app/components/pending-typed-msg-details.js
+++ /dev/null
@@ -1,59 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-const AccountPanel = require('./account-panel')
-const TypedMessageRenderer = require('./typed-message-renderer')
-
-module.exports = PendingMsgDetails
-
-inherits(PendingMsgDetails, Component)
-function PendingMsgDetails () {
- Component.call(this)
-}
-
-PendingMsgDetails.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- var msgParams = msgData.msgParams || {}
- var address = msgParams.from || state.selectedAddress
- var identity = state.identities[address] || { address: address }
- var account = state.accounts[address] || { address: address }
-
- var { data } = msgParams
-
- return (
- h('div', {
- key: msgData.id,
- style: {
- margin: '10px 20px',
- },
- }, [
-
- // account that will sign
- h(AccountPanel, {
- showFullAddress: true,
- identity: identity,
- account: account,
- imageifyIdenticons: state.imageifyIdenticons,
- }),
-
- // message data
- h('div', {
- style: {
- height: '260px',
- },
- }, [
- h('label.font-small', { style: { display: 'block' } }, 'YOU ARE SIGNING'),
- h(TypedMessageRenderer, {
- value: data,
- style: {
- height: '215px',
- },
- }),
- ]),
-
- ])
- )
-}
diff --git a/ui/app/components/pending-typed-msg.js b/ui/app/components/pending-typed-msg.js
deleted file mode 100644
index f8926d0a3..000000000
--- a/ui/app/components/pending-typed-msg.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const PendingTxDetails = require('./pending-typed-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.cancelTypedMessage,
- }, 'Cancel'),
- h('button', {
- onClick: state.signTypedMessage,
- }, 'Sign'),
- ]),
- ])
-
- )
-}
diff --git a/ui/app/components/range-slider.js b/ui/app/components/range-slider.js
deleted file mode 100644
index 823f5eb01..000000000
--- a/ui/app/components/range-slider.js
+++ /dev/null
@@ -1,58 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = RangeSlider
-
-inherits(RangeSlider, Component)
-function RangeSlider () {
- Component.call(this)
-}
-
-RangeSlider.prototype.render = function () {
- const state = this.state || {}
- const props = this.props
- const onInput = props.onInput || function () {}
- const name = props.name
- const {
- min = 0,
- max = 100,
- increment = 1,
- defaultValue = 50,
- mirrorInput = false,
- } = this.props.options
- const {container, input, range} = props.style
-
- return (
- h('.flex-row', {
- style: container,
- }, [
- h('input', {
- type: 'range',
- name: name,
- min: min,
- max: max,
- step: increment,
- style: range,
- value: state.value || defaultValue,
- onChange: mirrorInput ? this.mirrorInputs.bind(this, event) : onInput,
- }),
-
- // Mirrored input for range
- mirrorInput ? h('input.large-input', {
- type: 'number',
- name: `${name}Mirror`,
- min: min,
- max: max,
- value: state.value || defaultValue,
- step: increment,
- style: input,
- onChange: this.mirrorInputs.bind(this, event),
- }) : null,
- ])
- )
-}
-
-RangeSlider.prototype.mirrorInputs = function (event) {
- this.setState({value: event.target.value})
-}
diff --git a/ui/app/components/send-token/index.js b/ui/app/components/send-token/index.js
deleted file mode 100644
index 99d078251..000000000
--- a/ui/app/components/send-token/index.js
+++ /dev/null
@@ -1,439 +0,0 @@
-const Component = require('react').Component
-const connect = require('react-redux').connect
-const h = require('react-hyperscript')
-const classnames = require('classnames')
-const abi = require('ethereumjs-abi')
-const inherits = require('util').inherits
-const actions = require('../../actions')
-const selectors = require('../../selectors')
-const { isValidAddress, allNull } = require('../../util')
-
-// const BalanceComponent = require('./balance-component')
-const Identicon = require('../identicon')
-const TokenBalance = require('../token-balance')
-const CurrencyToggle = require('../send/currency-toggle')
-const GasTooltip = require('../send/gas-tooltip')
-const GasFeeDisplay = require('../send/gas-fee-display')
-
-module.exports = connect(mapStateToProps, mapDispatchToProps)(SendTokenScreen)
-
-function mapStateToProps (state) {
- // const sidebarOpen = state.appState.sidebarOpen
-
- const { warning } = state.appState
- const identities = state.metamask.identities
- const addressBook = state.metamask.addressBook
- const conversionRate = state.metamask.conversionRate
- const currentBlockGasLimit = state.metamask.currentBlockGasLimit
- const accounts = state.metamask.accounts
- const selectedTokenAddress = state.metamask.selectedTokenAddress
- const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
- const selectedToken = selectors.getSelectedToken(state)
- const tokenExchangeRates = state.metamask.tokenExchangeRates
- const pair = `${selectedToken.symbol.toLowerCase()}_eth`
- const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
-
- return {
- selectedAddress,
- selectedTokenAddress,
- identities,
- addressBook,
- conversionRate,
- tokenExchangeRate,
- currentBlockGasLimit,
- selectedToken,
- warning,
- }
-}
-
-function mapDispatchToProps (dispatch) {
- return {
- backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
- hideWarning: () => dispatch(actions.hideWarning()),
- addToAddressBook: (recipient, nickname) => dispatch(
- actions.addToAddressBook(recipient, nickname)
- ),
- signTx: txParams => dispatch(actions.signTx(txParams)),
- signTokenTx: (tokenAddress, toAddress, amount, txData) => (
- dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData))
- ),
- updateTokenExchangeRate: token => dispatch(actions.updateTokenExchangeRate(token)),
- estimateGas: params => dispatch(actions.estimateGas(params)),
- getGasPrice: () => dispatch(actions.getGasPrice()),
- }
-}
-
-inherits(SendTokenScreen, Component)
-function SendTokenScreen () {
- Component.call(this)
- this.state = {
- to: '',
- amount: '0x0',
- amountToSend: '0x0',
- selectedCurrency: 'USD',
- isGasTooltipOpen: false,
- gasPrice: null,
- gasLimit: null,
- errors: {},
- }
-}
-
-SendTokenScreen.prototype.componentWillMount = function () {
- const {
- updateTokenExchangeRate,
- selectedToken: { symbol },
- getGasPrice,
- estimateGas,
- selectedAddress,
- } = this.props
-
- updateTokenExchangeRate(symbol)
-
- const data = Array.prototype.map.call(
- abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
- x => ('00' + x.toString(16)).slice(-2)
- ).join('')
-
- console.log(data)
- Promise.all([
- getGasPrice(),
- estimateGas({
- from: selectedAddress,
- value: '0x0',
- gas: '746a528800',
- data,
- }),
- ])
- .then(([blockGasPrice, estimatedGas]) => {
- console.log({ blockGasPrice, estimatedGas})
- this.setState({
- gasPrice: blockGasPrice,
- gasLimit: estimatedGas,
- })
- })
-}
-
-SendTokenScreen.prototype.validate = function () {
- const {
- to,
- amount: stringAmount,
- gasPrice: hexGasPrice,
- gasLimit: hexGasLimit,
- } = this.state
-
- const gasPrice = parseInt(hexGasPrice, 16)
- const gasLimit = parseInt(hexGasLimit, 16) / 1000000000
- const amount = Number(stringAmount)
-
- const errors = {
- to: !to ? 'Required' : null,
- amount: !amount ? 'Required' : null,
- gasPrice: !gasPrice ? 'Gas Price Required' : null,
- gasLimit: !gasLimit ? 'Gas Limit Required' : null,
- }
-
- if (to && !isValidAddress(to)) {
- errors.to = 'Invalid address'
- }
-
- const isValid = Object.entries(errors).every(([key, value]) => value === null)
- return {
- isValid,
- errors: isValid ? {} : errors,
- }
-}
-
-SendTokenScreen.prototype.setErrorsFor = function (field) {
- const { errors: previousErrors } = this.state
-
- const {
- isValid,
- errors: newErrors,
- } = this.validate()
-
- const nextErrors = Object.assign({}, previousErrors, {
- [field]: newErrors[field] || null,
- })
-
- if (!isValid) {
- this.setState({
- errors: nextErrors,
- isValid,
- })
- }
-}
-
-SendTokenScreen.prototype.clearErrorsFor = function (field) {
- const { errors: previousErrors } = this.state
- const nextErrors = Object.assign({}, previousErrors, {
- [field]: null,
- })
-
- this.setState({
- errors: nextErrors,
- isValid: allNull(nextErrors),
- })
-}
-
-SendTokenScreen.prototype.getAmountToSend = function (amount, selectedToken) {
- const { decimals } = selectedToken || {}
- const multiplier = Math.pow(10, Number(decimals || 0))
- const sendAmount = '0x' + Number(amount * multiplier).toString(16)
- return sendAmount
-}
-
-SendTokenScreen.prototype.submit = function () {
- const {
- to,
- amount,
- gasPrice,
- gasLimit,
- } = this.state
-
- const {
- identities,
- selectedAddress,
- selectedTokenAddress,
- hideWarning,
- addToAddressBook,
- signTokenTx,
- selectedToken,
- } = this.props
-
- const { nickname = ' ' } = identities[to] || {}
-
- hideWarning()
- addToAddressBook(to, nickname)
-
- const txParams = {
- from: selectedAddress,
- value: '0',
- gas: gasLimit,
- gasPrice: gasPrice,
- }
-
- const sendAmount = this.getAmountToSend(amount, selectedToken)
-
- signTokenTx(selectedTokenAddress, to, sendAmount, txParams)
-}
-
-SendTokenScreen.prototype.renderToAddressInput = function () {
- const {
- identities,
- addressBook,
- } = this.props
-
- const {
- to,
- errors: { to: errorMessage },
- } = this.state
-
- return h('div', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': errorMessage,
- }),
- }, [
- h('div', ['To:']),
- h('input.large-input.send-screen-input', {
- name: 'address',
- list: 'addresses',
- placeholder: 'Address',
- value: to,
- onChange: e => this.setState({
- to: e.target.value,
- errors: {},
- }),
- onBlur: () => {
- this.setErrorsFor('to')
- },
- onFocus: event => {
- if (to) event.target.select()
- this.clearErrorsFor('to')
- },
- }),
- h('datalist#addresses', [
- // Corresponds to the addresses owned.
- Object.entries(identities).map(([key, { address, name }]) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- addressBook.map(({ address, name }) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- ]),
- h('div.send-screen-input-wrapper__error-message', [ errorMessage ]),
- ])
-}
-
-SendTokenScreen.prototype.renderAmountInput = function () {
- const {
- selectedCurrency,
- amount,
- errors: { amount: errorMessage },
- } = this.state
-
- const {
- tokenExchangeRate,
- selectedToken: {symbol},
- } = this.props
-
- return h('div.send-screen-input-wrapper', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': errorMessage,
- }),
- }, [
- h('div.send-screen-amount-labels', [
- h('span', ['Amount']),
- h(CurrencyToggle, {
- currentCurrency: tokenExchangeRate ? selectedCurrency : 'USD',
- currencies: tokenExchangeRate ? [ symbol, 'USD' ] : [],
- onClick: currency => this.setState({ selectedCurrency: currency }),
- }),
- ]),
- h('input.large-input.send-screen-input', {
- placeholder: `0 ${symbol}`,
- type: 'number',
- value: amount,
- onChange: e => this.setState({
- amount: e.target.value,
- }),
- onBlur: () => {
- this.setErrorsFor('amount')
- },
- onFocus: () => this.clearErrorsFor('amount'),
- }),
- h('div.send-screen-input-wrapper__error-message', [ errorMessage ]),
- ])
-}
-
-SendTokenScreen.prototype.renderGasInput = function () {
- const {
- isGasTooltipOpen,
- gasPrice,
- gasLimit,
- selectedCurrency,
- errors: {
- gasPrice: gasPriceErrorMessage,
- gasLimit: gasLimitErrorMessage,
- },
- } = this.state
-
- const {
- conversionRate,
- tokenExchangeRate,
- currentBlockGasLimit,
- } = this.props
-
- return h('div.send-screen-input-wrapper', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': gasPriceErrorMessage || gasLimitErrorMessage,
- }),
- }, [
- isGasTooltipOpen && h(GasTooltip, {
- className: 'send-tooltip',
- gasPrice: gasPrice || '0x0',
- gasLimit: gasLimit || '0x0',
- onClose: () => this.setState({ isGasTooltipOpen: false }),
- onFeeChange: ({ gasLimit, gasPrice }) => {
- this.setState({ gasLimit, gasPrice, errors: {} })
- },
- onBlur: () => {
- this.setErrorsFor('gasLimit')
- this.setErrorsFor('gasPrice')
- },
- onFocus: () => {
- this.clearErrorsFor('gasLimit')
- this.clearErrorsFor('gasPrice')
- },
- }),
-
- h('div.send-screen-gas-labels', {}, [
- h('span', [ h('i.fa.fa-bolt'), 'Gas fee:']),
- h('span', ['What\'s this?']),
- ]),
- h('div.large-input.send-screen-gas-input', [
- h(GasFeeDisplay, {
- conversionRate,
- tokenExchangeRate,
- gasPrice: gasPrice || '0x0',
- activeCurrency: selectedCurrency,
- gas: gasLimit || '0x0',
- blockGasLimit: currentBlockGasLimit,
- }),
- h(
- 'div.send-screen-gas-input-customize',
- { onClick: () => this.setState({ isGasTooltipOpen: !isGasTooltipOpen }) },
- ['Customize']
- ),
- ]),
- h('div.send-screen-input-wrapper__error-message', [
- gasPriceErrorMessage || gasLimitErrorMessage,
- ]),
- ])
-}
-
-SendTokenScreen.prototype.renderMemoInput = function () {
- return h('div.send-screen-input-wrapper', [
- h('div', {}, ['Transaction memo (optional)']),
- h(
- 'input.large-input.send-screen-input',
- { onChange: e => this.setState({ memo: e.target.value }) }
- ),
- ])
-}
-
-SendTokenScreen.prototype.renderButtons = function () {
- const { selectedAddress, backToAccountDetail } = this.props
- const { isValid } = this.validate()
-
- return h('div.send-token__button-group', [
- h('button.send-token__button-next.btn-secondary', {
- className: !isValid && 'send-screen__send-button__disabled',
- onClick: () => isValid && this.submit(),
- }, ['Next']),
- h('button.send-token__button-cancel.btn-tertiary', {
- onClick: () => backToAccountDetail(selectedAddress),
- }, ['Cancel']),
- ])
-}
-
-SendTokenScreen.prototype.render = function () {
- const {
- selectedTokenAddress,
- selectedToken,
- warning,
- } = this.props
-
- return h('div.send-token', [
- h('div.send-token__content', [
- h(Identicon, {
- diameter: 75,
- address: selectedTokenAddress,
- }),
- h('div.send-token__title', ['Send Tokens']),
- h('div.send-token__description', ['Send Tokens to anyone with an Ethereum account']),
- h('div.send-token__balance-text', ['Your Token Balance is:']),
- h('div.send-token__token-balance', [
- h(TokenBalance, { token: selectedToken, balanceOnly: true }),
- ]),
- h('div.send-token__token-symbol', [selectedToken.symbol]),
- this.renderToAddressInput(),
- this.renderAmountInput(),
- this.renderGasInput(),
- this.renderMemoInput(),
- warning && h('div.send-screen-input-wrapper--error', {},
- h('div.send-screen-input-wrapper__error-message', [
- warning,
- ])
- ),
- ]),
- this.renderButtons(),
- ])
-}
diff --git a/ui/app/components/send/currency-toggle.js b/ui/app/components/send/currency-toggle.js
deleted file mode 100644
index 7aaccd490..000000000
--- a/ui/app/components/send/currency-toggle.js
+++ /dev/null
@@ -1,44 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const classnames = require('classnames')
-
-module.exports = CurrencyToggle
-
-inherits(CurrencyToggle, Component)
-function CurrencyToggle () {
- Component.call(this)
-}
-
-const defaultCurrencies = [ 'ETH', 'USD' ]
-
-CurrencyToggle.prototype.renderToggles = function () {
- const { onClick, activeCurrency } = this.props
- const [currencyA, currencyB] = this.props.currencies || defaultCurrencies
-
- return [
- h('span', {
- className: classnames('currency-toggle__item', {
- 'currency-toggle__item--selected': currencyA === activeCurrency,
- }),
- onClick: () => onClick(currencyA),
- }, [ currencyA ]),
- '<>',
- h('span', {
- className: classnames('currency-toggle__item', {
- 'currency-toggle__item--selected': currencyB === activeCurrency,
- }),
- onClick: () => onClick(currencyB),
- }, [ currencyB ]),
- ]
-}
-
-CurrencyToggle.prototype.render = function () {
- const currencies = this.props.currencies || defaultCurrencies
-
- return h('span.currency-toggle', currencies.length
- ? this.renderToggles()
- : []
- )
-}
-
diff --git a/ui/app/components/send/eth-fee-display.js b/ui/app/components/send/eth-fee-display.js
deleted file mode 100644
index 9eda5ec62..000000000
--- a/ui/app/components/send/eth-fee-display.js
+++ /dev/null
@@ -1,37 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const EthBalance = require('../eth-balance')
-const { getTxFeeBn } = require('../../util')
-
-module.exports = EthFeeDisplay
-
-inherits(EthFeeDisplay, Component)
-function EthFeeDisplay () {
- Component.call(this)
-}
-
-EthFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- return h(EthBalance, {
- value: getTxFeeBn(gas, gasPrice, blockGasLimit),
- currentCurrency: activeCurrency,
- conversionRate,
- showFiat: false,
- hideTooltip: true,
- styleOveride: {
- color: '#5d5d5d',
- fontSize: '16px',
- fontFamily: 'DIN OT',
- lineHeight: '22.4px',
- },
- })
-}
-
diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js
index 0c4c3f7a9..f6af13454 100644
--- a/ui/app/components/send/gas-fee-display-v2.js
+++ b/ui/app/components/send/gas-fee-display-v2.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const CurrencyDisplay = require('./currency-display')
+const t = require('../../../i18n')
module.exports = GasFeeDisplay
@@ -17,6 +18,7 @@ GasFeeDisplay.prototype.render = function () {
onClick,
primaryCurrency = 'ETH',
convertedCurrency,
+ gasLoadingError,
} = this.props
return h('div.send-v2__gas-fee-display', [
@@ -30,15 +32,16 @@ GasFeeDisplay.prototype.render = function () {
convertedPrefix: '$',
readOnly: true,
})
- : h('div.currency-display', 'Loading...'),
+ : gasLoadingError
+ ? h('div..currency-display.currency-display--message', 'Set with the gas price customizer.')
+ : h('div.currency-display', t('loading')),
- h('button.send-v2__sliders-icon-container', {
+ h('button.sliders-icon-container', {
onClick,
- disabled: !gasTotal,
+ disabled: !gasTotal && !gasLoadingError,
}, [
- h('i.fa.fa-sliders.send-v2__sliders-icon'),
+ h('i.fa.fa-sliders.sliders-icon'),
]),
])
}
-
diff --git a/ui/app/components/send/gas-fee-display.js b/ui/app/components/send/gas-fee-display.js
deleted file mode 100644
index a9a3f3f49..000000000
--- a/ui/app/components/send/gas-fee-display.js
+++ /dev/null
@@ -1,62 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const USDFeeDisplay = require('./usd-fee-display')
-const EthFeeDisplay = require('./eth-fee-display')
-const { getTxFeeBn, formatBalance, shortenBalance } = require('../../util')
-
-module.exports = GasFeeDisplay
-
-inherits(GasFeeDisplay, Component)
-function GasFeeDisplay () {
- Component.call(this)
-}
-
-GasFeeDisplay.prototype.getTokenValue = function () {
- const {
- tokenExchangeRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- const value = formatBalance(getTxFeeBn(gas, gasPrice, blockGasLimit), 6, true)
- const [ethNumber] = value.split(' ')
-
- return shortenBalance(Number(ethNumber) / tokenExchangeRate, 6)
-}
-
-GasFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- switch (activeCurrency) {
- case 'USD':
- return h(USDFeeDisplay, {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- })
- case 'ETH':
- return h(EthFeeDisplay, {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- })
- default:
- return h('div.token-gas', [
- h('div.token-gas__amount', this.getTokenValue()),
- h('div.token-gas__symbol', activeCurrency),
- ])
- }
-}
-
diff --git a/ui/app/components/send/gas-tooltip.js b/ui/app/components/send/gas-tooltip.js
index 46aff3499..d925d3ed8 100644
--- a/ui/app/components/send/gas-tooltip.js
+++ b/ui/app/components/send/gas-tooltip.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const InputNumber = require('../input-number.js')
+const t = require('../../../i18n')
module.exports = GasTooltip
@@ -81,7 +82,7 @@ GasTooltip.prototype.render = function () {
'marginTop': '81px',
},
}, [
- h('span.gas-tooltip-label', {}, ['Gas Limit']),
+ h('span.gas-tooltip-label', {}, [t('gasLimit')]),
h('i.fa.fa-info-circle'),
]),
h(InputNumber, {
@@ -97,4 +98,3 @@ GasTooltip.prototype.render = function () {
]),
])
}
-
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index b3c73f9b6..aca1a5a0a 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -53,6 +53,7 @@ function mapStateToProps (state) {
primaryCurrency,
convertedCurrency: getCurrentCurrency(state),
data,
+ selectedAddress,
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
tokenContract: getSelectedTokenContract(state),
unapprovedTxs: state.metamask.unapprovedTxs,
@@ -73,13 +74,13 @@ function mapDispatchToProps (dispatch) {
updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)),
updateTx: txData => dispatch(actions.updateTransaction(txData)),
setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)),
- addToAddressBook: address => dispatch(actions.addToAddressBook(address)),
+ addToAddressBook: (address, nickname) => dispatch(actions.addToAddressBook(address, nickname)),
updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)),
updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)),
updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)),
updateSendTokenBalance: tokenBalance => dispatch(actions.updateSendTokenBalance(tokenBalance)),
updateSendFrom: newFrom => dispatch(actions.updateSendFrom(newFrom)),
- updateSendTo: newTo => dispatch(actions.updateSendTo(newTo)),
+ updateSendTo: (newTo, nickname) => dispatch(actions.updateSendTo(newTo, nickname)),
updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)),
updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)),
updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)),
diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js
index e0cdd0a58..72074229e 100644
--- a/ui/app/components/send/to-autocomplete.js
+++ b/ui/app/components/send/to-autocomplete.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const AccountListItem = require('./account-list-item')
+const t = require('../../../i18n')
module.exports = ToAutoComplete
@@ -92,7 +93,7 @@ ToAutoComplete.prototype.render = function () {
return h('div.send-v2__to-autocomplete', {}, [
h('input.send-v2__to-autocomplete__input', {
- placeholder: 'Recipient Address',
+ placeholder: t('recipientAddress'),
className: inError ? `send-v2__error-border` : '',
value: to,
onChange: event => onChange(event.target.value),
@@ -111,4 +112,3 @@ ToAutoComplete.prototype.render = function () {
])
}
-
diff --git a/ui/app/components/send/usd-fee-display.js b/ui/app/components/send/usd-fee-display.js
deleted file mode 100644
index 4cf31a493..000000000
--- a/ui/app/components/send/usd-fee-display.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const FiatValue = require('../fiat-value')
-const { getTxFeeBn } = require('../../util')
-
-module.exports = USDFeeDisplay
-
-inherits(USDFeeDisplay, Component)
-function USDFeeDisplay () {
- Component.call(this)
-}
-
-USDFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- return h(FiatValue, {
- value: getTxFeeBn(gas, gasPrice, blockGasLimit),
- conversionRate,
- currentCurrency: activeCurrency,
- style: {
- color: '#5d5d5d',
- fontSize: '16px',
- fontFamily: 'DIN OT',
- lineHeight: '22.4px',
- },
- })
-}
-
diff --git a/ui/app/components/sender-to-recipient.js b/ui/app/components/sender-to-recipient.js
new file mode 100644
index 000000000..f35c353ad
--- /dev/null
+++ b/ui/app/components/sender-to-recipient.js
@@ -0,0 +1,66 @@
+const { Component } = require('react')
+const h = require('react-hyperscript')
+const PropTypes = require('prop-types')
+const t = require('../../i18n')
+const Identicon = require('./identicon')
+
+class SenderToRecipient extends Component {
+ renderRecipientIcon () {
+ const { recipientAddress } = this.props
+ return (
+ recipientAddress
+ ? h(Identicon, { address: recipientAddress, diameter: 20 })
+ : h('i.fa.fa-file-text-o')
+ )
+ }
+
+ renderRecipient () {
+ const { recipientName } = this.props
+ return (
+ h('.sender-to-recipient__recipient', [
+ this.renderRecipientIcon(),
+ h(
+ '.sender-to-recipient__name.sender-to-recipient__recipient-name',
+ recipientName || t('newContract')
+ ),
+ ])
+ )
+ }
+
+ render () {
+ const { senderName, senderAddress } = this.props
+
+ return (
+ h('.sender-to-recipient__container', [
+ h('.sender-to-recipient__sender', [
+ h('.sender-to-recipient__sender-icon', [
+ h(Identicon, {
+ address: senderAddress,
+ diameter: 20,
+ }),
+ ]),
+ h('.sender-to-recipient__name.sender-to-recipient__sender-name', senderName),
+ ]),
+ h('.sender-to-recipient__arrow-container', [
+ h('.sender-to-recipient__arrow-circle', [
+ h('img', {
+ height: 15,
+ width: 15,
+ src: '/images/arrow-right.svg',
+ }),
+ ]),
+ ]),
+ this.renderRecipient(),
+ ])
+ )
+ }
+}
+
+SenderToRecipient.propTypes = {
+ senderName: PropTypes.string,
+ senderAddress: PropTypes.string,
+ recipientName: PropTypes.string,
+ recipientAddress: PropTypes.string,
+}
+
+module.exports = SenderToRecipient
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index 2270b8236..5729f893c 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -7,6 +7,7 @@ const { qrcode } = require('qrcode-npm')
const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions')
const { isValidAddress } = require('../util')
const SimpleDropdown = require('./dropdowns/simple-dropdown')
+const t = require('../../i18n')
function mapStateToProps (state) {
const {
@@ -14,11 +15,13 @@ function mapStateToProps (state) {
tokenExchangeRates,
selectedAddress,
} = state.metamask
-
+ const { warning } = state.appState
+
return {
coinOptions,
tokenExchangeRates,
selectedAddress,
+ warning,
}
}
@@ -51,8 +54,7 @@ ShapeshiftForm.prototype.componentWillMount = function () {
this.props.shapeShiftSubview()
}
-ShapeshiftForm.prototype.onCoinChange = function (e) {
- const coin = e.target.value
+ShapeshiftForm.prototype.onCoinChange = function (coin) {
this.setState({
depositCoin: coin,
errorMessage: '',
@@ -92,7 +94,7 @@ ShapeshiftForm.prototype.onBuyWithShapeShift = function () {
}))
.catch(() => this.setState({
showQrCode: false,
- errorMessage: 'Invalid Request',
+ errorMessage: t('invalidRequest'),
isLoading: false,
}))
}
@@ -124,16 +126,16 @@ ShapeshiftForm.prototype.renderMarketInfo = function () {
return h('div.shapeshift-form__metadata', {}, [
- this.renderMetadata('Status', limit ? 'Available' : 'Unavailable'),
- this.renderMetadata('Limit', limit),
- this.renderMetadata('Exchange Rate', rate),
- this.renderMetadata('Minimum', minimum),
+ this.renderMetadata(t('status'), limit ? t('available') : t('unavailable')),
+ this.renderMetadata(t('limit'), limit),
+ this.renderMetadata(t('exchangeRate'), rate),
+ this.renderMetadata(t('min'), minimum),
])
}
ShapeshiftForm.prototype.renderQrCode = function () {
- const { depositAddress, isLoading } = this.state
+ const { depositAddress, isLoading, depositCoin } = this.state
const qrImage = qrcode(4, 'M')
qrImage.addData(depositAddress)
qrImage.make()
@@ -141,7 +143,7 @@ ShapeshiftForm.prototype.renderQrCode = function () {
return h('div.shapeshift-form', {}, [
h('div.shapeshift-form__deposit-instruction', [
- 'Deposit your BTC to the address below:',
+ t('depositCoin', [depositCoin.toUpperCase()]),
]),
h('div', depositAddress),
@@ -164,7 +166,7 @@ ShapeshiftForm.prototype.renderQrCode = function () {
ShapeshiftForm.prototype.render = function () {
- const { coinOptions, btnClass } = this.props
+ const { coinOptions, btnClass, warning } = this.props
const { depositCoin, errorMessage, showQrCode, depositAddress } = this.state
const coinPair = `${depositCoin}_eth`
const { tokenExchangeRates } = this.props
@@ -178,11 +180,11 @@ ShapeshiftForm.prototype.render = function () {
h('div.shapeshift-form__selector', [
- h('div.shapeshift-form__selector-label', 'Deposit'),
+ h('div.shapeshift-form__selector-label', t('deposit')),
h(SimpleDropdown, {
selectedOption: this.state.depositCoin,
- onSelect: this.onCoinChange,
+ onSelect: (coin) => this.onCoinChange(coin),
options: Object.entries(coinOptions).map(([coin]) => ({
value: coin.toLowerCase(),
displayValue: coin,
@@ -198,7 +200,7 @@ ShapeshiftForm.prototype.render = function () {
h('div.shapeshift-form__selector', [
h('div.shapeshift-form__selector-label', [
- 'Receive',
+ t('receive'),
]),
h('div.shapeshift-form__selector-input', ['ETH']),
@@ -207,14 +209,16 @@ ShapeshiftForm.prototype.render = function () {
]),
- h('div', {
+ warning && h('div.shapeshift-form__address-input-label', warning),
+
+ !warning && h('div', {
className: classnames('shapeshift-form__address-input-wrapper', {
'shapeshift-form__address-input-wrapper--error': errorMessage,
}),
}, [
h('div.shapeshift-form__address-input-label', [
- 'Your Refund Address',
+ t('refundAddress'),
]),
h('input.shapeshift-form__address-input', {
@@ -228,15 +232,15 @@ ShapeshiftForm.prototype.render = function () {
h('divshapeshift-form__address-input-error-message', [errorMessage]),
]),
- this.renderMarketInfo(),
+ !warning && this.renderMarketInfo(),
]),
- !depositAddress && h('button.shapeshift-form__shapeshift-buy-btn', {
+ !depositAddress && h('button.btn-primary--lg.shapeshift-form__shapeshift-buy-btn', {
className: btnClass,
disabled: !token,
onClick: () => this.onBuyWithShapeShift(),
- }, ['Buy']),
+ }, [t('buy')]),
])
}
diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js
index 017bf9f0c..fddbc6821 100644
--- a/ui/app/components/shift-list-item.js
+++ b/ui/app/components/shift-list-item.js
@@ -6,6 +6,7 @@ const vreme = new (require('vreme'))()
const explorerLink = require('etherscan-link').createExplorerLink
const actions = require('../actions')
const addressSummary = require('../util').addressSummary
+const t = require('../../i18n')
const CopyButton = require('./copyButton')
const EthBalance = require('./eth-balance')
@@ -75,7 +76,7 @@ ShiftListItem.prototype.renderUtilComponents = function () {
value: this.props.depositAddress,
}),
h(Tooltip, {
- title: 'QR Code',
+ title: t('qrCode'),
}, [
h('i.fa.fa-qrcode.pointer.pop-hover', {
onClick: () => props.dispatch(actions.reshowQrCode(props.depositAddress, props.depositType)),
@@ -135,8 +136,8 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, `${props.depositType} to ETH via ShapeShift`),
- h('div', 'No deposits received'),
+ }, t('toETHviaShapeShift', [props.depositType])),
+ h('div', t('noDeposits')),
h('div', {
style: {
fontSize: 'x-small',
@@ -158,8 +159,8 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, `${props.depositType} to ETH via ShapeShift`),
- h('div', 'Conversion in progress'),
+ }, t('toETHviaShapeShift', [props.depositType])),
+ h('div', t('conversionProgress')),
h('div', {
style: {
fontSize: 'x-small',
@@ -184,7 +185,7 @@ ShiftListItem.prototype.renderInfo = function () {
color: '#ABA9AA',
width: '100%',
},
- }, 'From ShapeShift'),
+ }, t('fromShapeShift')),
h('div', formatDate(props.time)),
h('div', {
style: {
@@ -196,7 +197,7 @@ ShiftListItem.prototype.renderInfo = function () {
])
case 'failed':
- return h('span.error', '(Failed)')
+ return h('span.error', '(' + t('failed') + ')')
default:
return ''
}
diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js
new file mode 100644
index 000000000..810a52e55
--- /dev/null
+++ b/ui/app/components/signature-request.js
@@ -0,0 +1,250 @@
+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 classnames = require('classnames')
+
+const AccountDropdownMini = require('./dropdowns/account-dropdown-mini')
+
+const actions = require('../actions')
+const t = require('../../i18n')
+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', t('sigRequest')),
+
+ 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', [t('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', [t('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 () {
+ return h('div.request-signature__request-info', [
+
+ h('div.request-signature__headline', [
+ t('yourSigRequested'),
+ ]),
+
+ ])
+}
+
+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 = t('youSign') + ':'
+
+ const { txData } = this.props
+ const { type, msgParams: { data } } = txData
+
+ if (type === 'personal_sign') {
+ rows = [{ name: t('message'), value: this.msgHexToText(data) }]
+ } else if (type === 'eth_signTypedData') {
+ rows = data
+ } else if (type === 'eth_sign') {
+ rows = [{ name: t('message'), value: data }]
+ notice = t('signNotice')
+ }
+
+ return h('div.request-signature__body', {}, [
+
+ this.renderAccountInfo(),
+
+ this.renderRequestInfo(),
+
+ h('div.request-signature__notice', {
+ className: classnames({
+ 'request-signature__notice': type === 'personal_sign' || type === 'eth_signTypedData',
+ 'request-signature__warning': type === 'eth_sign',
+ }),
+ }, [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 {
+ 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.btn-secondary--lg.request-signature__footer__cancel-button', {
+ onClick: cancel,
+ }, t('cancel')),
+ h('button.btn-primary--lg', {
+ onClick: sign,
+ }, t('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 32c9e4f24..0016a09c1 100644
--- a/ui/app/components/tab-bar.js
+++ b/ui/app/components/tab-bar.js
@@ -1,6 +1,6 @@
const { Component } = require('react')
const h = require('react-hyperscript')
-const PropTypes = require('react').PropTypes
+const PropTypes = require('prop-types')
const classnames = require('classnames')
class TabBar extends Component {
diff --git a/ui/app/components/template.js b/ui/app/components/template.js
deleted file mode 100644
index b6ed8eaa0..000000000
--- a/ui/app/components/template.js
+++ /dev/null
@@ -1,18 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = NewComponent
-
-inherits(NewComponent, Component)
-function NewComponent () {
- Component.call(this)
-}
-
-NewComponent.prototype.render = function () {
- const props = this.props
-
- return (
- h('span', props.message)
- )
-}
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
index 8e06e0f27..01529aeda 100644
--- a/ui/app/components/token-list.js
+++ b/ui/app/components/token-list.js
@@ -5,6 +5,7 @@ const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js')
const connect = require('react-redux').connect
const selectors = require('../selectors')
+const t = require('../../i18n')
function mapStateToProps (state) {
return {
@@ -42,7 +43,7 @@ TokenList.prototype.render = function () {
const { tokens, isLoading, error } = state
if (isLoading) {
- return this.message('Loading Tokens...')
+ return this.message(t('loadingTokens'))
}
if (error) {
@@ -52,7 +53,7 @@ TokenList.prototype.render = function () {
padding: '80px',
},
}, [
- 'We had trouble loading your token balances. You can view them ',
+ t('troubleTokenBalances'),
h('span.hotFix', {
style: {
color: 'rgba(247, 134, 28, 1)',
@@ -63,7 +64,7 @@ TokenList.prototype.render = function () {
url: `https://ethplorer.io/address/${userAddress}`,
})
},
- }, 'here'),
+ }, t('here')),
])
}
diff --git a/ui/app/components/transaction-list-item-icon.js b/ui/app/components/transaction-list-item-icon.js
deleted file mode 100644
index f442b05af..000000000
--- a/ui/app/components/transaction-list-item-icon.js
+++ /dev/null
@@ -1,68 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const Tooltip = require('./tooltip')
-
-const Identicon = require('./identicon')
-
-module.exports = TransactionIcon
-
-inherits(TransactionIcon, Component)
-function TransactionIcon () {
- Component.call(this)
-}
-
-TransactionIcon.prototype.render = function () {
- const { transaction, txParams, isMsg } = this.props
- switch (transaction.status) {
- case 'unapproved':
- return h(!isMsg ? '.unapproved-tx-icon' : 'i.fa.fa-certificate.fa-lg')
-
- case 'rejected':
- return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
- style: {
- width: '24px',
- },
- })
-
- case 'failed':
- return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
- style: {
- width: '24px',
- },
- })
-
- case 'submitted':
- return h(Tooltip, {
- title: 'Pending',
- position: 'right',
- }, [
- h('i.fa.fa-ellipsis-h', {
- style: {
- fontSize: '27px',
- },
- }),
- ])
- }
-
- if (isMsg) {
- return h('i.fa.fa-certificate.fa-lg', {
- style: {
- width: '24px',
- },
- })
- }
-
- if (txParams.to) {
- return h(Identicon, {
- diameter: 24,
- address: txParams.to || transaction.hash,
- })
- } else {
- return h('i.fa.fa-file-text-o.fa-lg', {
- style: {
- width: '24px',
- },
- })
- }
-}
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
deleted file mode 100644
index 4e3d2cb93..000000000
--- a/ui/app/components/transaction-list-item.js
+++ /dev/null
@@ -1,238 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const connect = require('react-redux').connect
-
-const EthBalance = require('./eth-balance')
-const addressSummary = require('../util').addressSummary
-const explorerLink = require('etherscan-link').createExplorerLink
-const CopyButton = require('./copyButton')
-const vreme = new (require('vreme'))()
-const Tooltip = require('./tooltip')
-const numberToBN = require('number-to-bn')
-const actions = require('../actions')
-
-const TransactionIcon = require('./transaction-list-item-icon')
-const ShiftListItem = require('./shift-list-item')
-
-const mapDispatchToProps = dispatch => {
- return {
- retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
- }
-}
-
-module.exports = connect(null, mapDispatchToProps)(TransactionListItem)
-
-inherits(TransactionListItem, Component)
-function TransactionListItem () {
- Component.call(this)
-}
-
-TransactionListItem.prototype.showRetryButton = function () {
- const { transaction = {} } = this.props
- const { status, time } = transaction
- return status === 'submitted' && Date.now() - time > 30000
-}
-
-TransactionListItem.prototype.render = function () {
- const { transaction, network, conversionRate, currentCurrency } = this.props
- const { status } = transaction
- if (transaction.key === 'shapeshift') {
- if (network === '1') return h(ShiftListItem, transaction)
- }
- var date = formatDate(transaction.time)
-
- let isLinkable = false
- const numericNet = parseInt(network)
- isLinkable = numericNet === 1 || numericNet === 3 || numericNet === 4 || numericNet === 42
-
- var isMsg = ('msgParams' in transaction)
- var isTx = ('txParams' in transaction)
- var isPending = status === 'unapproved'
- let txParams
- if (isTx) {
- txParams = transaction.txParams
- } else if (isMsg) {
- txParams = transaction.msgParams
- }
-
- const nonce = txParams.nonce ? numberToBN(txParams.nonce).toString(10) : ''
-
- const isClickable = ('hash' in transaction && isLinkable) || isPending
- return (
- h('.transaction-list-item.flex-column', {
- onClick: (event) => {
- if (isPending) {
- this.props.showTx(transaction.id)
- }
- event.stopPropagation()
- if (!transaction.hash || !isLinkable) return
- var url = explorerLink(transaction.hash, parseInt(network))
- global.platform.openWindow({ url })
- },
- style: {
- padding: '20px 0',
- alignItems: 'center',
- },
- }, [
- h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
- style: {
- width: '100%',
- },
- }, [
- h('.identicon-wrapper.flex-column.flex-center.select-none', [
- h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
- ]),
-
- h(Tooltip, {
- title: 'Transaction Number',
- position: 'right',
- }, [
- h('span', {
- style: {
- display: 'flex',
- cursor: 'normal',
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'center',
- padding: '10px',
- },
- }, nonce),
- ]),
-
- h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
- domainField(txParams),
- h('div', date),
- recipientField(txParams, transaction, isTx, isMsg),
- ]),
-
- // Places a copy button if tx is successful, else places a placeholder empty div.
- transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
-
- isTx ? h(EthBalance, {
- value: txParams.value,
- conversionRate,
- currentCurrency,
- width: '55px',
- shorten: true,
- showFiat: false,
- style: {fontSize: '15px'},
- }) : h('.flex-column'),
- ]),
-
- this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', {
- onClick: event => {
- event.stopPropagation()
- this.resubmit()
- },
- style: {
- height: '22px',
- borderRadius: '22px',
- color: '#F9881B',
- padding: '0 20px',
- backgroundColor: '#FFE3C9',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- fontSize: '8px',
- cursor: 'pointer',
- },
- }, [
- h('div', {
- style: {
- paddingRight: '2px',
- },
- }, 'Taking too long?'),
- h('div', {
- style: {
- textDecoration: 'underline',
- },
- }, 'Retry with a higher gas price here'),
- ]),
- ])
- )
-}
-
-TransactionListItem.prototype.resubmit = function () {
- const { transaction } = this.props
- this.props.retryTransaction(transaction.id)
-}
-
-function domainField (txParams) {
- return h('div', {
- style: {
- fontSize: 'x-small',
- color: '#ABA9AA',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- width: '100%',
- },
- }, [
- txParams.origin,
- ])
-}
-
-function recipientField (txParams, transaction, isTx, isMsg) {
- let message
-
- if (isMsg) {
- message = 'Signature Requested'
- } else if (txParams.to) {
- message = addressSummary(txParams.to)
- } else {
- message = 'Contract Published'
- }
-
- return h('div', {
- style: {
- fontSize: 'x-small',
- color: '#ABA9AA',
- },
- }, [
- message,
- renderErrorOrWarning(transaction),
- ])
-}
-
-function formatDate (date) {
- return vreme.format(new Date(date), 'March 16 2014 14:30')
-}
-
-function renderErrorOrWarning (transaction) {
- const { status } = transaction
-
- // show rejected
- if (status === 'rejected') {
- return h('span.error', ' (Rejected)')
- }
- if (transaction.err || transaction.warning) {
- const { err, warning = {} } = transaction
- const errFirst = !!((err && warning) || err)
-
- errFirst ? err.message : warning.message
-
- // show error
- if (err) {
- const message = err.message || ''
- return (
- h(Tooltip, {
- title: message,
- position: 'bottom',
- }, [
- h(`span.error`, ` (Failed)`),
- ])
- )
- }
-
- // show warning
- if (warning) {
- const message = warning.message
- return h(Tooltip, {
- title: message,
- position: 'bottom',
- }, [
- h(`span.warning`, ` (Warning)`),
- ])
- }
- }
-}
diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js
deleted file mode 100644
index 69b72614c..000000000
--- a/ui/app/components/transaction-list.js
+++ /dev/null
@@ -1,87 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-const TransactionListItem = require('./transaction-list-item')
-
-module.exports = TransactionList
-
-
-inherits(TransactionList, Component)
-function TransactionList () {
- Component.call(this)
-}
-
-TransactionList.prototype.render = function () {
- const { transactions, network, unapprovedMsgs, conversionRate } = this.props
-
- var shapeShiftTxList
- if (network === '1') {
- shapeShiftTxList = this.props.shapeShiftTxList
- }
- const txsToRender = !shapeShiftTxList ? transactions.concat(unapprovedMsgs) : transactions.concat(unapprovedMsgs, shapeShiftTxList)
- .sort((a, b) => b.time - a.time)
-
- return (
-
- h('section.transaction-list.full-flex-height', {
- style: {
- justifyContent: 'center',
- },
- }, [
-
- h('style', `
- .transaction-list .transaction-list-item:not(:last-of-type) {
- border-bottom: 1px solid #D4D4D4;
- }
- .transaction-list .transaction-list-item .ether-balance-label {
- display: block !important;
- font-size: small;
- }
- `),
-
- h('.tx-list', {
- style: {
- overflowY: 'auto',
- height: '100%',
- padding: '0 20px',
- textAlign: 'center',
- },
- }, [
-
- txsToRender.length
- ? txsToRender.map((transaction, i) => {
- let key
- switch (transaction.key) {
- case 'shapeshift':
- const { depositAddress, time } = transaction
- key = `shift-tx-${depositAddress}-${time}-${i}`
- break
- default:
- key = `tx-${transaction.id}-${i}`
- }
- return h(TransactionListItem, {
- transaction, i, network, key,
- conversionRate,
- showTx: (txId) => {
- this.props.viewPendingTx(txId)
- },
- })
- })
- : h('.flex-center.full-flex-height', {
- style: {
- flexDirection: 'column',
- justifyContent: 'center',
- },
- }, [
- h('p', {
- style: {
- marginTop: '50px',
- },
- }, 'No transaction history.'),
- ]),
- ]),
- ])
- )
-}
-
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index 7ccc5c315..d104eda88 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -9,18 +9,28 @@ abiDecoder.addABI(abi)
const Identicon = require('./identicon')
const contractMap = require('eth-contract-metadata')
+const actions = require('../actions')
const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const { calcTokenAmount } = require('../token-util')
const { getCurrentCurrency } = require('../selectors')
+const t = require('../../i18n')
-module.exports = connect(mapStateToProps)(TxListItem)
+module.exports = connect(mapStateToProps, mapDispatchToProps)(TxListItem)
function mapStateToProps (state) {
return {
tokens: state.metamask.tokens,
currentCurrency: getCurrentCurrency(state),
tokenExchangeRates: state.metamask.tokenExchangeRates,
+ selectedAddressTxList: state.metamask.selectedAddressTxList,
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ setSelectedToken: tokenAddress => dispatch(actions.setSelectedToken(tokenAddress)),
+ retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
}
}
@@ -31,6 +41,7 @@ function TxListItem () {
this.state = {
total: null,
fiatTotal: null,
+ isTokenTx: null,
}
}
@@ -39,12 +50,13 @@ TxListItem.prototype.componentDidMount = async function () {
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { name: txDataName } = decodedData || {}
+ const isTokenTx = txDataName === 'transfer'
- const { total, fiatTotal } = txDataName === 'transfer'
+ const { total, fiatTotal } = isTokenTx
? await this.getSendTokenTotal()
: this.getSendEtherTotal()
- this.setState({ total, fiatTotal })
+ this.setState({ total, fiatTotal, isTokenTx })
}
TxListItem.prototype.getAddressText = function () {
@@ -63,7 +75,7 @@ TxListItem.prototype.getAddressText = function () {
default:
return address
? `${address.slice(0, 10)}...${address.slice(-4)}`
- : 'Contract Published'
+ : t('contractDeployment')
}
}
@@ -167,22 +179,49 @@ TxListItem.prototype.getSendTokenTotal = async function () {
}
}
+TxListItem.prototype.showRetryButton = function () {
+ const {
+ transactionSubmittedTime,
+ selectedAddressTxList,
+ transactionId,
+ txParams,
+ } = this.props
+ const currentNonce = txParams.nonce
+ const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
+ const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
+ const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[currentNonceSubmittedTxs.length - 1]
+ const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce
+ && lastSubmittedTxWithCurrentNonce.id === transactionId
+
+ return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
+}
+
+TxListItem.prototype.setSelectedToken = function (tokenAddress) {
+ this.props.setSelectedToken(tokenAddress)
+}
+
+TxListItem.prototype.resubmit = function () {
+ const { transactionId } = this.props
+ this.props.retryTransaction(transactionId)
+}
+
TxListItem.prototype.render = function () {
const {
transactionStatus,
transactionAmount,
onClick,
- transActionId,
+ transactionId,
dateString,
address,
className,
+ txParams,
} = this.props
- const { total, fiatTotal } = this.state
+ const { total, fiatTotal, isTokenTx } = this.state
const showFiatTotal = transactionAmount !== '0x0' && fiatTotal
return h(`div${className || ''}`, {
- key: transActionId,
- onClick: () => onClick && onClick(transActionId),
+ key: transactionId,
+ onClick: () => onClick && onClick(transactionId),
}, [
h(`div.flex-column.tx-list-item-wrapper`, {}, [
@@ -223,9 +262,10 @@ TxListItem.prototype.render = function () {
className: classnames('tx-list-status', {
'tx-list-status--rejected': transactionStatus === 'rejected',
'tx-list-status--failed': transactionStatus === 'failed',
+ 'tx-list-status--dropped': transactionStatus === 'dropped',
}),
},
- transactionStatus,
+ this.txStatusIndicator(),
),
]),
]),
@@ -240,6 +280,48 @@ TxListItem.prototype.render = function () {
]),
]),
+
+ this.showRetryButton() && h('div.tx-list-item-retry-container', [
+
+ h('span.tx-list-item-retry-copy', 'Taking too long?'),
+
+ h('span.tx-list-item-retry-link', {
+ onClick: (event) => {
+ event.stopPropagation()
+ if (isTokenTx) {
+ this.setSelectedToken(txParams.to)
+ }
+ this.resubmit()
+ },
+ }, 'Increase the gas price on your transaction'),
+
+ ]),
+
]), // holding on icon from design
])
}
+
+TxListItem.prototype.txStatusIndicator = function () {
+ const { transactionStatus } = this.props
+
+ let name
+
+ if (transactionStatus === 'unapproved') {
+ name = t('unapproved')
+ } else if (transactionStatus === 'rejected') {
+ name = t('rejected')
+ } else if (transactionStatus === 'approved') {
+ name = t('approved')
+ } else if (transactionStatus === 'signed') {
+ name = t('signed')
+ } else if (transactionStatus === 'submitted') {
+ name = t('submitted')
+ } else if (transactionStatus === 'confirmed') {
+ name = t('confirmed')
+ } else if (transactionStatus === 'failed') {
+ name = t('failed')
+ } else if (transactionStatus === 'dropped') {
+ name = t('dropped')
+ }
+ return name
+}
diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js
index f60053bef..6236ddf49 100644
--- a/ui/app/components/tx-list.js
+++ b/ui/app/components/tx-list.js
@@ -13,6 +13,7 @@ const { tokenInfoGetter } = require('../token-util')
const { withRouter } = require('react-router-dom')
const { compose } = require('recompose')
const { CONFIRM_TRANSACTION_ROUTE } = require('../routes')
+const t = require('../../i18n')
module.exports = compose(
withRouter,
@@ -45,7 +46,7 @@ TxList.prototype.render = function () {
return h('div.flex-column', [
h('div.flex-row.tx-list-header-wrapper', [
h('div.flex-row.tx-list-header', [
- h('div', 'transactions'),
+ h('div', t('transactions')),
]),
]),
h('div.flex-column.tx-list-container', {}, [
@@ -62,7 +63,7 @@ TxList.prototype.renderTransaction = function () {
: [h(
'div.tx-list-item.tx-list-item--empty',
{ key: 'tx-list-none' },
- [ 'No Transactions' ],
+ [ t('noTransactions') ],
)]
}
@@ -80,9 +81,10 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
address: transaction.txParams.to,
transactionStatus: transaction.status,
transactionAmount: transaction.txParams.value,
- transActionId: transaction.id,
+ transactionId: transaction.id,
transactionHash: transaction.hash,
transactionNetworkId: transaction.metamaskNetworkId,
+ transactionSubmittedTime: transaction.submittedTime,
}
const {
@@ -90,30 +92,32 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
transactionStatus,
transactionAmount,
dateString,
- transActionId,
+ transactionId,
transactionHash,
transactionNetworkId,
+ transactionSubmittedTime,
} = props
const { history } = this.props
const opts = {
- key: transActionId || transactionHash,
+ key: transactionId || transactionHash,
txParams: transaction.txParams,
transactionStatus,
- transActionId,
+ transactionId,
dateString,
address,
transactionAmount,
transactionHash,
conversionRate,
tokenInfoGetter: this.tokenInfoGetter,
+ transactionSubmittedTime,
}
const isUnapproved = transactionStatus === 'unapproved'
if (isUnapproved) {
opts.onClick = () => history.push(CONFIRM_TRANSACTION_ROUTE)
- opts.transactionStatus = 'Not Started'
+ opts.transactionStatus = t('Not Started')
} else if (transactionHash) {
opts.onClick = () => this.view(transactionHash, transactionNetworkId)
}
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index 60c108c36..89d8d15df 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -8,6 +8,7 @@ const { compose } = require('recompose')
const actions = require('../actions')
const selectors = require('../selectors')
const { SEND_ROUTE } = require('../routes')
+const t = require('../../i18n')
const BalanceComponent = require('./balance-component')
const TxList = require('./tx-list')
@@ -74,25 +75,25 @@ TxView.prototype.renderButtons = function () {
return !selectedToken
? (
h('div.flex-row.flex-center.hero-balance-buttons', [
- h('button.btn-clear.hero-balance-button', {
+ h('button.btn-primary.hero-balance-button', {
onClick: () => showModal({
name: 'DEPOSIT_ETHER',
}),
- }, 'DEPOSIT'),
+ }, t('deposit')),
- h('button.btn-clear.hero-balance-button', {
+ h('button.btn-primary.hero-balance-button', {
style: {
marginLeft: '0.8em',
},
onClick: () => history.push(SEND_ROUTE),
- }, 'SEND'),
+ }, t('send')),
])
)
: (
h('div.flex-row.flex-center.hero-balance-buttons', [
- h('button.btn-clear.hero-balance-button', {
+ h('button.btn-primary.hero-balance-button', {
onClick: () => history.push(SEND_ROUTE),
- }, 'SEND'),
+ }, t('send')),
])
)
}
@@ -106,9 +107,10 @@ TxView.prototype.render = function () {
h('div.flex-row.phone-visible', {
style: {
- margin: '1.5em 1.2em 0',
justifyContent: 'space-between',
alignItems: 'center',
+ flex: '0 0 auto',
+ margin: '10px',
},
}, [
@@ -116,11 +118,10 @@ TxView.prototype.render = function () {
style: {
fontSize: '1.3em',
cursor: 'pointer',
+ padding: '10px',
},
- onClick: () => {
- this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar()
- },
- }, []),
+ onClick: () => this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar(),
+ }),
h('.identicon-wrapper.select-none', {
style: {
diff --git a/ui/app/components/typed-message-renderer.js b/ui/app/components/typed-message-renderer.js
deleted file mode 100644
index d170d63b7..000000000
--- a/ui/app/components/typed-message-renderer.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const extend = require('xtend')
-
-module.exports = TypedMessageRenderer
-
-inherits(TypedMessageRenderer, Component)
-function TypedMessageRenderer () {
- Component.call(this)
-}
-
-TypedMessageRenderer.prototype.render = function () {
- const props = this.props
- const { value, style } = props
- const text = renderTypedData(value)
-
- const defaultStyle = extend({
- width: '315px',
- maxHeight: '210px',
- resize: 'none',
- border: 'none',
- background: 'white',
- padding: '3px',
- overflow: 'scroll',
- }, style)
-
- return (
- h('div.font-small', {
- style: defaultStyle,
- }, text)
- )
-}
-
-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),
- ])
- })
-}
diff --git a/ui/app/components/wallet-content-display.js b/ui/app/components/wallet-content-display.js
deleted file mode 100644
index bfa061be4..000000000
--- a/ui/app/components/wallet-content-display.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = WalletContentDisplay
-
-inherits(WalletContentDisplay, Component)
-function WalletContentDisplay () {
- Component.call(this)
-}
-
-WalletContentDisplay.prototype.render = function () {
- const { title, amount, fiatValue, active, style } = this.props
-
- // TODO: Separate component: wallet-content-account
- return h('div.flex-column', {
- style: {
- marginLeft: '1.3em',
- alignItems: 'flex-start',
- ...style,
- },
- }, [
-
- h('span', {
- style: {
- fontSize: '1.1em',
- },
- }, title),
-
- h('span', {
- style: {
- fontSize: '1.8em',
- margin: '0.4em 0em',
- },
- }, amount),
-
- h('span', {
- style: {
- fontSize: '1.3em',
- },
- }, fiatValue),
-
- active && h('div', {
- style: {
- position: 'absolute',
- marginLeft: '-1.3em',
- height: '6em',
- width: '0.3em',
- background: '#D8D8D8', // $alto
- },
- }, [
- ]),
- ])
-
-}
-
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index ce0902a91..e7c7afc61 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -14,6 +14,7 @@ const BalanceComponent = require('./balance-component')
const TokenList = require('./token-list')
const selectors = require('../selectors')
const { ADD_TOKEN_ROUTE } = require('../routes')
+const t = require('../../i18n')
module.exports = compose(
withRouter,
@@ -122,7 +123,7 @@ WalletView.prototype.render = function () {
onClick: hideSidebar,
}),
- h('div.wallet-view__keyring-label', isLoose ? 'IMPORTED' : ''),
+ h('div.wallet-view__keyring-label.allcaps', isLoose ? t('imported') : ''),
h('div.flex-column.flex-center.wallet-view__name-container', {
style: { margin: '0 auto' },
@@ -139,13 +140,13 @@ WalletView.prototype.render = function () {
selectedIdentity.name,
]),
- h('button.btn-clear.wallet-view__details-button', 'DETAILS'),
+ h('button.btn-clear.wallet-view__details-button.allcaps', t('details')),
]),
]),
h(Tooltip, {
position: 'bottom',
- title: this.state.hasCopied ? 'Copied!' : 'Copy to clipboard',
+ title: this.state.hasCopied ? t('copiedExclamation') : t('copyToClipboard'),
wrapperClassName: 'wallet-view__tooltip',
}, [
h('button.wallet-view__address', {
@@ -173,9 +174,9 @@ WalletView.prototype.render = function () {
h(TokenList),
- h('button.btn-clear.wallet-view__add-token-button', {
+ h('button.btn-primary.wallet-view__add-token-button', {
onClick: () => history.push(ADD_TOKEN_ROUTE),
- }, 'Add Token'),
+ }, t('addToken')),
])
}