aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/app/account-detail.js117
-rw-r--r--ui/app/accounts/new-account/index.js82
-rw-r--r--ui/app/actions.js147
-rw-r--r--ui/app/app.js66
-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
-rw-r--r--ui/app/conf-tx.js27
-rw-r--r--ui/app/conversion-util.js13
-rw-r--r--ui/app/css/itcss/base/index.scss6
-rw-r--r--ui/app/css/itcss/components/account-menu.scss8
-rw-r--r--ui/app/css/itcss/components/add-token.scss189
-rw-r--r--ui/app/css/itcss/components/buttons.scss96
-rw-r--r--ui/app/css/itcss/components/confirm.scss62
-rw-r--r--ui/app/css/itcss/components/currency-display.scss3
-rw-r--r--ui/app/css/itcss/components/header.scss14
-rw-r--r--ui/app/css/itcss/components/hero-balance.scss6
-rw-r--r--ui/app/css/itcss/components/index.scss4
-rw-r--r--ui/app/css/itcss/components/modal.scss149
-rw-r--r--ui/app/css/itcss/components/network.scss20
-rw-r--r--ui/app/css/itcss/components/new-account.scss42
-rw-r--r--ui/app/css/itcss/components/newui-sections.scss44
-rw-r--r--ui/app/css/itcss/components/request-signature.scss35
-rw-r--r--ui/app/css/itcss/components/send.scss63
-rw-r--r--ui/app/css/itcss/components/sender-to-recipient.scss58
-rw-r--r--ui/app/css/itcss/components/settings.scss38
-rw-r--r--ui/app/css/itcss/components/transaction-list.scss55
-rw-r--r--ui/app/css/itcss/components/welcome-screen.scss59
-rw-r--r--ui/app/css/itcss/generic/index.scss68
-rw-r--r--ui/app/css/itcss/generic/reset.scss4
-rw-r--r--ui/app/css/itcss/settings/typography.scss338
-rw-r--r--ui/app/css/itcss/settings/variables.scss8
-rw-r--r--ui/app/first-time/init-menu.js55
-rw-r--r--ui/app/info.js110
-rw-r--r--ui/app/keychains/hd/recover-seed/confirmation.js122
-rw-r--r--ui/app/keychains/hd/restore-vault.js65
-rw-r--r--ui/app/main-container.js14
-rw-r--r--ui/app/reducers/app.js23
-rw-r--r--ui/app/reducers/metamask.js23
-rw-r--r--ui/app/selectors.js8
-rw-r--r--ui/app/send-v2.js106
-rw-r--r--ui/app/send.js547
-rw-r--r--ui/app/template.js30
-rw-r--r--ui/app/token-tracker.js0
-rw-r--r--ui/app/unlock.js126
-rw-r--r--ui/app/welcome-screen.js56
-rw-r--r--ui/i18n.js33
-rw-r--r--ui/index.js1
-rw-r--r--ui/lib/contract-namer.js33
129 files changed, 3914 insertions, 4073 deletions
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
deleted file mode 100644
index 85951f512..000000000
--- a/ui/app/account-detail.js
+++ /dev/null
@@ -1,117 +0,0 @@
-const inherits = require('util').inherits
-const extend = require('xtend')
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const actions = require('./actions')
-const valuesFor = require('./util').valuesFor
-const TransactionList = require('./components/transaction-list')
-const ExportAccountView = require('./components/account-export')
-const TabBar = require('./components/tab-bar')
-const TokenList = require('./components/token-list')
-
-module.exports = connect(mapStateToProps)(AccountDetailScreen)
-
-function mapStateToProps (state) {
- return {
- metamask: state.metamask,
- identities: state.metamask.identities,
- accounts: state.metamask.accounts,
- address: state.metamask.selectedAddress,
- accountDetail: state.appState.accountDetail,
- network: state.metamask.network,
- unapprovedMsgs: valuesFor(state.metamask.unapprovedMsgs),
- shapeShiftTxList: state.metamask.shapeShiftTxList,
- transactions: state.metamask.selectedAddressTxList || [],
- conversionRate: state.metamask.conversionRate,
- currentCurrency: state.metamask.currentCurrency,
- currentAccountTab: state.metamask.currentAccountTab,
- tokens: state.metamask.tokens,
- computedBalances: state.metamask.computedBalances,
- }
-}
-
-inherits(AccountDetailScreen, Component)
-function AccountDetailScreen () {
- Component.call(this)
-}
-
-// Note: This component is no longer used. Leaving the file for reference:
-// - structuring routing for add token
-// - state required for TxList
-// Delete file when those features are complete
-AccountDetailScreen.prototype.render = function () {}
-
-AccountDetailScreen.prototype.subview = function () {
- var subview
- try {
- subview = this.props.accountDetail.subview
- } catch (e) {
- subview = null
- }
-
- switch (subview) {
- case 'transactions':
- return this.tabSections()
- case 'export':
- var state = extend({key: 'export'}, this.props)
- return h(ExportAccountView, state)
- default:
- return this.tabSections()
- }
-}
-
-AccountDetailScreen.prototype.tabSections = function () {
- const { currentAccountTab } = this.props
-
- return h('section.tabSection.full-flex-height.grow-tenx', [
-
- h(TabBar, {
- tabs: [
- { content: 'Sent', key: 'history' },
- { content: 'Tokens', key: 'tokens' },
- ],
- selectedTab: currentAccountTab || 'history',
- onSelect: key => {
- this.props.dispatch(actions.setCurrentAccountTab(key))
- },
- }),
-
- this.tabSwitchView(),
- ])
-}
-
-AccountDetailScreen.prototype.tabSwitchView = function () {
- const props = this.props
- const { address, network } = props
- const { currentAccountTab, tokens } = this.props
-
- switch (currentAccountTab) {
- case 'tokens':
- return h(TokenList, {
- userAddress: address,
- network,
- tokens,
- addToken: () => this.props.dispatch(actions.showAddTokenPage()),
- })
- default:
- return this.transactionList()
- }
-}
-
-AccountDetailScreen.prototype.transactionList = function () {
- const {transactions, unapprovedMsgs, address,
- network, shapeShiftTxList, conversionRate } = this.props
-
- return h(TransactionList, {
- transactions: transactions.sort((a, b) => b.time - a.time),
- network,
- unapprovedMsgs,
- conversionRate,
- address,
- shapeShiftTxList,
- viewPendingTx: (txId) => {
- this.props.dispatch(actions.viewPendingTx(txId))
- },
- })
-}
diff --git a/ui/app/accounts/new-account/index.js b/ui/app/accounts/new-account/index.js
new file mode 100644
index 000000000..854568c77
--- /dev/null
+++ b/ui/app/accounts/new-account/index.js
@@ -0,0 +1,82 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const connect = require('react-redux').connect
+const actions = require('../../actions')
+const t = require('../../../i18n')
+const { getCurrentViewContext } = require('../../selectors')
+const classnames = require('classnames')
+
+const NewAccountCreateForm = require('./create-form')
+const NewAccountImportForm = require('../import')
+
+function mapStateToProps (state) {
+ return {
+ displayedForm: getCurrentViewContext(state),
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ displayForm: form => dispatch(actions.setNewAccountForm(form)),
+ showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
+ showExportPrivateKeyModal: () => {
+ dispatch(actions.showModal({ name: 'EXPORT_PRIVATE_KEY' }))
+ },
+ hideModal: () => dispatch(actions.hideModal()),
+ saveAccountLabel: (address, label) => dispatch(actions.saveAccountLabel(address, label)),
+ }
+}
+
+inherits(AccountDetailsModal, Component)
+function AccountDetailsModal (props) {
+ Component.call(this)
+
+ this.state = {
+ displayedForm: props.displayedForm,
+ }
+}
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailsModal)
+
+AccountDetailsModal.prototype.render = function () {
+ const { displayedForm, displayForm } = this.props
+
+ return h('div.new-account', {}, [
+
+ h('div.new-account__header', [
+
+ h('div.new-account__title', t('newAccount')),
+
+ h('div.new-account__tabs', [
+
+ h('div.new-account__tabs__tab', {
+ className: classnames('new-account__tabs__tab', {
+ 'new-account__tabs__selected': displayedForm === 'CREATE',
+ 'new-account__tabs__unselected cursor-pointer': displayedForm !== 'CREATE',
+ }),
+ onClick: () => displayForm('CREATE'),
+ }, t('createDen')),
+
+ h('div.new-account__tabs__tab', {
+ className: classnames('new-account__tabs__tab', {
+ 'new-account__tabs__selected': displayedForm === 'IMPORT',
+ 'new-account__tabs__unselected cursor-pointer': displayedForm !== 'IMPORT',
+ }),
+ onClick: () => displayForm('IMPORT'),
+ }, t('import')),
+
+ ]),
+
+ ]),
+
+ h('div.new-account__form', [
+
+ displayedForm === 'CREATE'
+ ? h(NewAccountCreateForm)
+ : h(NewAccountImportForm),
+
+ ]),
+
+ ])
+}
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 138479ca4..603261c0f 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -75,6 +75,8 @@ var actions = {
resetAccount,
showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage,
+ CLOSE_WELCOME_SCREEN: 'CLOSE_WELCOME_SCREEN',
+ closeWelcomeScreen,
// seed recovery actions
REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
revealSeedConfirmation: revealSeedConfirmation,
@@ -257,6 +259,9 @@ var actions = {
updateFeatureFlags,
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
+ setMouseUserState,
+ SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE',
+
// Network
setNetworkEndpoints,
updateNetworkEndpointType,
@@ -281,25 +286,43 @@ function goHome () {
// async actions
function tryUnlockMetamask (password) {
- return (dispatch) => {
+ return dispatch => {
dispatch(actions.showLoadingIndication())
dispatch(actions.unlockInProgress())
log.debug(`background.submitPassword`)
return new Promise((resolve, reject) => {
- background.submitPassword(password, (err) => {
- dispatch(actions.hideLoadingIndication())
-
- if (err) {
- dispatch(actions.unlockFailed(err.message))
- reject(err)
- } else {
- dispatch(actions.unlockSucceeded())
- dispatch(actions.transitionForward())
- return forceUpdateMetamaskState(dispatch).then(resolve)
+ background.submitPassword(password, error => {
+ if (error) {
+ return reject(error)
}
+
+ resolve()
})
})
+ .then(() => {
+ dispatch(actions.unlockSucceeded())
+ return forceUpdateMetamaskState(dispatch)
+ })
+ .then(() => {
+ return new Promise((resolve, reject) => {
+ background.verifySeedPhrase(err => {
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ }
+
+ resolve()
+ })
+ })
+ })
+ .then(() => {
+ dispatch(actions.transitionForward())
+ dispatch(actions.hideLoadingIndication())
+ })
+ .catch(err => {
+ dispatch(actions.unlockFailed(err.message))
+ dispatch(actions.hideLoadingIndication())
+ })
}
}
@@ -341,33 +364,38 @@ function createNewVaultAndRestore (password, seed) {
log.debug(`background.createNewVaultAndRestore`)
return new Promise((resolve, reject) => {
- background.createNewVaultAndRestore(password, seed, (err) => {
-
- dispatch(actions.hideLoadingIndication())
-
+ background.createNewVaultAndRestore(password, seed, err => {
if (err) {
- dispatch(actions.displayWarning(err.message))
return reject(err)
}
- dispatch(actions.showAccountsPage())
resolve()
})
})
+ .then(() => dispatch(actions.unMarkPasswordForgotten()))
+ .then(() => {
+ dispatch(actions.showAccountsPage())
+ dispatch(actions.hideLoadingIndication())
+ })
+ .catch(err => {
+ dispatch(actions.displayWarning(err.message))
+ dispatch(actions.hideLoadingIndication())
+ })
}
}
function createNewVaultAndKeychain (password) {
- return (dispatch) => {
+ return dispatch => {
dispatch(actions.showLoadingIndication())
log.debug(`background.createNewVaultAndKeychain`)
return new Promise((resolve, reject) => {
- background.createNewVaultAndKeychain(password, (err) => {
+ background.createNewVaultAndKeychain(password, err => {
if (err) {
dispatch(actions.displayWarning(err.message))
return reject(err)
}
+
log.debug(`background.placeSeedWords`)
background.placeSeedWords((err) => {
@@ -375,12 +403,14 @@ function createNewVaultAndKeychain (password) {
dispatch(actions.displayWarning(err.message))
return reject(err)
}
- dispatch(actions.hideLoadingIndication())
- forceUpdateMetamaskState(dispatch).then(resolve)
+
+ resolve()
})
})
})
-
+ .then(() => forceUpdateMetamaskState(dispatch))
+ .then(() => dispatch(actions.hideLoadingIndication()))
+ .catch(() => dispatch(actions.hideLoadingIndication()))
}
}
@@ -391,18 +421,27 @@ function revealSeedConfirmation () {
}
function requestRevealSeed (password) {
- return (dispatch) => {
+ return dispatch => {
dispatch(actions.showLoadingIndication())
log.debug(`background.submitPassword`)
- background.submitPassword(password, (err) => {
- if (err) {
- return dispatch(actions.displayWarning(err.message))
- }
- log.debug(`background.placeSeedWords`)
- background.placeSeedWords((err, result) => {
- if (err) return dispatch(actions.displayWarning(err.message))
- dispatch(actions.hideLoadingIndication())
- dispatch(actions.showNewVaultSeed(result))
+ return new Promise((resolve, reject) => {
+ background.submitPassword(password, err => {
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
+
+ log.debug(`background.placeSeedWords`)
+ background.placeSeedWords((err, result) => {
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
+
+ dispatch(actions.showNewVaultSeed(result))
+ dispatch(actions.hideLoadingIndication())
+ resolve()
+ })
})
})
}
@@ -673,10 +712,10 @@ function updateSendFrom (from) {
}
}
-function updateSendTo (to) {
+function updateSendTo (to, nickname = '') {
return {
type: actions.UPDATE_SEND_TO,
- value: to,
+ value: { to, nickname },
}
}
@@ -917,7 +956,6 @@ function showRestoreVault () {
function markPasswordForgotten () {
return (dispatch) => {
- dispatch(actions.showLoadingIndication())
return background.markPasswordForgotten(() => {
dispatch(actions.hideLoadingIndication())
dispatch(actions.forgotPassword())
@@ -927,19 +965,21 @@ function markPasswordForgotten () {
}
function unMarkPasswordForgotten () {
- return (dispatch) => {
- dispatch(actions.showLoadingIndication())
- return background.unMarkPasswordForgotten(() => {
- dispatch(actions.hideLoadingIndication())
- dispatch(actions.forgotPassword())
- forceUpdateMetamaskState(dispatch)
+ return dispatch => {
+ return new Promise(resolve => {
+ background.unMarkPasswordForgotten(() => {
+ dispatch(actions.forgotPassword(false))
+ resolve()
+ })
})
+ .then(() => forceUpdateMetamaskState(dispatch))
}
}
-function forgotPassword () {
+function forgotPassword (forgotPasswordState = true) {
return {
type: actions.FORGOT_PASSWORD,
+ value: forgotPasswordState,
}
}
@@ -982,6 +1022,12 @@ function showNewVaultSeed (seed) {
}
}
+function closeWelcomeScreen () {
+ return {
+ type: actions.CLOSE_WELCOME_SCREEN,
+ }
+}
+
function backToUnlockView () {
return {
type: actions.BACK_TO_UNLOCK_VIEW,
@@ -1305,8 +1351,10 @@ function retryTransaction (txId) {
if (err) {
return dispatch(actions.displayWarning(err.message))
}
+ const { selectedAddressTxList } = newState
+ const { id: newTxId } = selectedAddressTxList[selectedAddressTxList.length - 1]
dispatch(actions.updateMetamaskState(newState))
- dispatch(actions.viewPendingTx(txId))
+ dispatch(actions.viewPendingTx(newTxId))
})
}
}
@@ -1575,6 +1623,7 @@ function pairUpdate (coin) {
dispatch(actions.hideWarning())
shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => {
dispatch(actions.hideSubLoadingIndication())
+ if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error))
dispatch({
type: actions.PAIR_UPDATE,
value: {
@@ -1739,6 +1788,13 @@ function updateFeatureFlags (updatedFeatureFlags) {
}
}
+function setMouseUserState (isMouseUser) {
+ return {
+ type: actions.SET_MOUSE_USER_STATE,
+ value: isMouseUser,
+ }
+}
+
// Call Background Then Update
//
// A function generator for a common pattern wherein:
@@ -1773,12 +1829,11 @@ function callBackgroundThenUpdate (method, ...args) {
function forceUpdateMetamaskState (dispatch) {
log.debug(`background.getState`)
-
return new Promise((resolve, reject) => {
background.getState((err, newState) => {
if (err) {
- reject(err)
- return dispatch(actions.displayWarning(err.message))
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
}
dispatch(actions.updateMetamaskState(newState))
diff --git a/ui/app/app.js b/ui/app/app.js
index 168dfa9ac..d114cde09 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -6,6 +6,7 @@ const { compose } = require('recompose')
const h = require('react-hyperscript')
const actions = require('./actions')
const classnames = require('classnames')
+const t = require('../i18n')
// mascara
const MascaraCreatePassword = require('../../mascara/src/app/first-time/create-password-screen').default
@@ -14,8 +15,8 @@ const MascaraNoticeScreen = require('../../mascara/src/app/first-time/notice-scr
const MascaraSeedScreen = require('../../mascara/src/app/first-time/seed-screen').default
const MascaraConfirmSeedScreen = require('../../mascara/src/app/first-time/confirm-seed-screen').default
// init
-const InitializeMenuScreen = require('./first-time/init-menu')
const NewKeyChainScreen = require('./new-keychain')
+
// accounts
const MainContainer = require('./main-container')
const SendTransactionScreen2 = require('./components/send/send-v2-container')
@@ -127,22 +128,32 @@ class App extends Component {
isLoading,
loadingMessage,
network,
+ isMouseUser,
provider,
frequentRpcList,
currentView,
+ setMouseUserState,
} = this.props
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
const loadMessage = loadingMessage || isLoadingNetwork ?
- `Connecting to ${this.getNetworkName()}` : null
+ this.getConnectingLabel() : null
log.debug('Main ui render function')
return (
h('.flex-column.full-height', {
+ className: classnames({ 'mouse-user-styles': isMouseUser }),
style: {
overflowX: 'hidden',
position: 'relative',
alignItems: 'center',
},
+ tabIndex: '0',
+ onClick: () => setMouseUserState(true),
+ onKeyDown: (e) => {
+ if (e.keyCode === 9) {
+ setMouseUserState(false)
+ }
+ },
}, [
// global modal
@@ -236,6 +247,10 @@ class App extends Component {
showNetworkDropdown,
hideNetworkDropdown,
currentView,
+ isInitialized,
+ welcomeScreenSeen,
+ isPopup,
+ betaUI,
} = this.props
if (window.METAMASK_UI_TYPE === 'notification') {
@@ -261,7 +276,7 @@ class App extends Component {
style: {},
}, [
- h('.app-header.flex-row.flex-space-between', {
+ (isInitialized || welcomeScreenSeen || isPopup || !betaUI) && h('.app-header.flex-row.flex-space-between', {
className: classnames({
'app-header--initialized': !isOnboarding,
}),
@@ -282,7 +297,7 @@ class App extends Component {
]),
- h('div.header__right-actions', [
+ betaUI && isInitialized && h('div.header__right-actions', [
h('div.network-component-wrapper', {
style: {},
}, [
@@ -312,6 +327,15 @@ class App extends Component {
]),
]),
+ !isInitialized && !isPopup && betaUI && h('.alpha-warning__container', {}, [
+ h('h2', {
+ className: classnames({
+ 'alpha-warning': welcomeScreenSeen,
+ 'alpha-warning-welcome-screen': !welcomeScreenSeen,
+ }),
+ }, 'Please be aware that this version is still under development'),
+ ]),
+
])
)
}
@@ -567,6 +591,27 @@ class App extends Component {
}
}
+ getConnectingLabel = function () {
+ const { provider } = this.props
+ const providerName = provider.type
+
+ let name
+
+ if (providerName === 'mainnet') {
+ name = t('connectingToMainnet')
+ } else if (providerName === 'ropsten') {
+ name = t('connectingToRopsten')
+ } else if (providerName === 'kovan') {
+ name = t('connectingToRopsten')
+ } else if (providerName === 'rinkeby') {
+ name = t('connectingToRinkeby')
+ } else {
+ name = t('connectingToUnknown')
+ }
+
+ return name
+ }
+
getNetworkName () {
const { provider } = this.props
const providerName = provider.type
@@ -620,6 +665,11 @@ App.propTypes = {
unapprovedMsgCount: PropTypes.number,
unapprovedPersonalMsgCount: PropTypes.number,
unapprovedTypedMessagesCount: PropTypes.number,
+ welcomeScreenSeen: PropTypes.bool,
+ isPopup: PropTypes.bool,
+ betaUI: PropTypes.bool,
+ isMouseUser: PropTypes.bool,
+ setMouseUserState: PropTypes.func,
}
function mapStateToProps (state) {
@@ -663,8 +713,10 @@ function mapStateToProps (state) {
transForward: state.appState.transForward,
isMascara: state.metamask.isMascara,
isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized),
+ isPopup: state.metamask.isPopup,
seedWords: state.metamask.seedWords,
unapprovedTxs,
+ unapprovedMsgs: state.metamask.unapprovedMsgs,
unapprovedMsgCount,
unapprovedPersonalMsgCount,
unapprovedTypedMessagesCount,
@@ -676,6 +728,11 @@ function mapStateToProps (state) {
lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [],
currentCurrency: state.metamask.currentCurrency,
+ isMouseUser: state.appState.isMouseUser,
+ betaUI: state.metamask.featureFlags.betaUI,
+ isRevealingSeedWords: state.metamask.isRevealingSeedWords,
+ Qr: state.appState.Qr,
+ welcomeScreenSeen: state.metamask.welcomeScreenSeen,
// state needed to get account dropdown temporarily rendering from app bar
identities,
@@ -692,6 +749,7 @@ function mapDispatchToProps (dispatch, ownProps) {
hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()),
setCurrentCurrencyToUSD: () => dispatch(actions.setCurrentCurrency('usd')),
toggleAccountMenu: () => dispatch(actions.toggleAccountMenu()),
+ setMouseUserState: (isMouseUser) => dispatch(actions.setMouseUserState(isMouseUser)),
}
}
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')),
])
}
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index 9a323e6c2..fbccfd6c5 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -45,6 +45,7 @@ function mapStateToProps (state) {
unapprovedMsgCount,
unapprovedPersonalMsgCount,
send: state.metamask.send,
+ selectedAddressTxList: state.metamask.selectedAddressTxList,
}
}
@@ -53,19 +54,19 @@ function ConfirmTxScreen () {
Component.call(this)
}
-ConfirmTxScreen.prototype.componentWillMount = function () {
- const { unapprovedTxs = {}, send } = this.props
- const { to } = send
- if (Object.keys(unapprovedTxs).length === 0 && !to) {
- this.props.history.push(DEFAULT_ROUTE)
- }
-}
-
-ConfirmTxScreen.prototype.componentWillReceiveProps = function (nextProps) {
- const { send } = this.props
- const { to } = send
- const { unapprovedTxs = {} } = nextProps
- if (Object.keys(unapprovedTxs).length === 0 && !to) {
+ConfirmTxScreen.prototype.componentDidUpdate = function (prevProps) {
+ const {
+ unapprovedTxs,
+ network,
+ selectedAddressTxList,
+ } = this.props
+ const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps
+ const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network)
+ const prevTxData = prevUnconfTxList[prevIndex] || {}
+ const prevTx = selectedAddressTxList.find(({ id }) => id === prevTxData.id) || {}
+ const unconfTxList = txHelper(unapprovedTxs, {}, {}, {}, network)
+
+ if (prevTx.status === 'dropped' && unconfTxList.length === 0) {
this.props.history.push(DEFAULT_ROUTE)
}
}
diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js
index ee42ebea1..d484ed16d 100644
--- a/ui/app/conversion-util.js
+++ b/ui/app/conversion-util.js
@@ -187,6 +187,18 @@ const conversionGreaterThan = (
return firstValue.gt(secondValue)
}
+const conversionMax = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstIsGreater = conversionGreaterThan(
+ { ...firstProps },
+ { ...secondProps }
+ )
+
+ return firstIsGreater ? firstProps.value : secondProps.value
+}
+
const conversionGTE = (
{ ...firstProps },
{ ...secondProps },
@@ -216,6 +228,7 @@ module.exports = {
conversionGreaterThan,
conversionGTE,
conversionLTE,
+ conversionMax,
toNegative,
subtractCurrencies,
}
diff --git a/ui/app/css/itcss/base/index.scss b/ui/app/css/itcss/base/index.scss
index baa6ea037..1475e8bb5 100644
--- a/ui/app/css/itcss/base/index.scss
+++ b/ui/app/css/itcss/base/index.scss
@@ -1 +1,7 @@
// Base
+
+.mouse-user-styles {
+ button:focus {
+ outline: 0;
+ }
+}
diff --git a/ui/app/css/itcss/components/account-menu.scss b/ui/app/css/itcss/components/account-menu.scss
index 8ad7481c7..c4037d862 100644
--- a/ui/app/css/itcss/components/account-menu.scss
+++ b/ui/app/css/itcss/components/account-menu.scss
@@ -66,8 +66,9 @@
.keyring-label {
margin-top: 5px;
- background-color: $black;
- color: $dusty-gray;
+ background-color: $dusty-gray;
+ color: $black;
+ font-weight: normal;
}
}
@@ -86,7 +87,6 @@
flex: 1 0 auto;
display: flex;
flex-flow: column nowrap;
- padding-top: 4px;
}
&__check-mark {
@@ -114,13 +114,11 @@
color: $white;
font-size: 18px;
font-weight: 300;
- line-height: 16px;
}
&__balance {
color: $dusty-gray;
font-size: 14px;
- line-height: 19px;
}
&__action {
diff --git a/ui/app/css/itcss/components/add-token.scss b/ui/app/css/itcss/components/add-token.scss
index 13020f62f..f5c1de67c 100644
--- a/ui/app/css/itcss/components/add-token.scss
+++ b/ui/app/css/itcss/components/add-token.scss
@@ -1,37 +1,118 @@
.add-token {
width: 498px;
+ max-height: 805px;
display: flex;
flex-flow: column nowrap;
- align-items: center;
position: relative;
z-index: 12;
- font-family: 'DIN Next Light';
+ font-family: 'Roboto';
+ background: white;
+ border-radius: 8px;
&__wrapper {
background-color: $white;
- box-shadow: 0 2px 4px 0 rgba($black, .08);
display: flex;
flex-flow: column nowrap;
align-items: center;
flex: 0 0 auto;
}
- &__title-container {
+ &__header {
display: flex;
flex-flow: column nowrap;
- align-items: center;
- padding: 30px 60px 12px;
- border-bottom: 1px solid $gallery;
+ padding: 16px 16px 0px;
+ border-bottom: 1px solid $geyser;
flex: 0 0 auto;
+
+ &__cancel {
+ color: $dodger-blue;
+ display: flex;
+ align-items: center;
+
+ span {
+ font-family: Roboto;
+ font-size: 16px;
+ line-height: 21px;
+ margin-left: 8px;
+ }
+ }
+
+ &__title {
+ color: $tundora;
+ font-size: 32px;
+ font-weight: 500;
+ margin-top: 4px;
+ }
+
+ &__tabs {
+ margin-left: 22px;
+ display: flex;
+
+ &__tab {
+ height: 54px;
+ padding: 15px 10px;
+ color: $dusty-gray;
+ font-family: Roboto;
+ font-size: 18px;
+ line-height: 24px;
+ text-align: center;
+ }
+
+ &__tab:first-of-type {
+ margin-right: 20px;
+ }
+
+ &__unselected:hover {
+ color: $black;
+ border-bottom: none;
+ }
+
+ &__selected {
+ color: $curious-blue;
+ border-bottom: 3px solid $curious-blue;
+ }
+ }
}
- &__title {
- color: $scorpion;
- font-size: 20px;
- line-height: 26px;
- text-align: center;
- font-weight: 600;
- margin-bottom: 12px;
+ &__info-box {
+ height: 96px;
+ margin: 20px 24px 0px;
+ border-radius: 4px;
+ background-color: $alabaster;
+ position: relative;
+ padding-left: 18px;
+ display: flex;
+ flex-flow: column;
+
+ &__close::after {
+ content: '\00D7';
+ font-size: 29px;
+ font-weight: 200;
+ color: $dusty-gray;
+ position: absolute;
+ right: 17px;
+ cursor: pointer;
+ }
+
+ &__title {
+ color: $mid-gray;
+ font-family: Roboto;
+ font-size: 14px;
+ margin-top: 15px;
+ margin-bottom: 9px;
+ }
+
+ &__copy,
+ &__copy--blue {
+ color: $mid-gray;
+ font-family: Roboto;
+ font-size: 12px;
+ line-height: 18px;
+ }
+
+ &__copy--blue {
+ color: $curious-blue;
+ }
}
&__description {
@@ -48,19 +129,17 @@
&__content-container {
width: 100%;
- border-bottom: 1px solid $gallery;
}
&__input-container {
- padding: 11px 0;
- width: 263px;
- margin: 0 auto;
+ display: flex;
position: relative;
}
&__search-input-error-message {
position: absolute;
bottom: -10px;
+ left: 22px;
font-size: 12px;
width: 100%;
text-overflow: ellipsis;
@@ -69,16 +148,24 @@
color: $red;
}
- &__input {
- width: 100%;
- border: 2px solid $gallery;
+ &__input,
+ &__add-custom-input {
+ height: 54px;
+ padding: 21px 6px;
+ border: 1px solid $geyser;
border-radius: 4px;
- padding: 5px 15px;
- font-size: 14px;
- line-height: 19px;
+ margin: 22px 24px;
+ position: relative;
+ flex: 1 0 auto;
+ color: $scorpion;
+ font-family: Roboto;
+ font-size: 16px;
&::placeholder {
- color: $silver;
+ color: $scorpion;
+ font-family: Roboto;
+ font-size: 16px;
+ line-height: 21px;
}
}
@@ -115,13 +202,14 @@
&__add-custom-form {
display: flex;
flex-flow: column nowrap;
- margin: 8px 0 51px;
+ margin: 40px 0 30px;
}
&__add-custom-field {
- width: 290px;
- margin: 0 auto;
position: relative;
+ display: flex;
+ flex-flow: column;
+ flex: 1 0 auto;
&--error {
.add-token__add-custom-input {
@@ -132,7 +220,8 @@
&__add-custom-error-message {
position: absolute;
- bottom: -21px;
+ bottom: 1px;
+ left: 22px;
font-size: 12px;
width: 100%;
text-overflow: ellipsis;
@@ -144,39 +233,50 @@
&__add-custom-label {
font-size: 16px;
line-height: 21px;
- margin-bottom: 8px;
+ margin-left: 22px;
+ color: $scorpion;
}
&__add-custom-input {
- width: 100%;
- border: 1px solid $silver;
- padding: 5px 15px;
- font-size: 14px;
- line-height: 19px;
+ margin-top: 6px;
+ font-size: 16px;
&::placeholder {
color: $silver;
+ font-size: 16px;
}
}
&__add-custom-field + &__add-custom-field {
- margin-top: 21px;
+ margin-top: 6px;
}
&__buttons {
display: flex;
flex-flow: row nowrap;
- margin: 30px 0 51px;
flex: 0 0 auto;
align-items: center;
justify-content: center;
+ padding-bottom: 30px;
+ padding-top: 20px;
}
- &__button {
- flex: 1 0 141px;
+ &__confirm-button,
+ &__cancel-button {
margin: 0 12px;
- padding: 10px 22px;
+ padding: 10px 13px;
height: 54px;
+ width: 133px;
+ margin-right: 1.2rem;
+ }
+
+ &__token-icons-title {
+ color: #5B5D67;
+ font-family: Roboto;
+ font-size: 18px;
+ line-height: 24px;
+ margin-left: 24px;
+ margin-top: 8px;
}
&__token-icons-container {
@@ -191,7 +291,7 @@
flex: 0 0 42.5%;
align-items: center;
padding: 12px;
- margin: 2.5%;
+ margin: 0% 2.5% 1.5%;
box-sizing: border-box;
border-radius: 10px;
cursor: pointer;
@@ -305,13 +405,14 @@
top: 0;
width: 100%;
overflow: hidden;
- height: 100%;
+ flex: 1 0 auto;
&__wrapper {
box-shadow: none !important;
flex: 1 1 auto;
width: 100%;
- overflow-y: auto;
+ overflow-y: scroll;
+ height: 400px;
}
&__footers {
@@ -334,7 +435,7 @@
}
&__buttons {
- padding: 12px 0;
+ padding: 1rem;
margin: 0;
border-top: 1px solid $gallery;
width: 100%;
diff --git a/ui/app/css/itcss/components/buttons.scss b/ui/app/css/itcss/components/buttons.scss
index 1450b71cc..04e1ed96e 100644
--- a/ui/app/css/itcss/components/buttons.scss
+++ b/ui/app/css/itcss/components/buttons.scss
@@ -2,6 +2,76 @@
Buttons
*/
+.btn-primary,
+.btn-primary--lg,
+.btn-secondary,
+.btn-secondary--lg {
+ background: $white;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ box-sizing: border-box;
+ border-radius: 4px;
+ font-size: 14px;
+ font-weight: 500;
+ transition: border-color .3s ease;
+ padding: 0 20px;
+ min-width: 140px;
+ text-transform: uppercase;
+}
+
+.btn-primary,
+.btn-primary--lg {
+ color: $curious-blue;
+ border: 2px solid $spindle;
+
+ &:active {
+ background: $zumthor;
+ border-color: $curious-blue;
+ }
+
+ &:hover {
+ border-color: $curious-blue;
+ }
+
+ &--disabled,
+ &[disabled] {
+ cursor: auto;
+ opacity: .5;
+ pointer-events: none;
+ }
+}
+
+.btn-secondary,
+.btn-secondary--lg {
+ color: $scorpion;
+ border: 2px solid $dusty-gray;
+
+ &:active {
+ background: $gallery;
+ border-color: $dusty-gray;
+ }
+
+ &:hover {
+ border-color: $scorpion;
+ }
+
+ &--disabled,
+ &[disabled] {
+ cursor: auto;
+ opacity: .5;
+ pointer-events: none;
+ }
+}
+
+.btn-primary, .btn-secondary {
+ height: 44px;
+}
+
+.btn-primary--lg, .btn-secondary--lg {
+ height: 54px;
+}
+
.btn-green {
background-color: #02c9b1; // TODO: reusable color in colors.css
}
@@ -45,6 +115,18 @@
}
}
+.btn-confirm {
+ background-color: $caribbean-green; // TODO: reusable color in colors.css
+ text-align: center;
+ padding: .8rem 1rem;
+ color: $white;
+ border: 2px solid $caribbean-green;
+ border-radius: 4px;
+ font-size: .85rem;
+ font-weight: 400;
+ transition: border-color .3s ease;
+}
+
// No longer used in flat design, remove when modal buttons done
// div.wallet-btn {
// border: 1px solid rgb(91, 93, 103);
@@ -118,20 +200,6 @@ button.btn-thin {
font-size: 13px;
}
-.btn-secondary {
- border: 1px solid #979797;
- border-radius: 2px;
- background-color: $white;
- font-size: 16px;
- line-height: 24px;
- padding: 16px 42px;
-
- &[disabled] {
- background-color: $white !important;
- opacity: .5;
- }
-}
-
.btn-tertiary {
border: 1px solid transparent;
border-radius: 2px;
diff --git a/ui/app/css/itcss/components/confirm.scss b/ui/app/css/itcss/components/confirm.scss
index 255f66e66..abe138f54 100644
--- a/ui/app/css/itcss/components/confirm.scss
+++ b/ui/app/css/itcss/components/confirm.scss
@@ -103,15 +103,13 @@
}
.confirm-screen-back-button {
- background: transparent;
- left: 24px;
+ color: $curious-blue;
+ font-family: Roboto;
+ font-size: 1rem;
position: absolute;
- padding: 6px 12px;
- font-size: .7rem;
-
- @media screen and (max-width: $break-small) {
- margin-right: 12px;
- }
+ top: 38px;
+ right: 38px;
+ background: none;
}
.confirm-screen-account-wrapper {
@@ -204,7 +202,7 @@
}
.confirm-screen-label {
- font-size: 18px;
+ font-size: 16px;
line-height: 40px;
color: $scorpion;
text-align: left;
@@ -231,7 +229,6 @@ section .confirm-screen-account-number,
.confirm-screen-row {
display: flex;
flex-flow: row nowrap;
- border-bottom: 1px solid $alto;
width: 100%;
align-items: center;
padding: 12px;
@@ -239,6 +236,26 @@ section .confirm-screen-account-number,
font-size: 16px;
line-height: 22px;
font-weight: 300;
+
+ &:not(:last-of-type) {
+ border-bottom: 1px solid $alto;
+ }
+}
+
+@media screen and (max-width: 379px) {
+ .confirm-screen-row {
+ span.confirm-screen-section-column {
+ flex: 0.4;
+ }
+
+ div.confirm-screen-section-column {
+ flex: 0.6;
+ }
+
+ .currency-display__input {
+ font-size: 14px;
+ }
+ }
}
.confirm-screen-row-detail {
@@ -249,12 +266,9 @@ section .confirm-screen-account-number,
.confirm-screen-total-box {
background-color: $wild-sand;
- padding: 20px;
- padding-left: 35px;
- border-bottom: 1px solid $alto;
.confirm-screen-label {
- line-height: 18px;
+ line-height: 21px;
}
.confirm-screen-row-detail {
@@ -263,7 +277,7 @@ section .confirm-screen-account-number,
&__subtitle {
font-size: 12px;
- line-height: 22px;
+ line-height: 16px;
}
.confirm-screen-row-info {
@@ -306,21 +320,3 @@ section .confirm-screen-account-number,
font-weight: 300;
margin: 0 5px;
}
-
-#pending-tx-form {
- flex: 1 0 auto;
- position: relative;
- display: flex;
- flex-flow: row nowrap;
- background-color: $white;
- padding: 12px;
- border-bottom-left-radius: 8px;
- border-bottom-right-radius: 8px;
- width: 100%;
-
- @media screen and (max-width: $break-small) {
- border-top: 1px solid $alto;
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
- }
-}
diff --git a/ui/app/css/itcss/components/currency-display.scss b/ui/app/css/itcss/components/currency-display.scss
index 9459629b6..e043c1966 100644
--- a/ui/app/css/itcss/components/currency-display.scss
+++ b/ui/app/css/itcss/components/currency-display.scss
@@ -4,7 +4,7 @@
border: 1px solid $alto;
border-radius: 4px;
background-color: $white;
- color: $dusty-gray;
+ color: $scorpion;
font-family: Roboto;
font-size: 16px;
font-weight: 300;
@@ -52,5 +52,6 @@
&__currency-symbol {
margin-top: 1px;
+ color: $scorpion;
}
} \ No newline at end of file
diff --git a/ui/app/css/itcss/components/header.scss b/ui/app/css/itcss/components/header.scss
index ac2cecf7e..eeed9ee06 100644
--- a/ui/app/css/itcss/components/header.scss
+++ b/ui/app/css/itcss/components/header.scss
@@ -76,6 +76,20 @@
}
}
+.beta-label {
+ font-family: Roboto;
+ text-transform: uppercase;
+ font-weight: 500;
+ font-size: .8rem;
+ color: $buttercup;
+ margin-left: 5px;
+ line-height: initial;
+
+ @media screen and (max-width: 575px) {
+ display: none;
+ }
+}
+
h2.page-subtitle {
text-transform: uppercase;
color: #aeaeae;
diff --git a/ui/app/css/itcss/components/hero-balance.scss b/ui/app/css/itcss/components/hero-balance.scss
index 4af0c2c55..69cde8a0f 100644
--- a/ui/app/css/itcss/components/hero-balance.scss
+++ b/ui/app/css/itcss/components/hero-balance.scss
@@ -5,9 +5,6 @@
flex-direction: column;
justify-content: flex-start;
align-items: center;
- margin: .3em .9em 0;
- // height: 80vh;
- // max-height: 225px;
flex: 0 0 auto;
}
@@ -106,10 +103,11 @@
}
.hero-balance-button {
+ min-width: initial;
width: 6rem;
@media #{$sub-mid-size-breakpoint-range} {
- padding: 0.4rem;
+ padding: .4rem;
width: 4rem;
display: flex;
flex: 1;
diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss
index a1a716b9a..959eb9d15 100644
--- a/ui/app/css/itcss/components/index.scss
+++ b/ui/app/css/itcss/components/index.scss
@@ -57,3 +57,7 @@
@import './new-account.scss';
@import './tooltip.scss';
+
+@import './welcome-screen.scss';
+
+@import './sender-to-recipient.scss';
diff --git a/ui/app/css/itcss/components/modal.scss b/ui/app/css/itcss/components/modal.scss
index 5bca4a07d..9ae3ea7fa 100644
--- a/ui/app/css/itcss/components/modal.scss
+++ b/ui/app/css/itcss/components/modal.scss
@@ -261,7 +261,7 @@
.account-modal__button {
margin-top: 17px;
padding: 10px 22px;
- width: 235px;
+ width: 286px;
}
}
@@ -341,9 +341,8 @@
.export-private-key__button {
margin-top: 17px;
- padding: 10px 22px;
width: 141px;
- height: 54px;
+ min-width: initial;
}
.export-private-key__button--cancel {
@@ -547,38 +546,54 @@
//Notification Modal
-.notification-modal-wrapper {
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- align-items: center;
- position: relative;
- border: 1px solid $alto;
- box-shadow: 0 0 2px 2px $alto;
- font-family: Roboto;
-}
+.notification-modal {
-.notification-modal-header {
- background: $wild-sand;
- width: 100%;
- display: flex;
- justify-content: center;
- padding: 30px;
- font-size: 22px;
- color: $nile-blue;
- height: 79px;
-}
+ &__wrapper {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center;
+ position: relative;
+ border: 1px solid $alto;
+ box-shadow: 0 0 2px 2px $alto;
+ font-family: Roboto;
+ }
-.notification-modal-message {
- padding: 20px;
-}
+ &__header {
+ background: $wild-sand;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ padding: 30px;
+ font-size: 22px;
+ color: $nile-blue;
+ height: 79px;
+ }
-.notification-modal-message {
- width: 100%;
- display: flex;
- justify-content: center;
- font-size: 17px;
- color: $nile-blue;
+ &__message {
+ padding: 20px;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ font-size: 17px;
+ color: $nile-blue;
+ }
+
+ &__buttons {
+ display: flex;
+ justify-content: space-evenly;
+ width: 100%;
+ margin-bottom: 24px;
+ padding: 0px 42px;
+
+ &__btn {
+ cursor: pointer;
+ }
+ }
+
+ &__link {
+ color: $curious-blue;
+ }
}
// Deposit Ether Modal
@@ -625,32 +640,40 @@
&__buy-rows {
width: 100%;
- padding: 33px;
- padding-top: 0px;
+ padding: 0 30px;
display: flex;
flex-flow: column nowrap;
flex: 1;
- overflow-y: auto;
@media screen and (max-width: 575px) {
height: 0;
}
}
+ &__logo {
+ height: 60px;
+ background-repeat: no-repeat;
+ background-size: contain;
+ background-position: center;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
&__buy-row {
border-bottom: 1px solid $alto;
display: flex;
justify-content: space-between;
align-items: center;
- flex: 1;
- padding-bottom: 25px;
- padding-top: 25px;
+ flex: 1 0 auto;
+ padding: 30px 0 20px;
+ min-height: 170px;
@media screen and (max-width: 575px) {
- min-height: 360px;
+ min-height: 270px;
flex-flow: column;
- justify-content: center;
- padding-top: 45px;
+ justify-content: flex-start;
}
&__back {
@@ -676,34 +699,29 @@
}
}
- &__logo {
+ &__logo-container {
display: flex;
justify-content: center;
- flex: 0.3 1 auto;
+ flex: 0 0 auto;
+ padding: 0 20px;
- @media screen and (min-width: 575px) {
- min-width: 215px;
+ @media screen and (min-width: 576px) {
+ width: 12rem;
+ }
+
+ @media screen and (max-width: 575px) {
+ width: 100%;
+ max-height: 6rem;
+ padding-bottom: 20px;
}
}
&__coinbase-logo {
height: 40px;
- width: 180px;
}
&__shapeshift-logo {
height: 60px;
- width: 174px;
- }
-
- &__eth-logo {
- border-radius: 50%;
- width: 68px;
- height: 68px;
- border: 3px solid $tundora;
- z-index: 25;
- padding: 4px;
- background-color: #fff;
}
&__right {
@@ -712,10 +730,11 @@
&__description {
color: $cape-cod;
- flex: 0.5 1 auto;
+ padding-bottom: 20px;
+ align-self: flex-start;
@media screen and (min-width: 575px) {
- min-width: 315px;
+ width: 15rem;
}
&__title {
@@ -745,15 +764,7 @@
}
&__deposit-button, .shapeshift-form__shapeshift-buy-btn {
- height: 54px;
width: 257px;
- border: 1px solid $curious-blue;
- border-radius: 4px;
- display: flex;
- justify-content: center;
- font-size: 16px;
- color: $curious-blue;
- background-color: $white;
}
.shapeshift-form-wrapper {
@@ -771,6 +782,10 @@
width: auto;
flex: 1;
}
+
+ @media screen and (max-width: 575px) {
+ width: auto;
+ }
}
}
diff --git a/ui/app/css/itcss/components/network.scss b/ui/app/css/itcss/components/network.scss
index d9a39b8d5..c34b5cd06 100644
--- a/ui/app/css/itcss/components/network.scss
+++ b/ui/app/css/itcss/components/network.scss
@@ -10,8 +10,9 @@
.network-component.pointer {
border: 2px solid $silver;
border-radius: 82px;
- padding: 3px;
+ padding: 7px 3px;
flex: 0 0 auto;
+ display: flex;
&.ethereum-network .menu-icon-circle div {
background-color: rgba(3, 135, 137, .7) !important;
@@ -76,8 +77,11 @@
.network-name-item {
font-weight: 100;
- flex: 1 0 auto;
+ flex: 1;
color: $dusty-gray;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
}
.network-check,
@@ -155,3 +159,15 @@
.network-caret {
margin: 0 8px 2px;
}
+
+.network-display {
+ &__container {
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+
+ @media screen and (min-width: 576px) {
+ display: none;
+ }
+ }
+}
diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss
index 119d5c479..293579058 100644
--- a/ui/app/css/itcss/components/new-account.scss
+++ b/ui/app/css/itcss/components/new-account.scss
@@ -55,6 +55,16 @@
}
}
+.new-account-import-disclaimer {
+ width: 120%;
+ background-color: #F4F9FC;
+ display: inline-block;
+ align-items: center;
+ padding: 20px 30px 20px;
+ font-size: 12px;
+ line-height: 1.5;
+}
+
.new-account-import-form {
display: flex;
flex-flow: column;
@@ -152,15 +162,14 @@
display: flex;
flex-flow: column;
align-items: center;
+ padding: 30px 30px 0;
&__input-label {
color: $scorpion;
font-family: Roboto;
font-size: 16px;
line-height: 21px;
- margin-top: 29px;
align-self: flex-start;
- margin-left: 30px;
}
&__input {
@@ -181,32 +190,11 @@
margin-top: 39px;
display: flex;
width: 100%;
- justify-content: space-evenly;
+ justify-content: space-between;
}
- &__button-cancel,
- &__button-create {
- height: 55px;
+ &__button {
width: 150px;
- border-radius: 2px;
- background-color: #FFFFFF;
+ min-width: initial;
}
-
- &__button-cancel {
- border: 1px solid $dusty-gray;
- color: $dusty-gray;
- font-family: Roboto;
- font-size: 16px;
- line-height: 21px;
- text-align: center;
- }
-
- &__button-create {
- border: 1px solid $curious-blue;
- color: $curious-blue;
- font-family: Roboto;
- font-size: 16px;
- line-height: 21px;
- text-align: center;
- }
-} \ No newline at end of file
+}
diff --git a/ui/app/css/itcss/components/newui-sections.scss b/ui/app/css/itcss/components/newui-sections.scss
index 73faebe8b..777a82318 100644
--- a/ui/app/css/itcss/components/newui-sections.scss
+++ b/ui/app/css/itcss/components/newui-sections.scss
@@ -51,6 +51,7 @@ $wallet-view-bg: $alabaster;
cursor: pointer;
display: flex;
justify-content: center;
+ padding: 10px;
}
// wallet view and sidebar
@@ -264,7 +265,6 @@ $wallet-view-bg: $alabaster;
.account-name {
font-size: 24px;
font-weight: 300;
- line-height: 20px;
color: $black;
margin-top: 8px;
margin-bottom: .9rem;
@@ -290,3 +290,45 @@ $wallet-view-bg: $alabaster;
.token-balance__amount {
padding-right: 6px;
}
+
+// first time
+.first-view-main {
+ display: flex;
+ flex-direction: row-reverse;
+ justify-content: space-between;
+
+ @media screen and (max-width: 575px) {
+ height: 100%;
+ }
+
+ @media screen and (min-width: 576px) {
+ width: 85vw;
+ }
+
+ @media screen and (min-width: 769px) {
+ width: 80vw;
+ }
+
+ @media screen and (min-width: 1281px) {
+ width: 62vw;
+ }
+}
+
+.unlock-screen-container {
+ z-index: $main-container-z-index;
+ font-family: Roboto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex: 1 0 auto;
+ background: #f7f7f7;
+ width: 100%;
+}
+
+.unlock-screen {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ flex: 1 0 auto;
+}
diff --git a/ui/app/css/itcss/components/request-signature.scss b/ui/app/css/itcss/components/request-signature.scss
index d81099bfa..083481b8f 100644
--- a/ui/app/css/itcss/components/request-signature.scss
+++ b/ui/app/css/itcss/components/request-signature.scss
@@ -162,6 +162,7 @@
&__row {
display: flex;
flex-flow: column;
+ flex: 1 0 auto;
}
&__row-title {
@@ -190,41 +191,19 @@
width: 100%;
display: flex;
align-items: center;
- justify-content: space-evenly;
+ justify-content: center;
font-size: 22px;
position: relative;
flex: 0 0 auto;
border-top: 1px solid $geyser;
+ padding: 1.6rem;
- &__cancel-button,
- &__sign-button {
- display: flex;
- align-items: center;
- justify-content: center;
- flex: 1 0 auto;
- font-family: Roboto;
- font-size: 16px;
- font-weight: 300;
- height: 55px;
- line-height: 32px;
- cursor: pointer;
- border-radius: 2px;
- box-shadow: none;
- max-width: 162px;
- margin: 12px;
+ button {
+ width: 165px;
}
&__cancel-button {
- background: none;
- border: 1px solid $dusty-gray;
- margin-right: 6px;
- }
-
- &__sign-button {
- background-color: $caribbean-green;
- border-width: 0;
- color: $white;
- margin-left: 6px;
+ margin-right: 1.2rem;
}
}
-} \ No newline at end of file
+}
diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss
index fd73275e0..89d2be891 100644
--- a/ui/app/css/itcss/components/send.scss
+++ b/ui/app/css/itcss/components/send.scss
@@ -526,14 +526,10 @@
}
&__form {
- padding: 13px 0;
- width: 100%;
- overflow-y: auto;
+ padding: 10px 0 25px;
@media screen and (max-width: $break-small) {
- padding: 13px 0;
margin: 0;
- overflow-y: auto;
flex: 1 1 auto;
}
}
@@ -557,6 +553,25 @@
&__form-field {
flex: 1 1 auto;
+
+ .currency-display {
+ color: $tundora;
+
+ &__currency-symbol {
+ color: $tundora;
+ }
+
+ &__converted-value,
+ &__converted-currency {
+ color: $tundora;
+ }
+ }
+
+ .account-list-item {
+ &__account-secondary-balance {
+ color: $tundora;
+ }
+ }
}
&__form-label {
@@ -565,6 +580,7 @@
font-size: 16px;
line-height: 22px;
width: 88px;
+ font-weight: 400;
}
&__from-dropdown {
@@ -620,7 +636,7 @@
border: 1px solid $alto;
border-radius: 4px;
background-color: $white;
- color: $dusty-gray;
+ color: $tundora;
padding: 10px;
font-family: Roboto;
font-size: 16px;
@@ -640,6 +656,13 @@
&__gas-fee-display {
width: 100%;
+ position: relative;
+
+ .currency-display--message {
+ padding: 8px 38px 8px 10px;
+ display: flex;
+ align-items: center;
+ }
}
&__sliders-icon-container {
@@ -759,7 +782,6 @@
&__buttons {
display: flex;
justify-content: space-between;
- width: 181.75px;
margin-right: 21.25px;
}
@@ -777,13 +799,8 @@
}
&__cancel, &__save, &__save__error {
- height: 34.64px;
width: 85.74px;
- border: 1px solid $dusty-gray;
- border-radius: 2px;
- font-family: 'DIN OT';
- font-size: 12px;
- color: $dusty-gray;
+ min-width: initial;
}
&__save__error {
@@ -865,3 +882,23 @@
}
}
}
+
+.sliders-icon-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 24px;
+ width: 24px;
+ border: 1px solid $curious-blue;
+ border-radius: 4px;
+ background-color: $white;
+ position: absolute;
+ right: 15px;
+ top: 14px;
+ cursor: pointer;
+ font-size: 1em;
+}
+
+.sliders-icon {
+ color: $curious-blue;
+} \ No newline at end of file
diff --git a/ui/app/css/itcss/components/sender-to-recipient.scss b/ui/app/css/itcss/components/sender-to-recipient.scss
new file mode 100644
index 000000000..f16013cdf
--- /dev/null
+++ b/ui/app/css/itcss/components/sender-to-recipient.scss
@@ -0,0 +1,58 @@
+.sender-to-recipient {
+ &__container {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ border-bottom: 1px solid $geyser;
+ position: relative;
+ }
+
+ &__sender,
+ &__recipient {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ flex: 1;
+ padding: 10px 20px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ &__sender {
+ padding-right: 30px;
+ }
+
+ &__recipient {
+ border-left: 1px solid $geyser;
+ padding-left: 30px;
+ }
+
+ &__arrow-container {
+ position: absolute;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ &__arrow-circle {
+ background: $white;
+ padding: 5px;
+ border: 1px solid $geyser;
+ border-radius: 20px;
+ height: 30px;
+ width: 30px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ &__name {
+ padding-left: 5px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+}
diff --git a/ui/app/css/itcss/components/settings.scss b/ui/app/css/itcss/components/settings.scss
index d60ebd934..dcc9b98d5 100644
--- a/ui/app/css/itcss/components/settings.scss
+++ b/ui/app/css/itcss/components/settings.scss
@@ -130,24 +130,32 @@
cursor: pointer;
}
-.settings__clear-button {
- font-size: 16px;
- border: 1px solid $curious-blue;
- color: $curious-blue;
- border-radius: 2px;
- padding: 18px;
- background-color: $white;
- text-transform: uppercase;
-}
-
-.settings__clear-button--red {
- border: 1px solid $monzo;
+.settings__button--red {
+ border-color: lighten($monzo, 10%);
color: $monzo;
+
+ &:active {
+ background: lighten($monzo, 55%);
+ border-color: $monzo;
+ }
+
+ &:hover {
+ border-color: $monzo;
+ }
}
-.settings__clear-button--orange {
- border: 1px solid rgba(247, 134, 28, 1);
- color: rgba(247, 134, 28, 1);
+.settings__button--orange {
+ border-color: lighten($ecstasy, 20%);
+ color: $ecstasy;
+
+ &:active {
+ background: lighten($ecstasy, 40%);
+ border-color: $ecstasy;
+ }
+
+ &:hover {
+ border-color: $ecstasy;
+ }
}
.settings__info-logo-wrapper {
diff --git a/ui/app/css/itcss/components/transaction-list.scss b/ui/app/css/itcss/components/transaction-list.scss
index c3df493df..d03faf486 100644
--- a/ui/app/css/itcss/components/transaction-list.scss
+++ b/ui/app/css/itcss/components/transaction-list.scss
@@ -97,7 +97,7 @@
.tx-list-content-wrapper {
align-items: stretch;
- margin-bottom: 4px;
+ margin: 4px 0;
flex: 1 0 auto;
width: 100%;
display: flex;
@@ -126,6 +126,54 @@
}
}
+.tx-list-item-retry-container {
+ background: #d1edff;
+ width: 100%;
+ border-radius: 4px;
+ font-size: 0.8em;
+ display: flex;
+ justify-content: center;
+ margin-left: 44px;
+ width: calc(100% - 44px);
+
+ @media screen and (min-width: 576px) and (max-width: 679px) {
+ flex-flow: column;
+ align-items: center;
+ }
+
+ @media screen and (min-width: 380px) and (max-width: 575px) {
+ flex-flow: row;
+ }
+
+ @media screen and (max-width: 379px) {
+ flex-flow: column;
+ align-items: center;
+ }
+}
+
+.tx-list-item-retry-copy {
+ font-family: Roboto;
+}
+
+.tx-list-item-retry-link {
+ text-decoration: underline;
+ margin-left: 6px;
+ cursor: pointer;
+
+ @media screen and (min-width: 576px) and (max-width: 679px) {
+ margin-left: 0px;
+ }
+
+ @media screen and (min-width: 380px) and (max-width: 575px) {
+ margin-left: 6px;
+ }
+
+ @media screen and (max-width: 379px) {
+ margin-left: 0px;
+ text-align: center;
+ }
+}
+
.tx-list-date {
color: $dusty-gray;
font-size: 12px;
@@ -136,6 +184,7 @@
align-self: center;
flex: 0 0 auto;
margin-right: 16px;
+ display: flex;
}
.tx-list-account-and-status-wrapper {
@@ -189,6 +238,10 @@
.tx-list-status--failed {
color: $monzo;
}
+
+ .tx-list-status--dropped {
+ opacity: 0.5;
+ }
}
.tx-list-item {
diff --git a/ui/app/css/itcss/components/welcome-screen.scss b/ui/app/css/itcss/components/welcome-screen.scss
new file mode 100644
index 000000000..bfd174ad9
--- /dev/null
+++ b/ui/app/css/itcss/components/welcome-screen.scss
@@ -0,0 +1,59 @@
+.welcome-screen {
+ display: flex;
+ flex-flow: column;
+ justify-content: center;
+ align-items: center;
+ font-family: Roboto;
+ font-weight: 400;
+ width: 100%;
+ flex: 1 0 auto;
+ padding: 70px 0;
+ background: $white;
+
+ @media screen and (max-width: 575px) {
+ padding: 0;
+ }
+
+ &__info {
+ display: flex;
+ flex-flow: column;
+ width: 100%;
+ height: 100%;
+ align-items: center;
+
+ &__header {
+ font-size: 1.65em;
+ margin-bottom: 14px;
+
+ @media screen and (max-width: 575px) {
+ font-size: 1.5em;
+ }
+ }
+
+ &__copy {
+ font-size: 1em;
+ width: 400px;
+ max-width: 90vw;
+ text-align: center;
+
+ @media screen and (max-width: 575px) {
+ font-size: 0.9em;
+ }
+ }
+ }
+
+ &__button {
+ height: 54px;
+ width: 198px;
+ box-shadow: 0 2px 4px 0 rgba(0,0,0,0.14);
+ color: #FFFFFF;
+ font-size: 20px;
+ font-weight: 500;
+ line-height: 26px;
+ text-align: center;
+ text-transform: uppercase;
+ margin: 35px 0 14px;
+ transition: 200ms ease-in-out;
+ background-color: rgba(247, 134, 28, 0.9);
+ }
+}
diff --git a/ui/app/css/itcss/generic/index.scss b/ui/app/css/itcss/generic/index.scss
index 75f823320..92321394b 100644
--- a/ui/app/css/itcss/generic/index.scss
+++ b/ui/app/css/itcss/generic/index.scss
@@ -13,7 +13,6 @@ body {
font-family: Roboto, Arial;
color: #4d4d4d;
font-weight: 300;
- line-height: 1.4em;
background: #f7f7f7;
width: 100%;
height: 100%;
@@ -70,6 +69,10 @@ input.large-input {
height: 36px;
}
+.allcaps {
+ text-transform: uppercase;
+}
+
.page-container {
width: 400px;
background-color: $white;
@@ -77,13 +80,36 @@ input.large-input {
z-index: 25;
display: flex;
flex-flow: column;
+ border-radius: 7px;
&__header {
display: flex;
flex-flow: column;
border-bottom: 1px solid $geyser;
- padding: 1.6rem 1rem;
+ padding: 20px;
flex: 0 0 auto;
+ position: relative;
+ }
+
+ &__header-close {
+ color: $tundora;
+ position: absolute;
+ top: 20px;
+ right: 20px;
+ cursor: pointer;
+ overflow: hidden;
+
+ &::after {
+ content: '\00D7';
+ font-size: 40px;
+ line-height: 20px;
+ }
+ }
+
+ &__header-row {
+ padding-bottom: 10px;
+ display: flex;
+ justify-content: space-between;
}
&__footer {
@@ -93,32 +119,46 @@ input.large-input {
border-top: 1px solid $geyser;
padding: 1.6rem;
flex: 0 0 auto;
+
+ .btn-clear,
+ .btn-cancel,
+ .btn-confirm {
+ font-size: 1rem;
+ }
}
&__footer-button {
width: 165px;
- height: 60px;
+ height: 55px;
font-size: 1rem;
text-transform: uppercase;
- margin-right: 1rem;
+ margin-right: 1.2rem;
+ border-radius: 2px;
&:last-of-type {
margin-right: 0;
}
}
+ &__back-button {
+ color: #2f9ae0;
+ font-size: 1rem;
+ cursor: pointer;
+ font-weight: 400;
+ }
+
&__title {
- color: $tundora;
- font-family: Roboto;
+ color: $black;
font-size: 2rem;
font-weight: 500;
- line-height: initial;
+ line-height: 2rem;
}
&__subtitle {
padding-top: .5rem;
line-height: initial;
font-size: .9rem;
+ color: $gray;
}
&__tabs {
@@ -155,6 +195,19 @@ input.large-input {
}
}
}
+
+ &--full-width {
+ width: initial;
+ }
+
+ &--full-height {
+ height: 100%;
+ }
+
+ &__content {
+ height: 100%;
+ overflow-y: auto;
+ }
}
@media screen and (max-width: 250px) {
@@ -181,5 +234,6 @@ input.large-input {
width: 100%;
overflow-y: auto;
background-color: $white;
+ border-radius: 0;
}
}
diff --git a/ui/app/css/itcss/generic/reset.scss b/ui/app/css/itcss/generic/reset.scss
index e054d533e..a417a0453 100644
--- a/ui/app/css/itcss/generic/reset.scss
+++ b/ui/app/css/itcss/generic/reset.scss
@@ -112,10 +112,6 @@ section {
display: block;
}
-body {
- line-height: 1;
-}
-
ol,
ul {
list-style: none;
diff --git a/ui/app/css/itcss/settings/typography.scss b/ui/app/css/itcss/settings/typography.scss
index ac8c41336..8a56d9c6c 100644
--- a/ui/app/css/itcss/settings/typography.scss
+++ b/ui/app/css/itcss/settings/typography.scss
@@ -1,6 +1,340 @@
-@import url('https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900');
+@import url('/fonts/Font_Awesome/font-awesome.min.css');
-@import url('https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css');
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 100;
+ src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 100;
+ src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 100;
+ src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 100;
+ src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 100;
+ src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 100;
+ src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 100;
+ src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 900;
+ src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 900;
+ src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
+ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 900;
+ src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 900;
+ src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
+ unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 900;
+ src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
+ unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 900;
+ src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 900;
+ src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
@font-face {
font-family: 'Montserrat Regular';
diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss
index 4c0972527..51548306f 100644
--- a/ui/app/css/itcss/settings/variables.scss
+++ b/ui/app/css/itcss/settings/variables.scss
@@ -46,6 +46,14 @@ $manatee: #93949d;
$spindle: #c7ddec;
$mid-gray: #5b5d67;
$cape-cod: #38393a;
+$onahau: #d1edff;
+$java: #29b6af;
+$wild-strawberry: #ff4a8d;
+$cornflower-blue: #7057ff;
+$saffron: #f6c343;
+$dodger-blue: #3099f2;
+$zumthor: #edf7ff;
+$ecstasy: #f7861c;
/*
Z-Indicies
diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js
index addcedc77..8d63d5970 100644
--- a/ui/app/first-time/init-menu.js
+++ b/ui/app/first-time/init-menu.js
@@ -6,8 +6,11 @@ const PropTypes = require('prop-types')
const Mascot = require('../components/mascot')
const actions = require('../actions')
const Tooltip = require('../components/tooltip')
+const t = require('../../i18n')
const getCaretCoordinates = require('textarea-caret')
const { RESTORE_VAULT_ROUTE, DEFAULT_ROUTE } = require('../routes')
+const environmentType = require('../../../app/scripts/lib/environment-type')
+const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
class InitializeMenuScreen extends Component {
constructor (props) {
@@ -31,7 +34,6 @@ class InitializeMenuScreen extends Component {
}
render () {
- const { history } = this.props
const { warning } = this.state
return (
@@ -48,8 +50,7 @@ class InitializeMenuScreen extends Component {
color: '#7F8082',
marginBottom: 10,
},
- }, 'MetaMask'),
-
+ }, t('appName')),
h('div', [
h('h3', {
@@ -58,10 +59,10 @@ class InitializeMenuScreen extends Component {
color: '#7F8082',
display: 'inline',
},
- }, 'Encrypt your new DEN'),
+ }, t('encryptNewDen')),
h(Tooltip, {
- title: 'Your DEN is your password-encrypted storage within MetaMask.',
+ title: t('denExplainer'),
}, [
h('i.fa.fa-question-circle.pointer', {
style: {
@@ -81,7 +82,7 @@ class InitializeMenuScreen extends Component {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
- placeholder: 'New Password (min 8 chars)',
+ placeholder: t('newPassword'),
onInput: this.inputChanged.bind(this),
style: {
width: 260,
@@ -93,7 +94,7 @@ class InitializeMenuScreen extends Component {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box-confirm',
- placeholder: 'Confirm Password',
+ placeholder: t('confirmPassword'),
onKeyPress: this.createVaultOnEnter.bind(this),
onInput: this.inputChanged.bind(this),
style: {
@@ -108,17 +109,29 @@ class InitializeMenuScreen extends Component {
style: {
margin: 12,
},
- }, 'Create'),
+ }, t('createDen')),
h('.flex-row.flex-center.flex-grow', [
h('p.pointer', {
- onClick: () => history.push(RESTORE_VAULT_ROUTE),
+ onClick: () => this.showRestoreVault(),
style: {
fontSize: '0.8em',
color: 'rgb(247, 134, 28)',
textDecoration: 'underline',
},
- }, 'Import Existing DEN'),
+ }, t('importDen')),
+ ]),
+
+ h('.flex-row.flex-center.flex-grow', [
+ h('p.pointer', {
+ onClick: this.showOldUI.bind(this),
+ style: {
+ fontSize: '0.8em',
+ color: '#aeaeae',
+ textDecoration: 'underline',
+ marginTop: '32px',
+ },
+ }, 'Use classic interface'),
]),
])
@@ -142,11 +155,12 @@ class InitializeMenuScreen extends Component {
this.setState({ warning: null })
if (password.length < 8) {
- this.setState({ warning: 'password not long enough' })
+ this.setState({ warning: t('passwordShort') })
return
}
+
if (password !== passwordConfirm) {
- this.setState({ warning: 'passwords don\'t match' })
+ this.setState({ warning: t('passwordMismatch') })
return
}
@@ -164,6 +178,20 @@ class InitializeMenuScreen extends Component {
y: boundingRect.top + coordinates.top - element.scrollTop,
})
}
+
+ showRestoreVault () {
+ this.props.markPasswordForgotten()
+ if (environmentType() === 'popup') {
+ global.platform.openExtensionInBrowser()
+ }
+
+ this.props.history.push(RESTORE_VAULT_ROUTE)
+ }
+
+ showOldUI () {
+ this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
+ .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
+ }
}
InitializeMenuScreen.propTypes = {
@@ -171,6 +199,8 @@ InitializeMenuScreen.propTypes = {
isInitialized: PropTypes.bool,
isUnlocked: PropTypes.bool,
createNewVaultAndKeychain: PropTypes.func,
+ markPasswordForgotten: PropTypes.func,
+ dispatch: PropTypes.func,
}
const mapStateToProps = state => {
@@ -185,6 +215,7 @@ const mapStateToProps = state => {
const mapDispatchToProps = dispatch => {
return {
createNewVaultAndKeychain: password => dispatch(actions.createNewVaultAndKeychain(password)),
+ markPasswordForgotten: () => dispatch(actions.markPasswordForgotten()),
}
}
diff --git a/ui/app/info.js b/ui/app/info.js
deleted file mode 100644
index 17e1ecbb4..000000000
--- a/ui/app/info.js
+++ /dev/null
@@ -1,110 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-
-module.exports = connect(mapStateToProps)(InfoScreen)
-
-function mapStateToProps (state) {
- return {}
-}
-
-inherits(InfoScreen, Component)
-function InfoScreen () {
- Component.call(this)
-}
-
-InfoScreen.prototype.renderLogo = function () {
- return (
- h('div.settings__info-logo-wrapper', [
- h('img.settings__info-logo', { src: 'images/info-logo.png' }),
- ])
- )
-}
-
-InfoScreen.prototype.renderInfoLinks = function () {
- return (
- h('div.settings__content-item.settings__content-item--without-height', [
- h('div.settings__info-link-header', 'Links'),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://metamask.io/privacy.html',
- target: '_blank',
- }, [
- h('span.settings__info-link', 'Privacy Policy'),
- ]),
- ]),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://metamask.io/terms.html',
- target: '_blank',
- }, [
- h('span.settings__info-link', 'Terms of Use'),
- ]),
- ]),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://metamask.io/attributions.html',
- target: '_blank',
- }, [
- h('span.settings__info-link', 'Attributions'),
- ]),
- ]),
- h('hr.settings__info-separator'),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://support.metamask.io',
- target: '_blank',
- }, [
- h('span.settings__info-link', 'Visit our Support Center'),
- ]),
- ]),
- h('div.settings__info-link-item', [
- h('a', {
- href: 'https://metamask.io/',
- target: '_blank',
- }, [
- h('span.settings__info-link', 'Visit our web site'),
- ]),
- ]),
- h('div.settings__info-link-item', [
- h('a', {
- target: '_blank',
- href: 'mailto:help@metamask.io?subject=Feedback',
- }, [
- h('span.settings__info-link', 'Email us!'),
- ]),
- ]),
- ])
- )
-}
-
-InfoScreen.prototype.render = function () {
- const version = global.platform.getVersion()
-
- return (
- h('div.settings__content', [
- h('div.settings__content-row', [
- h('div.settings__content-item.settings__content-item--without-height', [
- this.renderLogo(),
- h('div.settings__info-item', [
- h('div.settings__info-version-header', 'MetaMask Version'),
- h('div.settings__info-version-number', `${version}`),
- ]),
- h('div.settings__info-item', [
- h(
- 'div.settings__info-about',
- 'MetaMask is designed and built in California.'
- ),
- ]),
- ]),
- this.renderInfoLinks(),
- ]),
- ])
- )
-}
-
-InfoScreen.prototype.navigateTo = function (url) {
- global.platform.openWindow({ url })
-}
-
diff --git a/ui/app/keychains/hd/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js
new file mode 100644
index 000000000..bc5339549
--- /dev/null
+++ b/ui/app/keychains/hd/recover-seed/confirmation.js
@@ -0,0 +1,122 @@
+const inherits = require('util').inherits
+
+const Component = require('react').Component
+const connect = require('react-redux').connect
+const h = require('react-hyperscript')
+const actions = require('../../../actions')
+const t = require('../../../../i18n')
+
+module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
+
+inherits(RevealSeedConfirmation, Component)
+function RevealSeedConfirmation () {
+ Component.call(this)
+}
+
+function mapStateToProps (state) {
+ return {
+ warning: state.appState.warning,
+ }
+}
+
+RevealSeedConfirmation.prototype.render = function () {
+ const props = this.props
+
+ return (
+
+ h('.initialize-screen.flex-column.flex-center.flex-grow', {
+ style: { maxWidth: '420px' },
+ }, [
+
+ h('h3.flex-center.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: 24,
+ width: '100%',
+ fontSize: '20px',
+ padding: 6,
+ },
+ }, [
+ 'Reveal Seed Words',
+ ]),
+
+ h('.div', {
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ padding: '20px',
+ justifyContent: 'center',
+ },
+ }, [
+
+ h('h4', t('revealSeedWordsWarning')),
+
+ // confirmation
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: t('enterPasswordConfirm'),
+ onKeyPress: this.checkConfirmation.bind(this),
+ style: {
+ width: 260,
+ marginTop: '12px',
+ },
+ }),
+
+ h('.flex-row.flex-start', {
+ style: {
+ marginTop: 30,
+ width: '50%',
+ },
+ }, [
+ // cancel
+ h('button.primary', {
+ onClick: this.goHome.bind(this),
+ }, 'CANCEL'),
+
+ // submit
+ h('button.primary', {
+ style: { marginLeft: '10px' },
+ onClick: this.revealSeedWords.bind(this),
+ }, 'OK'),
+
+ ]),
+
+ (props.warning) && (
+ h('span.error', {
+ style: {
+ margin: '20px',
+ },
+ }, props.warning.split('-'))
+ ),
+
+ props.inProgress && (
+ h('span.in-progress-notification', t('generatingSeed'))
+ ),
+ ]),
+ ])
+ )
+}
+
+RevealSeedConfirmation.prototype.componentDidMount = function () {
+ document.getElementById('password-box').focus()
+}
+
+RevealSeedConfirmation.prototype.goHome = function () {
+ this.props.dispatch(actions.showConfigPage(false))
+}
+
+// create vault
+
+RevealSeedConfirmation.prototype.checkConfirmation = function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ this.revealSeedWords()
+ }
+}
+
+RevealSeedConfirmation.prototype.revealSeedWords = function () {
+ var password = document.getElementById('password-box').value
+ this.props.dispatch(actions.requestRevealSeed(password))
+}
diff --git a/ui/app/keychains/hd/restore-vault.js b/ui/app/keychains/hd/restore-vault.js
index a4ed137f9..5e4e004cf 100644
--- a/ui/app/keychains/hd/restore-vault.js
+++ b/ui/app/keychains/hd/restore-vault.js
@@ -2,6 +2,7 @@ const inherits = require('util').inherits
const PersistentForm = require('../../../lib/persistent-form')
const connect = require('react-redux').connect
const h = require('react-hyperscript')
+const t = require('../../../i18n')
const actions = require('../../actions')
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
@@ -36,23 +37,23 @@ RestoreVaultScreen.prototype.render = function () {
padding: 6,
},
}, [
- 'Restore Vault',
+ t('restoreVault'),
]),
// wallet seed entry
- h('h3', 'Wallet Seed'),
+ h('h3', t('walletSeed')),
h('textarea.twelve-word-phrase.letter-spacey', {
dataset: {
persistentFormId: 'wallet-seed',
},
- placeholder: 'Enter your secret twelve word phrase here to restore your vault.',
+ placeholder: t('secretPhrase'),
}),
// password
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
- placeholder: 'New Password (min 8 chars)',
+ placeholder: t('newPassword8Chars'),
dataset: {
persistentFormId: 'password',
},
@@ -66,7 +67,7 @@ RestoreVaultScreen.prototype.render = function () {
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box-confirm',
- placeholder: 'Confirm Password',
+ placeholder: t('confirmPassword'),
onKeyPress: this.createOnEnter.bind(this),
dataset: {
persistentFormId: 'password-confirmation',
@@ -93,26 +94,33 @@ RestoreVaultScreen.prototype.render = function () {
// cancel
h('button.primary', {
onClick: this.showInitializeMenu.bind(this),
- }, 'CANCEL'),
+ style: {
+ textTransform: 'uppercase',
+ },
+ }, t('cancel')),
// submit
h('button.primary', {
onClick: this.createNewVaultAndRestore.bind(this),
- }, 'OK'),
-
+ style: {
+ textTransform: 'uppercase',
+ },
+ }, t('ok')),
]),
])
-
)
}
RestoreVaultScreen.prototype.showInitializeMenu = function () {
- this.props.dispatch(actions.unMarkPasswordForgotten())
- if (this.props.forgottenPassword) {
- this.props.dispatch(actions.backToUnlockView())
- } else {
- this.props.dispatch(actions.showInitializeMenu())
- }
+ const { dispatch, forgottenPassword } = this.props
+ dispatch(actions.unMarkPasswordForgotten())
+ .then(() => {
+ if (forgottenPassword) {
+ dispatch(actions.backToUnlockView())
+ } else {
+ dispatch(actions.showInitializeMenu())
+ }
+ })
}
RestoreVaultScreen.prototype.createOnEnter = function (event) {
@@ -128,21 +136,34 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
var passwordConfirmBox = document.getElementById('password-box-confirm')
var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) {
- this.warning = 'Password not long enough'
+ this.warning = t('passwordNotLongEnough')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (password !== passwordConfirm) {
- this.warning = 'Passwords don\'t match'
+ this.warning = t('passwordsDontMatch')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
// check seed
var seedBox = document.querySelector('textarea.twelve-word-phrase')
var seed = seedBox.value.trim()
+
+ // true if the string has more than a space between words.
+ if (seed.split(' ').length > 1) {
+ this.warning = t('spaceBetween')
+ this.props.dispatch(actions.displayWarning(this.warning))
+ return
+ }
+ // true if seed contains a character that is not between a-z or a space
+ if (!seed.match(/^[a-z ]+$/)) {
+ this.warning = t('loweCaseWords')
+ this.props.dispatch(actions.displayWarning(this.warning))
+ return
+ }
if (seed.split(' ').length !== 12) {
- this.warning = 'seed phrases are 12 words long'
+ this.warning = t('seedPhraseReq')
this.props.dispatch(actions.displayWarning(this.warning))
return
}
@@ -150,11 +171,5 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
this.warning = null
this.props.dispatch(actions.displayWarning(this.warning))
this.props.dispatch(actions.createNewVaultAndRestore(password, seed))
- .then(() => {
- this.props.dispatch(actions.unMarkPasswordForgotten())
- })
- .catch((err) => {
- log.error(err.message)
- })
-
+ .catch(err => log.error(err.message))
}
diff --git a/ui/app/main-container.js b/ui/app/main-container.js
index 4fea3a6d1..6dd4ff151 100644
--- a/ui/app/main-container.js
+++ b/ui/app/main-container.js
@@ -32,19 +32,7 @@ MainContainer.prototype.render = function () {
return h(Settings, {key: 'config'})
default:
log.debug('rendering locked screen')
- contents = {
- component: UnlockScreen,
- style: {
- boxShadow: 'none',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- background: '#F7F7F7',
- // must force 100%, because lock screen is full-width
- width: '100%',
- },
- key: 'locked',
- }
+ return h('.unlock-screen-container', {}, h(UnlockScreen, { key: 'locked' }))
}
}
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index 6885d029a..74a0f9299 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -59,6 +59,7 @@ function reduceApp (state, action) {
// Used to display error text
warning: null,
buyView: {},
+ isMouseUser: false,
}, state.appState)
switch (action.type) {
@@ -137,14 +138,18 @@ function reduceApp (state, action) {
})
case actions.FORGOT_PASSWORD:
- return extend(appState, {
- currentView: {
- name: 'restoreVault',
- },
- transForward: false,
- forgottenPassword: true,
+ const newState = extend(appState, {
+ forgottenPassword: action.value,
})
+ if (action.value) {
+ newState.currentView = {
+ name: 'restoreVault',
+ }
+ }
+
+ return newState
+
case actions.SHOW_INIT_MENU:
return extend(appState, {
currentView: defaultView,
@@ -658,6 +663,12 @@ function reduceApp (state, action) {
data: action.value.data,
},
})
+
+ case actions.SET_MOUSE_USER_STATE:
+ return extend(appState, {
+ isMouseUser: action.value,
+ })
+
default:
return appState
}
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index 294c29948..9cba5e83b 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -1,6 +1,7 @@
const extend = require('xtend')
const actions = require('../actions')
const MetamascaraPlatform = require('../../../app/scripts/platforms/window')
+const environmentType = require('../../../app/scripts/lib/environment-type')
const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/config').enums
module.exports = reduceMetamask
@@ -14,6 +15,7 @@ function reduceMetamask (state, action) {
isUnlocked: false,
isAccountMenuOpen: false,
isMascara: window.platform instanceof MetamascaraPlatform,
+ isPopup: environmentType() === 'popup',
rpcTarget: 'https://rawtestrpc.metamask.io/',
identities: {},
unapprovedTxs: {},
@@ -36,17 +38,23 @@ function reduceMetamask (state, action) {
errors: {},
maxModeOn: false,
editingTransactionId: null,
+ forceGasMin: null,
+ toNickname: '',
},
coinOptions: {},
useBlockie: false,
featureFlags: {},
networkEndpointType: OLD_UI_NETWORK_TYPE,
+ isRevealingSeedWords: false,
+ welcomeScreenSeen: false,
}, state.metamask)
switch (action.type) {
case actions.SHOW_ACCOUNTS_PAGE:
- newState = extend(metamaskState)
+ newState = extend(metamaskState, {
+ isRevealingSeedWords: false,
+ })
delete newState.seedWords
return newState
@@ -122,10 +130,10 @@ function reduceMetamask (state, action) {
},
})
+
case actions.SHOW_NEW_VAULT_SEED:
return extend(metamaskState, {
- isUnlocked: true,
- isInitialized: false,
+ isRevealingSeedWords: true,
seedWords: action.value,
})
@@ -231,7 +239,8 @@ function reduceMetamask (state, action) {
return extend(metamaskState, {
send: {
...metamaskState.send,
- to: action.value,
+ to: action.value.to,
+ toNickname: action.value.nickname,
},
})
@@ -291,6 +300,7 @@ function reduceMetamask (state, action) {
memo: '',
errors: {},
editingTransactionId: null,
+ forceGasMin: null,
},
})
@@ -342,6 +352,11 @@ function reduceMetamask (state, action) {
networkEndpointType: action.value,
})
+ case actions.CLOSE_WELCOME_SCREEN:
+ return extend(metamaskState, {
+ welcomeScreenSeen: true,
+ })
+
default:
return metamaskState
diff --git a/ui/app/selectors.js b/ui/app/selectors.js
index 5d2635775..2bdc39004 100644
--- a/ui/app/selectors.js
+++ b/ui/app/selectors.js
@@ -18,6 +18,7 @@ const selectors = {
getCurrentAccountWithSendEtherInfo,
getGasPrice,
getGasLimit,
+ getForceGasMin,
getAddressBook,
getSendFrom,
getCurrentCurrency,
@@ -55,8 +56,9 @@ function getSelectedToken (state) {
const tokens = state.metamask.tokens || []
const selectedTokenAddress = state.metamask.selectedTokenAddress
const selectedToken = tokens.filter(({ address }) => address === selectedTokenAddress)[0]
+ const sendToken = state.metamask.send.token
- return selectedToken || null
+ return selectedToken || sendToken || null
}
function getSelectedTokenExchangeRate (state) {
@@ -130,6 +132,10 @@ function getGasLimit (state) {
return state.metamask.send.gasLimit
}
+function getForceGasMin (state) {
+ return state.metamask.send.forceGasMin
+}
+
function getSendFrom (state) {
return state.metamask.send.from
}
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index 4230e8a22..df819dbd8 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -1,12 +1,13 @@
const { inherits } = require('util')
const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript')
+const t = require('../i18n')
const ethAbi = require('ethereumjs-abi')
const ethUtil = require('ethereumjs-util')
const FromDropdown = require('./components/send/from-dropdown')
-const ToAutoComplete = require('./components/send/to-autocomplete')
+const EnsInput = require('./components/ens-input')
const CurrencyDisplay = require('./components/send/currency-display')
const MemoTextArea = require('./components/send/memo-textarea')
const GasFeeDisplay = require('./components/send/gas-fee-display-v2')
@@ -43,6 +44,7 @@ function SendTransactionScreen () {
to: null,
amount: null,
},
+ gasLoadingError: false,
}
this.handleToChange = this.handleToChange.bind(this)
@@ -129,6 +131,10 @@ SendTransactionScreen.prototype.updateGas = function () {
.then(([gasPrice, gas]) => {
const newGasTotal = this.getGasTotal(gas, gasPrice)
updateGasTotal(newGasTotal)
+ this.setState({ gasLoadingError: false })
+ })
+ .catch(err => {
+ this.setState({ gasLoadingError: true })
})
} else {
const newGasTotal = this.getGasTotal(gasLimit, gasPrice)
@@ -180,19 +186,21 @@ SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) {
}
SendTransactionScreen.prototype.renderHeader = function () {
- const { selectedToken } = this.props
- const tokenText = selectedToken ? 'tokens' : 'ETH'
+ const { selectedToken, clearSend, goHome } = this.props
return h('div.page-container__header', [
- h('div.page-container__title', selectedToken ? 'Send Tokens' : 'Send ETH'),
+ h('div.page-container__title', selectedToken ? t('sendTokens') : t('sendETH')),
- h('div.page-container__subtitle', `Only send ${tokenText} to an Ethereum address.`),
+ h('div.page-container__subtitle', t('onlySendToEtherAddress')),
+
+ h('div.page-container__header-close', {
+ onClick: () => {
+ clearSend()
+ goHome()
+ },
+ }),
- h(
- 'div.page-container__subtitle',
- 'Sending to a different crytpocurrency that is not Ethereum may result in permanent loss.'
- ),
])
}
@@ -246,7 +254,7 @@ SendTransactionScreen.prototype.renderFromRow = function () {
])
}
-SendTransactionScreen.prototype.handleToChange = function (to) {
+SendTransactionScreen.prototype.handleToChange = function (to, nickname = '') {
const {
updateSendTo,
updateSendErrors,
@@ -255,19 +263,19 @@ SendTransactionScreen.prototype.handleToChange = function (to) {
let toError = null
if (!to) {
- toError = 'Required'
+ toError = t('required')
} else if (!isValidAddress(to)) {
- toError = 'Recipient address is invalid'
+ toError = t('invalidAddressRecipient')
} else if (to === from) {
- toError = 'From and To address cannot be the same'
+ toError = t('fromToSame')
}
- updateSendTo(to)
+ updateSendTo(to, nickname)
updateSendErrors({ to: toError })
}
SendTransactionScreen.prototype.renderToRow = function () {
- const { toAccounts, errors, to } = this.props
+ const { toAccounts, errors, to, network } = this.props
const { toDropdownOpen } = this.state
@@ -275,14 +283,17 @@ SendTransactionScreen.prototype.renderToRow = function () {
h('div.send-v2__form-label', [
- 'To:',
+ t('to'),
- this.renderErrorMessage('to'),
+ this.renderErrorMessage(t('to')),
]),
h('div.send-v2__form-field', [
- h(ToAutoComplete, {
+ h(EnsInput, {
+ name: 'address',
+ placeholder: 'Recipient Address',
+ network,
to,
accounts: Object.entries(toAccounts).map(([key, account]) => account),
dropdownOpen: toDropdownOpen,
@@ -359,8 +370,9 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
})
}
+ const verifyTokenBalance = selectedToken && tokenBalance !== null
let sufficientTokens
- if (selectedToken) {
+ if (verifyTokenBalance) {
sufficientTokens = isTokenBalanceSufficient({
tokenBalance,
amount,
@@ -374,11 +386,11 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
)
if (conversionRate && !sufficientBalance) {
- amountError = 'Insufficient funds.'
- } else if (selectedToken && !sufficientTokens) {
- amountError = 'Insufficient tokens.'
+ amountError = t('insufficientFunds')
+ } else if (verifyTokenBalance && !sufficientTokens) {
+ amountError = t('insufficientTokens')
} else if (amountLessThanZero) {
- amountError = 'Can not send negative amounts of ETH.'
+ amountError = t('negativeETH')
}
updateSendErrors({ amount: amountError })
@@ -394,20 +406,21 @@ SendTransactionScreen.prototype.renderAmountRow = function () {
amount,
setMaxModeTo,
maxModeOn,
+ gasTotal,
} = this.props
return h('div.send-v2__form-row', [
- h('div.send-v2__form-label', [
+ h('div.send-v2__form-label', [
'Amount:',
this.renderErrorMessage('amount'),
- !errors.amount && h('div.send-v2__amount-max', {
+ !errors.amount && gasTotal && h('div.send-v2__amount-max', {
onClick: (event) => {
event.preventDefault()
setMaxModeTo(true)
this.setAmountToMax()
},
- }, [ !maxModeOn ? 'Max' : '' ]),
+ }, [ !maxModeOn ? t('max') : '' ]),
]),
h('div.send-v2__form-field', [
@@ -432,10 +445,11 @@ SendTransactionScreen.prototype.renderGasRow = function () {
showCustomizeGasModal,
gasTotal,
} = this.props
+ const { gasLoadingError } = this.state
return h('div.send-v2__form-row', [
- h('div.send-v2__form-label', 'Gas fee:'),
+ h('div.send-v2__form-label', h('gasFee')),
h('div.send-v2__form-field', [
@@ -444,6 +458,7 @@ SendTransactionScreen.prototype.renderGasRow = function () {
conversionRate,
convertedCurrency,
onClick: showCustomizeGasModal,
+ gasLoadingError,
}),
]),
@@ -469,18 +484,19 @@ SendTransactionScreen.prototype.renderMemoRow = function () {
}
SendTransactionScreen.prototype.renderForm = function () {
- return h('div.send-v2__form', {}, [
-
- this.renderFromRow(),
+ return h('.page-container__content', {}, [
+ h('.send-v2__form', [
+ this.renderFromRow(),
- this.renderToRow(),
+ this.renderToRow(),
- this.renderAmountRow(),
+ this.renderAmountRow(),
- this.renderGasRow(),
+ this.renderGasRow(),
- // this.renderMemoRow(),
+ // this.renderMemoRow(),
+ ]),
])
}
@@ -488,23 +504,26 @@ SendTransactionScreen.prototype.renderFooter = function () {
const {
clearSend,
gasTotal,
+ tokenBalance,
+ selectedToken,
errors: { amount: amountError, to: toError },
history,
} = this.props
+ const missingTokenBalance = selectedToken && !tokenBalance
const noErrors = !amountError && toError === null
return h('div.page-container__footer', [
- h('button.btn-cancel.page-container__footer-button', {
+ h('button.btn-secondary--lg.page-container__footer-button', {
onClick: () => {
clearSend()
history.goBack()
},
- }, 'Cancel'),
- h('button.btn-clear.page-container__footer-button', {
- disabled: !noErrors || !gasTotal,
+ }, t('cancel')),
+ h('button.btn-primary--lg.page-container__footer-button', {
+ disabled: !noErrors || !gasTotal || missingTokenBalance,
onClick: event => this.onSubmit(event),
- }, 'Next'),
+ }, t('next')),
])
}
@@ -523,11 +542,11 @@ SendTransactionScreen.prototype.render = function () {
)
}
-SendTransactionScreen.prototype.addToAddressBookIfNew = function (newAddress) {
+SendTransactionScreen.prototype.addToAddressBookIfNew = function (newAddress, nickname = '') {
const { toAccounts, addToAddressBook } = this.props
if (!toAccounts.find(({ address }) => newAddress === address)) {
// TODO: nickname, i.e. addToAddressBook(recipient, nickname)
- addToAddressBook(newAddress)
+ addToAddressBook(newAddress, nickname)
}
}
@@ -564,9 +583,11 @@ SendTransactionScreen.prototype.getEditedTx = function () {
data,
})
} else {
+ const data = unapprovedTxs[editingTransactionId].txParams.data
Object.assign(editingTx.txParams, {
value: ethUtil.addHexPrefix(amount),
to: ethUtil.addHexPrefix(to),
+ data,
})
}
@@ -586,6 +607,7 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
updateTx,
selectedToken,
editingTransactionId,
+ toNickname,
errors: { amount: amountError, to: toError },
} = this.props
@@ -595,7 +617,7 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
return
}
- this.addToAddressBookIfNew(to)
+ this.addToAddressBookIfNew(to, toNickname)
if (editingTransactionId) {
const editedTx = this.getEditedTx()
diff --git a/ui/app/send.js b/ui/app/send.js
deleted file mode 100644
index 517b7690d..000000000
--- a/ui/app/send.js
+++ /dev/null
@@ -1,547 +0,0 @@
-// const { inherits } = require('util')
-// const PersistentForm = require('../lib/persistent-form')
-// const h = require('react-hyperscript')
-// const connect = require('react-redux').connect
-// const Identicon = require('./components/identicon')
-// const EnsInput = require('./components/ens-input')
-// const GasTooltip = require('./components/send/gas-tooltip')
-// const CurrencyToggle = require('./components/send/currency-toggle')
-// const GasFeeDisplay = require('./components/send/gas-fee-display')
-// const { getSelectedIdentity } = require('./selectors')
-
-// const {
-// showAccountsPage,
-// backToAccountDetail,
-// displayWarning,
-// hideWarning,
-// addToAddressBook,
-// signTx,
-// estimateGas,
-// getGasPrice,
-// } = require('./actions')
-// const { stripHexPrefix, addHexPrefix } = require('ethereumjs-util')
-// const { isHex, numericBalance, isValidAddress, allNull } = require('./util')
-// const { conversionUtil, conversionGreaterThan } = require('./conversion-util')
-
-// module.exports = connect(mapStateToProps)(SendTransactionScreen)
-
-// function mapStateToProps (state) {
-// const {
-// selectedAddress: address,
-// accounts,
-// identities,
-// network,
-// addressBook,
-// conversionRate,
-// currentBlockGasLimit: blockGasLimit,
-// } = state.metamask
-// const { warning } = state.appState
-// const selectedIdentity = getSelectedIdentity(state)
-// const account = accounts[address]
-
-// return {
-// address,
-// accounts,
-// identities,
-// network,
-// addressBook,
-// conversionRate,
-// blockGasLimit,
-// warning,
-// selectedIdentity,
-// error: warning && warning.split('.')[0],
-// account,
-// identity: identities[address],
-// balance: account ? account.balance : null,
-// }
-// }
-
-// inherits(SendTransactionScreen, PersistentForm)
-// function SendTransactionScreen () {
-// PersistentForm.call(this)
-
-// // [WIP] These are the bare minimum of tx props needed to sign a transaction
-// // We will need a few more for contract-related interactions
-// this.state = {
-// newTx: {
-// from: '',
-// to: '',
-// amountToSend: '0x0',
-// gasPrice: null,
-// gas: null,
-// amount: '0x0',
-// txData: null,
-// memo: '',
-// },
-// activeCurrency: 'USD',
-// tooltipIsOpen: false,
-// errors: {},
-// isValid: false,
-// }
-
-// this.back = this.back.bind(this)
-// this.closeTooltip = this.closeTooltip.bind(this)
-// this.onSubmit = this.onSubmit.bind(this)
-// this.setActiveCurrency = this.setActiveCurrency.bind(this)
-// this.toggleTooltip = this.toggleTooltip.bind(this)
-// this.validate = this.validate.bind(this)
-// this.getAmountToSend = this.getAmountToSend.bind(this)
-// this.setErrorsFor = this.setErrorsFor.bind(this)
-// this.clearErrorsFor = this.clearErrorsFor.bind(this)
-
-// this.renderFromInput = this.renderFromInput.bind(this)
-// this.renderToInput = this.renderToInput.bind(this)
-// this.renderAmountInput = this.renderAmountInput.bind(this)
-// this.renderGasInput = this.renderGasInput.bind(this)
-// this.renderMemoInput = this.renderMemoInput.bind(this)
-// this.renderErrorMessage = this.renderErrorMessage.bind(this)
-// }
-
-// SendTransactionScreen.prototype.componentWillMount = function () {
-// const { newTx } = this.state
-// const { address } = this.props
-
-// Promise.all([
-// this.props.dispatch(getGasPrice()),
-// this.props.dispatch(estimateGas({
-// from: address,
-// gas: '746a528800',
-// })),
-// ])
-// .then(([blockGasPrice, estimatedGas]) => {
-// console.log({ blockGasPrice, estimatedGas})
-// this.setState({
-// newTx: {
-// ...newTx,
-// gasPrice: blockGasPrice,
-// gas: estimatedGas,
-// },
-// })
-// })
-// }
-
-// SendTransactionScreen.prototype.renderErrorMessage = function(errorType, warning) {
-// const { errors } = this.state
-// const errorMessage = errors[errorType];
-
-// return errorMessage || warning
-// ? h('div.send-screen-input-wrapper__error-message', [ errorMessage || warning ])
-// : null
-// }
-
-// SendTransactionScreen.prototype.renderFromInput = function (from, identities) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div', 'From:'),
-
-// h('input.large-input.send-screen-input', {
-// list: 'accounts',
-// placeholder: 'Account',
-// value: from,
-// onChange: (event) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// from: event.target.value,
-// },
-// })
-// },
-// onBlur: () => this.setErrorsFor('from'),
-// onFocus: event => {
-// this.clearErrorsFor('from')
-// this.state.newTx.from && event.target.select()
-// },
-// }),
-
-// h('datalist#accounts', [
-// Object.entries(identities).map(([key, { address, name }]) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// ]),
-
-// this.renderErrorMessage('from'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderToInput = function (to, identities, addressBook) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div', 'To:'),
-
-// h('input.large-input.send-screen-input', {
-// name: 'address',
-// list: 'addresses',
-// placeholder: 'Address',
-// value: to,
-// onChange: (event) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// to: event.target.value,
-// },
-// })
-// },
-// onBlur: () => {
-// this.setErrorsFor('to')
-// },
-// onFocus: event => {
-// this.clearErrorsFor('to')
-// this.state.newTx.to && event.target.select()
-// },
-// }),
-
-// h('datalist#addresses', [
-// // Corresponds to the addresses owned.
-// ...Object.entries(identities).map(([key, { address, name }]) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// // Corresponds to previously sent-to addresses.
-// ...addressBook.map(({ address, name }) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// ]),
-
-// this.renderErrorMessage('to'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderAmountInput = function (activeCurrency) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div.send-screen-amount-labels', [
-// h('span', 'Amount'),
-// h(CurrencyToggle, {
-// activeCurrency,
-// onClick: (newCurrency) => this.setActiveCurrency(newCurrency),
-// }), // holding on icon from design
-// ]),
-
-// h('input.large-input.send-screen-input', {
-// placeholder: `0 ${activeCurrency}`,
-// type: 'number',
-// onChange: (event) => {
-// const amountToSend = event.target.value
-// ? this.getAmountToSend(event.target.value)
-// : '0x0'
-
-// this.setState({
-// newTx: Object.assign(
-// this.state.newTx,
-// {
-// amount: event.target.value,
-// amountToSend: amountToSend,
-// }
-// ),
-// })
-// },
-// onBlur: () => {
-// this.setErrorsFor('amount')
-// },
-// onFocus: () => this.clearErrorsFor('amount'),
-// }),
-
-// this.renderErrorMessage('amount'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderGasInput = function (gasPrice, gas, activeCurrency, conversionRate, blockGasLimit) {
-// return h('div.send-screen-input-wrapper', [
-// this.state.tooltipIsOpen && h(GasTooltip, {
-// className: 'send-tooltip',
-// gasPrice,
-// gasLimit: gas,
-// onClose: this.closeTooltip,
-// onFeeChange: ({gasLimit, gasPrice}) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// gas: gasLimit,
-// gasPrice,
-// },
-// })
-// },
-// }),
-
-// h('div.send-screen-gas-labels', [
-// h('span', [
-// h('i.fa.fa-bolt'),
-// 'Gas fee:',
-// ]),
-// h('span', 'What\'s this?'),
-// ]),
-
-// // TODO: handle loading time when switching to USD
-// h('div.large-input.send-screen-gas-input', {}, [
-// h(GasFeeDisplay, {
-// activeCurrency,
-// conversionRate,
-// gas,
-// gasPrice,
-// blockGasLimit,
-// }),
-// h('div.send-screen-gas-input-customize', {
-// onClick: this.toggleTooltip,
-// }, [
-// 'Customize',
-// ]),
-// ]),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderMemoInput = function () {
-// return h('div.send-screen-input-wrapper', [
-// h('div', 'Transaction memo (optional)'),
-// h('input.large-input.send-screen-input', {
-// onChange: () => {
-// this.setState({
-// newTx: Object.assign(
-// this.state.newTx,
-// {
-// memo: event.target.value,
-// }
-// ),
-// })
-// },
-// }),
-// ])
-// }
-
-// SendTransactionScreen.prototype.render = function () {
-// this.persistentFormParentId = 'send-tx-form'
-
-// const props = this.props
-// const {
-// warning,
-// identities,
-// addressBook,
-// conversionRate,
-// } = props
-
-// const {
-// blockGasLimit,
-// newTx,
-// activeCurrency,
-// isValid,
-// } = this.state
-// const { gas, gasPrice } = newTx
-
-// return (
-
-// h('div.send-screen-wrapper', [
-// // Main Send token Card
-// h('div.send-screen-card', [
-
-// h('img.send-eth-icon', { src: '../images/eth_logo.svg' }),
-
-// h('div.send-screen__title', 'Send'),
-
-// h('div.send-screen__subtitle', 'Send Ethereum to anyone with an Ethereum account'),
-
-// this.renderFromInput(this.state.newTx.from, identities),
-
-// this.renderToInput(this.state.newTx.to, identities, addressBook),
-
-// this.renderAmountInput(activeCurrency),
-
-// this.renderGasInput(
-// gasPrice || '0x0',
-// gas || '0x0',
-// activeCurrency,
-// conversionRate,
-// blockGasLimit
-// ),
-
-// this.renderMemoInput(),
-
-// this.renderErrorMessage(null, warning),
-
-// ]),
-
-// // Buttons underneath card
-// h('section.flex-column.flex-center', [
-// h('button.btn-secondary.send-screen__send-button', {
-// className: !isValid && 'send-screen__send-button__disabled',
-// onClick: (event) => isValid && this.onSubmit(event),
-// }, 'Next'),
-// h('button.btn-tertiary.send-screen__cancel-button', {
-// onClick: this.back,
-// }, 'Cancel'),
-// ]),
-// ])
-
-// )
-// }
-
-// SendTransactionScreen.prototype.toggleTooltip = function () {
-// this.setState({ tooltipIsOpen: !this.state.tooltipIsOpen })
-// }
-
-// SendTransactionScreen.prototype.closeTooltip = function () {
-// this.setState({ tooltipIsOpen: false })
-// }
-
-// SendTransactionScreen.prototype.setActiveCurrency = function (newCurrency) {
-// this.setState({ activeCurrency: newCurrency })
-// }
-
-// SendTransactionScreen.prototype.back = function () {
-// var address = this.props.address
-// this.props.dispatch(backToAccountDetail(address))
-// }
-
-// SendTransactionScreen.prototype.validate = function (balance, amountToSend, { to, from }) {
-// const sufficientBalance = conversionGreaterThan(
-// {
-// value: balance,
-// fromNumericBase: 'hex',
-// },
-// {
-// value: amountToSend,
-// fromNumericBase: 'hex',
-// },
-// )
-
-// const amountLessThanZero = conversionGreaterThan(
-// {
-// value: 0,
-// fromNumericBase: 'dec',
-// },
-// {
-// value: amountToSend,
-// fromNumericBase: 'hex',
-// },
-// )
-
-// const errors = {}
-
-// if (!sufficientBalance) {
-// errors.amount = 'Insufficient funds.'
-// }
-
-// if (amountLessThanZero) {
-// errors.amount = 'Can not send negative amounts of ETH.'
-// }
-
-// if (!from) {
-// errors.from = 'Required'
-// }
-
-// if (from && !isValidAddress(from)) {
-// errors.from = 'Sender address is invalid.'
-// }
-
-// if (!to) {
-// errors.to = 'Required'
-// }
-
-// if (to && !isValidAddress(to)) {
-// errors.to = 'Recipient address is invalid.'
-// }
-
-// // if (txData && !isHex(stripHexPrefix(txData))) {
-// // message = 'Transaction data must be hex string.'
-// // return this.props.dispatch(displayWarning(message))
-// // }
-
-// return {
-// isValid: allNull(errors),
-// errors,
-// }
-// }
-
-// SendTransactionScreen.prototype.getAmountToSend = function (amount) {
-// const { activeCurrency } = this.state
-// const { conversionRate } = this.props
-
-// return conversionUtil(amount, {
-// fromNumericBase: 'dec',
-// toNumericBase: 'hex',
-// fromCurrency: activeCurrency,
-// toCurrency: 'ETH',
-// toDenomination: 'WEI',
-// conversionRate,
-// invertConversionRate: activeCurrency !== 'ETH',
-// })
-// }
-
-// SendTransactionScreen.prototype.setErrorsFor = function (field) {
-// const { balance } = this.props
-// const { newTx, errors: previousErrors } = this.state
-// const { amountToSend } = newTx
-
-// const {
-// isValid,
-// errors: newErrors
-// } = this.validate(balance, amountToSend, newTx)
-
-// const nextErrors = Object.assign({}, previousErrors, {
-// [field]: newErrors[field] || null
-// })
-
-// if (!isValid) {
-// this.setState({
-// errors: nextErrors,
-// isValid,
-// })
-// }
-// }
-
-// SendTransactionScreen.prototype.clearErrorsFor = function (field) {
-// const { errors: previousErrors } = this.state
-// const nextErrors = Object.assign({}, previousErrors, {
-// [field]: null
-// })
-
-// this.setState({
-// errors: nextErrors,
-// isValid: allNull(nextErrors),
-// })
-// }
-
-// SendTransactionScreen.prototype.onSubmit = function (event) {
-// event.preventDefault()
-// const { warning, balance } = this.props
-// const state = this.state || {}
-
-// const recipient = state.newTx.to
-// const sender = state.newTx.from
-// const nickname = state.nickname || ' '
-
-// // TODO: convert this to hex when created and include it in send
-// const txData = state.newTx.memo
-
-// this.props.dispatch(hideWarning())
-
-// this.props.dispatch(addToAddressBook(recipient, nickname))
-
-// var txParams = {
-// from: this.state.newTx.from,
-// to: this.state.newTx.to,
-
-// value: this.state.newTx.amountToSend,
-
-// gas: this.state.newTx.gas,
-// gasPrice: this.state.newTx.gasPrice,
-// }
-
-// if (recipient) txParams.to = addHexPrefix(recipient)
-// if (txData) txParams.data = txData
-
-// this.props.dispatch(signTx(txParams))
-// }
diff --git a/ui/app/template.js b/ui/app/template.js
deleted file mode 100644
index d15b30fd2..000000000
--- a/ui/app/template.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-
-module.exports = connect(mapStateToProps)(COMPONENTNAME)
-
-function mapStateToProps (state) {
- return {}
-}
-
-inherits(COMPONENTNAME, Component)
-function COMPONENTNAME () {
- Component.call(this)
-}
-
-COMPONENTNAME.prototype.render = function () {
- const props = this.props
-
- return (
- h('div', {
- style: {
- background: 'blue',
- },
- }, [
- `Hello, ${props.sender}`,
- ])
- )
-}
-
diff --git a/ui/app/token-tracker.js b/ui/app/token-tracker.js
deleted file mode 100644
index e69de29bb..000000000
--- a/ui/app/token-tracker.js
+++ /dev/null
diff --git a/ui/app/unlock.js b/ui/app/unlock.js
index e77d17d7b..322808619 100644
--- a/ui/app/unlock.js
+++ b/ui/app/unlock.js
@@ -5,6 +5,9 @@ const connect = require('react-redux').connect
const actions = require('./actions')
const getCaretCoordinates = require('textarea-caret')
const EventEmitter = require('events').EventEmitter
+const t = require('../i18n')
+const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
+const environmentType = require('../../app/scripts/lib/environment-type')
const Mascot = require('./components/mascot')
@@ -26,65 +29,72 @@ UnlockScreen.prototype.render = function () {
const state = this.props
const warning = state.warning
return (
- h('.flex-column', {
- style: {
- width: 'inherit',
- },
- }, [
- h('.unlock-screen.flex-column.flex-center.flex-grow', [
-
- h(Mascot, {
- animationEventEmitter: this.animationEventEmitter,
- }),
-
- h('h1', {
- style: {
- fontSize: '1.4em',
- textTransform: 'uppercase',
- color: '#7F8082',
- },
- }, 'MetaMask'),
-
- h('input.large-input', {
- type: 'password',
- id: 'password-box',
- placeholder: 'enter password',
- style: {
- background: 'white',
- },
- onKeyPress: this.onKeyPress.bind(this),
- onInput: this.inputChanged.bind(this),
- }),
-
- h('.error', {
- style: {
- display: warning ? 'block' : 'none',
- padding: '0 20px',
- textAlign: 'center',
- },
- }, warning),
-
- h('button.primary.cursor-pointer', {
- onClick: this.onSubmit.bind(this),
- style: {
- margin: 10,
- },
- }, 'Unlock'),
- ]),
-
- h('.flex-row.flex-center.flex-grow', [
- h('p.pointer', {
- onClick: () => {
- this.props.dispatch(actions.markPasswordForgotten())
+ h('.unlock-screen', [
+
+ h(Mascot, {
+ animationEventEmitter: this.animationEventEmitter,
+ }),
+
+ h('h1', {
+ style: {
+ fontSize: '1.4em',
+ textTransform: 'uppercase',
+ color: '#7F8082',
+ },
+ }, t('appName')),
+
+ h('input.large-input', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: 'enter password',
+ style: {
+ background: 'white',
+ },
+ onKeyPress: this.onKeyPress.bind(this),
+ onInput: this.inputChanged.bind(this),
+ }),
+
+ h('.error', {
+ style: {
+ display: warning ? 'block' : 'none',
+ padding: '0 20px',
+ textAlign: 'center',
+ },
+ }, warning),
+
+ h('button.primary.cursor-pointer', {
+ onClick: this.onSubmit.bind(this),
+ style: {
+ margin: 10,
+ },
+ }, t('login')),
+
+ h('p.pointer', {
+ onClick: () => {
+ this.props.dispatch(actions.markPasswordForgotten())
+ if (environmentType() === 'popup') {
global.platform.openExtensionInBrowser()
- },
- style: {
- fontSize: '0.8em',
- color: 'rgb(247, 134, 28)',
- textDecoration: 'underline',
- },
- }, 'Restore from seed phrase'),
- ]),
+ }
+ },
+ style: {
+ fontSize: '0.8em',
+ color: 'rgb(247, 134, 28)',
+ textDecoration: 'underline',
+ },
+ }, t('restoreFromSeed')),
+
+ h('p.pointer', {
+ onClick: () => {
+ this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
+ .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE)))
+ },
+ style: {
+ fontSize: '0.8em',
+ color: '#aeaeae',
+ textDecoration: 'underline',
+ marginTop: '32px',
+ },
+ }, t('classicInterface')),
])
)
}
diff --git a/ui/app/welcome-screen.js b/ui/app/welcome-screen.js
new file mode 100644
index 000000000..cdbb6dba8
--- /dev/null
+++ b/ui/app/welcome-screen.js
@@ -0,0 +1,56 @@
+import EventEmitter from 'events'
+import h from 'react-hyperscript'
+import { Component } from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import {closeWelcomeScreen} from './actions'
+import Mascot from './components/mascot'
+
+class WelcomeScreen extends Component {
+ static propTypes = {
+ closeWelcomeScreen: PropTypes.func.isRequired,
+ }
+
+ constructor(props) {
+ super(props)
+ this.animationEventEmitter = new EventEmitter()
+ }
+
+ initiateAccountCreation = () => {
+ this.props.closeWelcomeScreen()
+ }
+
+ render () {
+ return h('div.welcome-screen', [
+
+ h('div.welcome-screen__info', [
+
+ h(Mascot, {
+ animationEventEmitter: this.animationEventEmitter,
+ width: '225',
+ height: '225',
+ }),
+
+ h('div.welcome-screen__info__header', 'Welcome to MetaMask Beta'),
+
+ h('div.welcome-screen__info__copy', 'MetaMask is a secure identity vault for Ethereum.'),
+
+ h('div.welcome-screen__info__copy', `It allows you to hold ether & tokens,
+ and serves as your bridge to decentralized applications.`),
+
+ h('button.welcome-screen__button', {
+ onClick: this.initiateAccountCreation,
+ }, 'Continue'),
+
+ ]),
+
+ ])
+ }
+}
+
+export default connect(
+ null,
+ dispatch => ({
+ closeWelcomeScreen: () => dispatch(closeWelcomeScreen()),
+ })
+)(WelcomeScreen)
diff --git a/ui/i18n.js b/ui/i18n.js
new file mode 100644
index 000000000..abfece426
--- /dev/null
+++ b/ui/i18n.js
@@ -0,0 +1,33 @@
+
+// cross-browser connection to extension i18n API
+
+const chrome = chrome || null
+const browser = browser || null
+const extension = require('extensionizer')
+var log = require('loglevel')
+window.log = log
+let getMessage
+
+if (extension.i18n && extension.i18n.getMessage) {
+ getMessage = extension.i18n.getMessage
+} else {
+ // fallback function
+ log.warn('browser.i18n API not available, calling back to english.')
+ const msg = require('../app/_locales/en/messages.json')
+ getMessage = function (key, substitutions) {
+ if (!msg[key]) {
+ log.error(key)
+ throw new Error(key)
+ }
+ let phrase = msg[key].message
+ if (substitutions && substitutions.length) {
+ phrase = phrase.replace(/\$1/g, substitutions[0])
+ if (substitutions.length > 1) {
+ phrase = phrase.replace(/\$2/g, substitutions[1])
+ }
+ }
+ return phrase
+ }
+}
+
+module.exports = getMessage
diff --git a/ui/index.js b/ui/index.js
index bc3676c1f..fdb2f23e0 100644
--- a/ui/index.js
+++ b/ui/index.js
@@ -25,6 +25,7 @@ function launchMetamaskUi (opts, cb) {
function startApp (metamaskState, accountManager, opts) {
// parse opts
+ if (!metamaskState.featureFlags) metamaskState.featureFlags = {}
const store = configureStore({
// metamaskState represents the cross-tab state
diff --git a/ui/lib/contract-namer.js b/ui/lib/contract-namer.js
deleted file mode 100644
index f05e770cc..000000000
--- a/ui/lib/contract-namer.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* CONTRACT NAMER
- *
- * Takes an address,
- * Returns a nicname if we have one stored,
- * otherwise returns null.
- */
-
-const contractMap = require('eth-contract-metadata')
-const ethUtil = require('ethereumjs-util')
-
-module.exports = function (addr, identities = {}) {
- const checksummed = ethUtil.toChecksumAddress(addr)
- if (contractMap[checksummed] && contractMap[checksummed].name) {
- return contractMap[checksummed].name
- }
-
- const address = addr.toLowerCase()
- const ids = hashFromIdentities(identities)
- return addrFromHash(address, ids)
-}
-
-function hashFromIdentities (identities) {
- const result = {}
- for (const key in identities) {
- result[key] = identities[key].name
- }
- return result
-}
-
-function addrFromHash (addr, hash) {
- const address = addr.toLowerCase()
- return hash[address] || null
-}