aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--README.md5
-rw-r--r--app/_locales/en/messages.json3
-rw-r--r--app/phishing.html49
-rw-r--r--app/scripts/controllers/detect-tokens.js2
-rw-r--r--app/scripts/controllers/preferences.js94
-rw-r--r--app/scripts/metamask-controller.js3
-rw-r--r--app/scripts/migrations/028.js40
-rw-r--r--app/scripts/migrations/index.js1
-rw-r--r--mascara/src/app/first-time/confirm-seed-screen.js1
-rw-r--r--mascara/src/app/first-time/import-seed-phrase-screen.js9
-rw-r--r--mascara/src/app/first-time/index.js20
-rw-r--r--old-ui/app/account-qr.js86
-rw-r--r--old-ui/app/app.js461
-rw-r--r--old-ui/app/components/app-bar.js432
-rw-r--r--old-ui/app/components/qr-code.js79
-rw-r--r--old-ui/app/components/shapeshift-form.js4
-rw-r--r--old-ui/app/components/transaction-list-item.js8
-rw-r--r--old-ui/app/css/index.css128
-rw-r--r--old-ui/app/new-ui-annoucement.js85
-rw-r--r--package-lock.json663
-rw-r--r--package.json4
-rw-r--r--test/e2e/beta/contract-test/contract.js5
-rw-r--r--test/e2e/beta/contract-test/index.html3
-rw-r--r--test/e2e/beta/from-import-beta-ui.spec.js67
-rw-r--r--test/e2e/beta/metamask-beta-ui.spec.js170
-rwxr-xr-xtest/e2e/beta/run-all.sh4
-rw-r--r--test/e2e/metamask.spec.js15
-rw-r--r--test/integration/lib/first-time.js5
-rw-r--r--test/unit/app/controllers/detect-tokens-test.js21
-rw-r--r--test/unit/app/controllers/preferences-controller-test.js158
-rw-r--r--ui/app/actions.js3
-rw-r--r--ui/app/components/confirm-page-container/confirm-page-container.component.js6
-rw-r--r--ui/app/components/dropdowns/components/dropdown.js1
-rw-r--r--ui/app/components/dropdowns/network-dropdown.js3
-rw-r--r--ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js21
-rw-r--r--ui/app/components/tx-list-item.js8
-rw-r--r--ui/app/css/itcss/components/network.scss1
38 files changed, 1388 insertions, 1284 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e1e1ff4b..ea5d90500 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## Current Master
+- [#4884](https://github.com/MetaMask/metamask-extension/pull/4884): Allow to have tokens per account and network.
+
+## 4.9.0 Tue Aug 07 2018
+
- Add new tokens auto detection
- Remove rejected transactions from transaction history
- Add Trezor Support
diff --git a/README.md b/README.md
index 8a1eda46c..38aceaf7e 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,9 @@ If you're a web dapp developer, we've got two types of guides for you:
## Building locally
- Install [Node.js](https://nodejs.org/en/) version 8.11.3 and npm version 6.1.0
- - Install dependencies:
- - If you are using nvm (recommended) running `nvm use` will automatically choose the right node version for you.
+ - If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you.
+ - Select npm 6.1.0: ```npm install -g npm@6.1.0```
+ - Install dependencies: ```npm install```
- Install gulp globally with `npm install -g gulp-cli`.
- Build the project to the `./dist/` folder with `gulp build`.
- Optionally, to rebuild on file changes, run `gulp dev`.
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 6f315bb7a..c4c19762d 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -513,6 +513,9 @@
"invalidRPC": {
"message": "Invalid RPC URI"
},
+ "invalidSeedPhrase": {
+ "message": "Invalid seed phrase"
+ },
"jsonFail": {
"message": "Something went wrong. Please make sure your JSON file is properly formatted."
},
diff --git a/app/phishing.html b/app/phishing.html
index 86f2985cc..e20c9ac9c 100644
--- a/app/phishing.html
+++ b/app/phishing.html
@@ -3,26 +3,29 @@
<html>
<head>
- <title>Phishing Warning</title>
+ <title>Dangerous Website Warning</title>
<style>
-body {
- background: #c50000;
- padding: 50px;
- display: flex;
- justify-content: center;
- font-family: sans-serif;
-}
-.centered {
- display: flex;
- flex-direction: column;
- justify-content: center;
- color: white;
- max-width: 600px;
-}
-a {
- color: white;
-}
+ body {
+ background: #c50000;
+ padding: 50px;
+ display: flex;
+ justify-content: center;
+ font-family: sans-serif;
+ }
+
+ .centered {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ color: white;
+ max-width: 600px;
+ }
+
+ a {
+ color: white;
+ }
+
</style>
<script>
@@ -50,11 +53,11 @@ a {
<img src="/images/ethereum-metamask-chrome.png" style="width:100%">
<h3>ATTENTION</h3>
- <p>MetaMask believes this domain to have malicious intent and has prevented you from interacting with it.</p>
- <p>This is because the site tested positive on the <a href="https://github.com/metamask/eth-phishing-detect">Ethereum Phishing Detector</a>.</p>
- <p>You can turn MetaMask off to interact with this site, but it's advised not to.</p>
- <p>If you think this domain is incorrectly flagged, <a href="https://github.com/metamask/eth-phishing-detect/issues/new">please file an issue</a>.</p>
+ <p>MetaMask believes this domain could currently compromise your security and has prevented you from interacting with it.</p>
+ <p>This is because the site tested positive on the <a href="https://github.com/metamask/eth-phishing-detect">Ethereum Phishing Detector</a>. This includes outright malicious websites and legitimate websites that have been compromised by a malicious actor.</p>
+ <p>You can turn MetaMask off to interact with this site, but it is advised not to.</p>
+ <p>If you think this domain is incorrectly flagged or if a blocked legitimate website has resolved its security issues, <a href="https://github.com/metamask/eth-phishing-detect/issues/new">please file an issue</a>.</p>
</div>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js
index 195ec918a..62e639795 100644
--- a/app/scripts/controllers/detect-tokens.js
+++ b/app/scripts/controllers/detect-tokens.js
@@ -85,7 +85,7 @@ class DetectTokensController {
set preferences (preferences) {
if (!preferences) { return }
this._preferences = preferences
- preferences.store.subscribe(({ tokens }) => { this.tokenAddresses = tokens.map((obj) => { return obj.address }) })
+ preferences.store.subscribe(({ tokens = [] }) => { this.tokenAddresses = tokens.map((obj) => { return obj.address }) })
preferences.store.subscribe(({ selectedAddress }) => {
if (this.selectedAddress !== selectedAddress) {
this.selectedAddress = selectedAddress
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index 3bbd48f06..a42bb77b3 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -14,6 +14,7 @@ class PreferencesController {
* @property {array} store.frequentRpcList A list of custom rpcs to provide the user
* @property {string} store.currentAccountTab Indicates the selected tab in the ui
* @property {array} store.tokens The tokens the user wants display in their token lists
+ * @property {object} store.accountTokens The tokens stored per account and then per network type
* @property {boolean} store.useBlockie The users preference for blockie identicons within the UI
* @property {object} store.featureFlags A key-boolean map, where keys refer to features and booleans to whether the
* user wishes to see that feature
@@ -25,6 +26,7 @@ class PreferencesController {
const initState = extend({
frequentRpcList: [],
currentAccountTab: 'history',
+ accountTokens: {},
tokens: [],
suggestedTokens: {},
useBlockie: false,
@@ -35,9 +37,10 @@ class PreferencesController {
}, opts.initState)
this.diagnostics = opts.diagnostics
-
+ this.network = opts.network
this.store = new ObservableStore(initState)
this.showAddTokenUi = opts.showAddTokenUi
+ this._subscribeProviderType()
}
// PUBLIC METHODS
@@ -121,12 +124,19 @@ class PreferencesController {
*/
setAddresses (addresses) {
const oldIdentities = this.store.getState().identities
+ const oldAccountTokens = this.store.getState().accountTokens
+
const identities = addresses.reduce((ids, address, index) => {
const oldId = oldIdentities[address] || {}
ids[address] = {name: `Account ${index + 1}`, address, ...oldId}
return ids
}, {})
- this.store.updateState({ identities })
+ const accountTokens = addresses.reduce((tokens, address) => {
+ const oldTokens = oldAccountTokens[address] || {}
+ tokens[address] = oldTokens
+ return tokens
+ }, {})
+ this.store.updateState({ identities, accountTokens })
}
/**
@@ -137,11 +147,13 @@ class PreferencesController {
*/
removeAddress (address) {
const identities = this.store.getState().identities
+ const accountTokens = this.store.getState().accountTokens
if (!identities[address]) {
throw new Error(`${address} can't be deleted cause it was not found`)
}
delete identities[address]
- this.store.updateState({ identities })
+ delete accountTokens[address]
+ this.store.updateState({ identities, accountTokens })
// If the selected account is no longer valid,
// select an arbitrary other account:
@@ -161,14 +173,17 @@ class PreferencesController {
*/
addAddresses (addresses) {
const identities = this.store.getState().identities
+ const accountTokens = this.store.getState().accountTokens
addresses.forEach((address) => {
// skip if already exists
if (identities[address]) return
// add missing identity
const identityCount = Object.keys(identities).length
+
+ accountTokens[address] = {}
identities[address] = { name: `Account ${identityCount + 1}`, address }
})
- this.store.updateState({ identities })
+ this.store.updateState({ identities, accountTokens })
}
/*
@@ -226,15 +241,15 @@ class PreferencesController {
* Setter for the `selectedAddress` property
*
* @param {string} _address A new hex address for an account
- * @returns {Promise<void>} Promise resolves with undefined
+ * @returns {Promise<void>} Promise resolves with tokens
*
*/
setSelectedAddress (_address) {
- return new Promise((resolve, reject) => {
- const address = normalizeAddress(_address)
- this.store.updateState({ selectedAddress: address })
- resolve()
- })
+ const address = normalizeAddress(_address)
+ this._updateTokens(address)
+ this.store.updateState({ selectedAddress: address })
+ const tokens = this.store.getState().tokens
+ return Promise.resolve(tokens)
}
/**
@@ -283,9 +298,7 @@ class PreferencesController {
} else {
tokens.push(newEntry)
}
-
- this.store.updateState({ tokens })
-
+ this._updateAccountTokens(tokens)
return Promise.resolve(tokens)
}
@@ -298,10 +311,8 @@ class PreferencesController {
*/
removeToken (rawAddress) {
const tokens = this.store.getState().tokens
-
const updatedTokens = tokens.filter(token => token.address !== rawAddress)
-
- this.store.updateState({ tokens: updatedTokens })
+ this._updateAccountTokens(updatedTokens)
return Promise.resolve(updatedTokens)
}
@@ -443,6 +454,57 @@ class PreferencesController {
const numDecimals = parseInt(decimals, 10)
if (isNaN(numDecimals) || numDecimals > 18 || numDecimals < 0) throw new Error(`Invalid decimals ${decimals}`)
if (!isValidAddress(rawAddress)) throw new Error(`Invalid address ${rawAddress}`)
+
+ /**
+ * Subscription to network provider type.
+ *
+ *
+ */
+ _subscribeProviderType () {
+ this.network.providerStore.subscribe(() => {
+ const { tokens } = this._getTokenRelatedStates()
+ this.store.updateState({ tokens })
+ })
+ }
+
+ /**
+ * Updates `accountTokens` and `tokens` of current account and network according to it.
+ *
+ * @param {array} tokens Array of tokens to be updated.
+ *
+ */
+ _updateAccountTokens (tokens) {
+ const { accountTokens, providerType, selectedAddress } = this._getTokenRelatedStates()
+ accountTokens[selectedAddress][providerType] = tokens
+ this.store.updateState({ accountTokens, tokens })
+ }
+
+ /**
+ * Updates `tokens` of current account and network.
+ *
+ * @param {string} selectedAddress Account address to be updated with.
+ *
+ */
+ _updateTokens (selectedAddress) {
+ const { tokens } = this._getTokenRelatedStates(selectedAddress)
+ this.store.updateState({ tokens })
+ }
+
+ /**
+ * A getter for `tokens` and `accountTokens` related states.
+ *
+ * @param {string} selectedAddress A new hex address for an account
+ * @returns {Object.<array, object, string, string>} States to interact with tokens in `accountTokens`
+ *
+ */
+ _getTokenRelatedStates (selectedAddress) {
+ const accountTokens = this.store.getState().accountTokens
+ if (!selectedAddress) selectedAddress = this.store.getState().selectedAddress
+ const providerType = this.network.providerStore.getState().type
+ if (!(selectedAddress in accountTokens)) accountTokens[selectedAddress] = {}
+ if (!(providerType in accountTokens[selectedAddress])) accountTokens[selectedAddress][providerType] = []
+ const tokens = accountTokens[selectedAddress][providerType]
+ return { tokens, accountTokens, providerType, selectedAddress }
}
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 801363cb0..1464b16a6 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -88,6 +88,7 @@ module.exports = class MetamaskController extends EventEmitter {
initState: initState.PreferencesController,
initLangCode: opts.initLangCode,
showAddTokenUi: opts.showAddTokenUi,
+ network: this.networkController,
})
// currency controller
@@ -1439,7 +1440,7 @@ module.exports = class MetamaskController extends EventEmitter {
}
/**
- * A method for activating the retrieval of price data and auto detect tokens,
+ * A method for activating the retrieval of price data,
* which should only be fetched when the UI is visible.
* @private
* @param {boolean} active - True if price data should be getting fetched.
diff --git a/app/scripts/migrations/028.js b/app/scripts/migrations/028.js
new file mode 100644
index 000000000..6553a1052
--- /dev/null
+++ b/app/scripts/migrations/028.js
@@ -0,0 +1,40 @@
+// next version number
+const version = 28
+
+/*
+
+normalizes txParams on unconfirmed txs
+
+*/
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: async function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ return versionedData
+ },
+}
+
+function transformState (state) {
+ const newState = state
+
+ if (newState.PreferencesController) {
+ if (newState.PreferencesController.tokens) {
+ const identities = newState.TransactionController.identities
+ const tokens = newState.PreferencesController.tokens
+ newState.PreferencesController.accountTokens = {}
+ for (const identity in identities) {
+ newState.PreferencesController.accountTokens[identity] = {'mainnet': tokens}
+ }
+ newState.PreferencesController.tokens = []
+ }
+ }
+
+ return newState
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index bd0005221..3b512715e 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -38,4 +38,5 @@ module.exports = [
require('./025'),
require('./026'),
require('./027'),
+ require('./028'),
]
diff --git a/mascara/src/app/first-time/confirm-seed-screen.js b/mascara/src/app/first-time/confirm-seed-screen.js
index 7c0431431..dfbaffe33 100644
--- a/mascara/src/app/first-time/confirm-seed-screen.js
+++ b/mascara/src/app/first-time/confirm-seed-screen.js
@@ -106,6 +106,7 @@ class ConfirmSeedScreen extends Component {
key={i}
className={classnames('backup-phrase__confirm-seed-option', {
'backup-phrase__confirm-seed-option--selected': isSelected,
+ 'backup-phrase__confirm-seed-option--unselected': !isSelected,
})}
onClick={() => {
if (!isSelected) {
diff --git a/mascara/src/app/first-time/import-seed-phrase-screen.js b/mascara/src/app/first-time/import-seed-phrase-screen.js
index fd2516ad4..883893e88 100644
--- a/mascara/src/app/first-time/import-seed-phrase-screen.js
+++ b/mascara/src/app/first-time/import-seed-phrase-screen.js
@@ -1,3 +1,4 @@
+import {validateMnemonic} from 'bip39'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
@@ -39,8 +40,12 @@ class ImportSeedPhraseScreen extends Component {
handleSeedPhraseChange (seedPhrase) {
let seedPhraseError = null
- if (seedPhrase && this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) {
- seedPhraseError = this.context.t('seedPhraseReq')
+ if (seedPhrase) {
+ if (this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) {
+ seedPhraseError = this.context.t('seedPhraseReq')
+ } else if (!validateMnemonic(seedPhrase)) {
+ seedPhraseError = this.context.t('invalidSeedPhrase')
+ }
}
this.setState({ seedPhrase, seedPhraseError })
diff --git a/mascara/src/app/first-time/index.js b/mascara/src/app/first-time/index.js
index dc254bb19..6e4dc74bb 100644
--- a/mascara/src/app/first-time/index.js
+++ b/mascara/src/app/first-time/index.js
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { withRouter, Switch, Route } from 'react-router-dom'
import { compose } from 'recompose'
-import classnames from 'classnames'
import CreatePasswordScreen from './create-password-screen'
import UniqueImageScreen from './unique-image-screen'
@@ -44,28 +43,9 @@ class FirstTimeFlow extends Component {
noActiveNotices: false,
};
- renderAppBar () {
- const { welcomeScreenSeen } = this.props
-
- return (
- <div className="alpha-warning__container">
- <h2 className={classnames({
- 'alpha-warning': welcomeScreenSeen,
- 'alpha-warning-welcome-screen': !welcomeScreenSeen,
- })}
- >
- Please be aware that this version is still under development
- </h2>
- </div>
- )
- }
-
render () {
- const { isPopup } = this.props
-
return (
<div className="flex-column flex-grow">
- { !isPopup && this.renderAppBar() }
<div className="first-time-flow">
<Switch>
<Route exact path={INITIALIZE_IMPORT_ACCOUNT_ROUTE} component={ImportAccountScreen} />
diff --git a/old-ui/app/account-qr.js b/old-ui/app/account-qr.js
new file mode 100644
index 000000000..b41cc5112
--- /dev/null
+++ b/old-ui/app/account-qr.js
@@ -0,0 +1,86 @@
+const PropTypes = require('prop-types')
+const {PureComponent} = require('react')
+const h = require('react-hyperscript')
+const {qrcode: qrCode} = require('qrcode-npm')
+const {connect} = require('react-redux')
+const {isHexPrefixed} = require('ethereumjs-util')
+const actions = require('../../ui/app/actions')
+const CopyButton = require('./components/copyButton')
+
+class AccountQrScreen extends PureComponent {
+ static defaultProps = {
+ warning: null,
+ }
+
+ static propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ buyView: PropTypes.any.isRequired,
+ Qr: PropTypes.object.isRequired,
+ selectedAddress: PropTypes.string.isRequired,
+ warning: PropTypes.node,
+ }
+
+ render () {
+ const {dispatch, Qr, selectedAddress, warning} = this.props
+ const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}`
+ const qrImage = qrCode(4, 'M')
+
+ qrImage.addData(address)
+ qrImage.make()
+
+ return h('div.flex-column.full-width', {
+ style: {
+ alignItems: 'center',
+ boxSizing: 'border-box',
+ padding: '50px',
+ },
+ }, [
+ h('div.flex-row.full-width', {
+ style: {
+ alignItems: 'flex-start',
+ },
+ }, [
+ h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
+ onClick () {
+ dispatch(actions.backToAccountDetail(selectedAddress))
+ },
+ }),
+ ]),
+ h('div.qr-header', Qr.message),
+ warning && h('span.error.flex-center', {
+ style: {
+ textAlign: 'center',
+ width: '229px',
+ height: '82px',
+ },
+ }, [
+ this.props.warning,
+ ]),
+ h('div#qr-container.flex-column', {
+ style: {
+ marginTop: '25px',
+ marginBottom: '15px',
+ },
+ dangerouslySetInnerHTML: {
+ __html: qrImage.createTableTag(4),
+ },
+ }),
+ h('div.flex-row.full-width', [
+ h('h3.ellip-address.grow-tenx', Qr.data),
+ h(CopyButton, {
+ value: Qr.data,
+ }),
+ ]),
+ ])
+ }
+}
+
+function mapStateToProps (state) {
+ return {
+ Qr: state.appState.Qr,
+ buyView: state.appState.buyView,
+ warning: state.appState.warning,
+ }
+}
+
+module.exports = connect(mapStateToProps)(AccountQrScreen)
diff --git a/old-ui/app/app.js b/old-ui/app/app.js
index fb15d414d..f60ee5beb 100644
--- a/old-ui/app/app.js
+++ b/old-ui/app/app.js
@@ -14,6 +14,7 @@ const NewKeyChainScreen = require('./new-keychain')
const UnlockScreen = require('./unlock')
// accounts
const AccountDetailScreen = require('./account-detail')
+const AccountQrScreen = require('./account-qr')
const SendTransactionScreen = require('./send')
const ConfirmTxScreen = require('./conf-tx')
// notice
@@ -25,17 +26,13 @@ const AddTokenScreen = require('./add-token')
const AddSuggestedTokenScreen = require('./add-suggested-token')
const Import = require('./accounts/import')
const InfoScreen = require('./info')
+const NewUiAnnouncement = require('./new-ui-annoucement')
+const AppBar = require('./components/app-bar')
const Loading = require('./components/loading')
-const SandwichExpando = require('sandwich-expando')
-const Dropdown = require('./components/dropdown').Dropdown
-const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
-const NetworkIndicator = require('./components/network')
const BuyView = require('./components/buy-button-subview')
-const QrView = require('./components/qr-code')
const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
-const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
module.exports = connect(mapStateToProps)(App)
@@ -88,13 +85,29 @@ function mapStateToProps (state) {
}
App.prototype.render = function () {
- var props = this.props
- const { isLoading, loadingMessage, transForward, network } = props
- const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
- const loadMessage = loadingMessage || isLoadingNetwork ?
- `Connecting to ${this.getNetworkName()}` : null
+ const {
+ currentView,
+ dispatch,
+ isLoading,
+ loadingMessage,
+ transForward,
+ network,
+ featureFlags,
+ } = this.props
+ const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
+ const loadMessage = loadingMessage || isLoadingNetwork
+ ? `Connecting to ${this.getNetworkName()}`
+ : null
log.debug('Main ui render function')
+ if (!featureFlags.skipAnnounceBetaUI) {
+ return (
+ h(NewUiAnnouncement, {
+ dispatch,
+ })
+ )
+ }
+
return (
h('.flex-column.full-height', {
style: {
@@ -104,12 +117,9 @@ App.prototype.render = function () {
alignItems: 'center',
},
}, [
-
- // app bar
- this.renderAppBar(),
- this.renderNetworkDropdown(),
- this.renderDropdown(),
-
+ h(AppBar, {
+ ...this.props,
+ }),
this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
// panel content
@@ -123,299 +133,6 @@ App.prototype.render = function () {
])
)
}
-
-App.prototype.renderAppBar = function () {
- if (window.METAMASK_UI_TYPE === 'notification') {
- return null
- }
-
- const props = this.props
- const state = this.state || {}
- const isNetworkMenuOpen = state.isNetworkMenuOpen || false
- const {isMascara, isOnboarding} = props
-
- // Do not render header if user is in mascara onboarding
- if (isMascara && isOnboarding) {
- return null
- }
-
- // Do not render header if user is in mascara buy ether
- if (isMascara && props.currentView.name === 'buyEth') {
- return null
- }
-
- return (
-
- h('.full-width', {
- height: '38px',
- }, [
-
- h('.app-header.flex-row.flex-space-between', {
- style: {
- alignItems: 'center',
- visibility: props.isUnlocked ? 'visible' : 'none',
- background: props.isUnlocked ? 'white' : 'none',
- height: '38px',
- position: 'relative',
- zIndex: 12,
- },
- }, [
-
- h('div.left-menu-section', {
- style: {
- display: 'flex',
- flexDirection: 'row',
- alignItems: 'center',
- },
- }, [
-
- // mini logo
- h('img', {
- height: 24,
- width: 24,
- src: './images/icon-128.png',
- }),
-
- h(NetworkIndicator, {
- network: this.props.network,
- provider: this.props.provider,
- onClick: (event) => {
- event.preventDefault()
- event.stopPropagation()
- this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
- },
- }),
-
- ]),
-
- props.isUnlocked && h('div', {
- style: {
- display: 'flex',
- flexDirection: 'row',
- alignItems: 'center',
- },
- }, [
-
- props.isUnlocked && h(AccountDropdowns, {
- style: {},
- enableAccountsSelector: true,
- identities: this.props.identities,
- selected: this.props.selectedAddress,
- network: this.props.network,
- keyrings: this.props.keyrings,
- }, []),
-
- // hamburger
- props.isUnlocked && h(SandwichExpando, {
- className: 'sandwich-expando',
- width: 16,
- barHeight: 2,
- padding: 0,
- isOpen: state.isMainMenuOpen,
- color: 'rgb(247,146,30)',
- onClick: () => {
- this.setState({
- isMainMenuOpen: !state.isMainMenuOpen,
- })
- },
- }),
- ]),
- ]),
- ])
- )
-}
-
-App.prototype.renderNetworkDropdown = function () {
- const props = this.props
- const { provider: { type: providerType, rpcTarget: activeNetwork } } = props
- const rpcList = props.frequentRpcList
- const state = this.state || {}
- const isOpen = state.isNetworkMenuOpen
-
- return h(Dropdown, {
- useCssTransition: true,
- isOpen,
- onClickOutside: (event) => {
- const { classList } = event.target
- const isNotToggleElement = [
- classList.contains('menu-icon'),
- classList.contains('network-name'),
- classList.contains('network-indicator'),
- ].filter(bool => bool).length === 0
- // classes from three constituent nodes of the toggle element
-
- if (isNotToggleElement) {
- this.setState({ isNetworkMenuOpen: false })
- }
- },
- zIndex: 11,
- style: {
- position: 'absolute',
- left: '2px',
- top: '36px',
- },
- innerStyle: {
- padding: '2px 16px 2px 0px',
- },
- }, [
-
- h(
- DropdownMenuItem,
- {
- key: 'main',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('mainnet')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('.menu-icon.diamond'),
- 'Main Ethereum Network',
- providerType === 'mainnet' ? h('.check', '✓') : null,
- ]
- ),
-
- h(
- DropdownMenuItem,
- {
- key: 'ropsten',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('ropsten')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('.menu-icon.red-dot'),
- 'Ropsten Test Network',
- providerType === 'ropsten' ? h('.check', '✓') : null,
- ]
- ),
-
- h(
- DropdownMenuItem,
- {
- key: 'kovan',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('kovan')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('.menu-icon.hollow-diamond'),
- 'Kovan Test Network',
- providerType === 'kovan' ? h('.check', '✓') : null,
- ]
- ),
-
- h(
- DropdownMenuItem,
- {
- key: 'rinkeby',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('rinkeby')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('.menu-icon.golden-square'),
- 'Rinkeby Test Network',
- providerType === 'rinkeby' ? h('.check', '✓') : null,
- ]
- ),
-
- h(
- DropdownMenuItem,
- {
- key: 'default',
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => props.dispatch(actions.setProviderType('localhost')),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- 'Localhost 8545',
- activeNetwork === 'http://localhost:8545' ? h('.check', '✓') : null,
- ]
- ),
-
- this.renderCustomOption(props.provider),
- this.renderCommonRpc(rpcList, props.provider),
-
- h(
- DropdownMenuItem,
- {
- closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
- onClick: () => this.props.dispatch(actions.showConfigPage()),
- style: {
- fontSize: '18px',
- },
- },
- [
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- 'Custom RPC',
- activeNetwork === 'custom' ? h('.check', '✓') : null,
- ]
- ),
-
- ])
-}
-
-App.prototype.renderDropdown = function () {
- const state = this.state || {}
- const isOpen = state.isMainMenuOpen
-
- return h(Dropdown, {
- useCssTransition: true,
- isOpen: isOpen,
- zIndex: 11,
- onClickOutside: (event) => {
- const classList = event.target.classList
- const parentClassList = event.target.parentElement.classList
-
- const isToggleElement = classList.contains('sandwich-expando') ||
- parentClassList.contains('sandwich-expando')
-
- if (isOpen && !isToggleElement) {
- this.setState({ isMainMenuOpen: false })
- }
- },
- style: {
- position: 'absolute',
- right: '2px',
- top: '38px',
- },
- innerStyle: {},
- }, [
- h(DropdownMenuItem, {
- closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
- onClick: () => { this.props.dispatch(actions.showConfigPage()) },
- }, 'Settings'),
-
- h(DropdownMenuItem, {
- closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
- onClick: () => { this.props.dispatch(actions.lockMetamask()) },
- }, 'Log Out'),
-
- h(DropdownMenuItem, {
- closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
- onClick: () => { this.props.dispatch(actions.showInfoPage()) },
- }, 'Info/Help'),
-
- h(DropdownMenuItem, {
- closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
- onClick: () => {
- this.props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
- },
- }, 'Try Beta!'),
- ])
-}
-
App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork, loadMessage }) {
const { isMascara } = this.props
@@ -427,25 +144,6 @@ App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork,
})
}
-App.prototype.renderBackButton = function (style, justArrow = false) {
- var props = this.props
- return (
- h('.flex-row', {
- key: 'leftArrow',
- style: style,
- onClick: () => props.dispatch(actions.goBackToInitView()),
- }, [
- h('i.fa.fa-arrow-left.cursor-pointer'),
- justArrow ? null : h('div.cursor-pointer', {
- style: {
- marginLeft: '3px',
- },
- onClick: () => props.dispatch(actions.goBackToInitView()),
- }, 'BACK'),
- ])
- )
-}
-
App.prototype.renderPrimary = function () {
log.debug('rendering primary')
var props = this.props
@@ -467,22 +165,6 @@ App.prototype.renderPrimary = function () {
key: 'NoticeScreen',
onConfirm: () => props.dispatch(actions.markNoticeRead(props.nextUnreadNotice)),
}),
-
- !props.isInitialized && h('.flex-row.flex-center.flex-grow', [
- h('p.pointer', {
- onClick: () => {
- global.platform.openExtensionInBrowser()
- props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
- },
- style: {
- fontSize: '0.8em',
- color: '#aeaeae',
- textDecoration: 'underline',
- marginTop: '32px',
- },
- }, 'Try Beta Version'),
- ]),
-
])
} else if (props.lostAccounts && props.lostAccounts.length > 0) {
log.debug('rendering notice screen for lost accounts view.')
@@ -586,31 +268,10 @@ App.prototype.renderPrimary = function () {
case 'qr':
log.debug('rendering show qr screen')
- return h('div', {
- style: {
- position: 'absolute',
- height: '100%',
- top: '0px',
- left: '0px',
- },
- }, [
- h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
- onClick: () => props.dispatch(actions.backToAccountDetail(props.selectedAddress)),
- style: {
- marginLeft: '10px',
- marginTop: '50px',
- },
- }),
- h('div', {
- style: {
- position: 'absolute',
- left: '44px',
- width: '285px',
- },
- }, [
- h(QrView, {key: 'qr'}),
- ]),
- ])
+ return h(AccountQrScreen, {
+ key: 'account-qr',
+ selectedAddress: props.selectedAddress,
+ })
default:
log.debug('rendering default, account detail screen')
@@ -629,41 +290,6 @@ App.prototype.toggleMetamaskActive = function () {
this.props.dispatch(actions.lockMetamask(false))
}
}
-
-App.prototype.renderCustomOption = function (provider) {
- const { rpcTarget, type } = provider
- const props = this.props
-
- 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':
- return null
-
- default:
- return h(
- DropdownMenuItem,
- {
- key: rpcTarget,
- onClick: () => props.dispatch(actions.setRpcTarget(rpcTarget)),
- closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
- },
- [
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- label,
- h('.check', '✓'),
- ]
- )
- }
-}
-
App.prototype.getNetworkName = function () {
const { provider } = this.props
const providerName = provider.type
@@ -684,28 +310,3 @@ App.prototype.getNetworkName = function () {
return name
}
-
-App.prototype.renderCommonRpc = function (rpcList, provider) {
- const props = this.props
- const rpcTarget = provider.rpcTarget
-
- return rpcList.map((rpc) => {
- if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) {
- return null
- } else {
- return h(
- DropdownMenuItem,
- {
- key: `common${rpc}`,
- closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
- onClick: () => props.dispatch(actions.setRpcTarget(rpc)),
- },
- [
- h('i.fa.fa-question-circle.fa-lg.menu-icon'),
- rpc,
- rpcTarget === rpc ? h('.check', '✓') : null,
- ]
- )
- }
- })
-}
diff --git a/old-ui/app/components/app-bar.js b/old-ui/app/components/app-bar.js
new file mode 100644
index 000000000..8ab647efd
--- /dev/null
+++ b/old-ui/app/components/app-bar.js
@@ -0,0 +1,432 @@
+const PropTypes = require('prop-types')
+const {Component} = require('react')
+const h = require('react-hyperscript')
+const actions = require('../../../ui/app/actions')
+const SandwichExpando = require('sandwich-expando')
+const {Dropdown} = require('./dropdown')
+const {DropdownMenuItem} = require('./dropdown')
+const NetworkIndicator = require('./network')
+const {AccountDropdowns} = require('./account-dropdowns')
+
+const LOCALHOST_RPC_URL = 'http://localhost:8545'
+
+module.exports = class AppBar extends Component {
+ static defaultProps = {
+ selectedAddress: undefined,
+ }
+
+ static propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ frequentRpcList: PropTypes.array.isRequired,
+ isMascara: PropTypes.bool.isRequired,
+ isOnboarding: PropTypes.bool.isRequired,
+ identities: PropTypes.any.isRequired,
+ selectedAddress: PropTypes.string,
+ isUnlocked: PropTypes.bool.isRequired,
+ network: PropTypes.any.isRequired,
+ keyrings: PropTypes.any.isRequired,
+ provider: PropTypes.any.isRequired,
+ }
+
+ static renderSpace () {
+ return (
+ h('span', {
+ dangerouslySetInnerHTML: {
+ __html: '&nbsp;',
+ },
+ })
+ )
+ }
+
+ state = {
+ isNetworkMenuOpen: false,
+ }
+
+ renderAppBar () {
+ if (window.METAMASK_UI_TYPE === 'notification') {
+ return null
+ }
+
+ const props = this.props
+ const {isMascara, isOnboarding} = props
+
+ // Do not render header if user is in mascara onboarding
+ if (isMascara && isOnboarding) {
+ return null
+ }
+
+ // Do not render header if user is in mascara buy ether
+ if (isMascara && props.currentView.name === 'buyEth') {
+ return null
+ }
+
+ return (
+ h('div.app-bar', [
+ this.renderAppBarNewUiNotice(),
+ this.renderAppBarAppHeader(),
+ ])
+ )
+ }
+
+ renderAppBarNewUiNotice () {
+ const {dispatch} = this.props
+
+ return (
+ h('div.app-bar__new-ui-banner', {
+ style: {
+ height: '28px',
+ zIndex: 12,
+ },
+ }, [
+ 'Try the New MetaMask',
+ AppBar.renderSpace(),
+ h('span.banner__link', {
+ async onClick () {
+ await dispatch(actions.setFeatureFlag('betaUI', true))
+ global.platform.openExtensionInBrowser()
+ },
+ }, [
+ 'Now',
+ ]),
+ AppBar.renderSpace(),
+ 'or',
+ AppBar.renderSpace(),
+ h('span.banner__link', {
+ onClick () {
+ global.platform.openWindow({
+ url: 'https://medium.com/metamask/74dba32cc7f7',
+ })
+ },
+ }, [
+ 'Learn More',
+ ]),
+ ])
+ )
+ }
+
+ renderAppBarAppHeader () {
+ const {
+ identities,
+ selectedAddress,
+ isUnlocked,
+ network,
+ keyrings,
+ provider,
+ } = this.props
+ const {
+ isNetworkMenuOpen,
+ isMainMenuOpen,
+ } = this.state
+
+ return (
+ h('.full-width', {
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ height: '38px',
+ },
+ }, [
+ h('.app-header.flex-row.flex-space-between', {
+ style: {
+ alignItems: 'center',
+ visibility: isUnlocked ? 'visible' : 'none',
+ background: isUnlocked ? 'white' : 'none',
+ height: '38px',
+ position: 'relative',
+ zIndex: 12,
+ },
+ }, [
+ h('div.left-menu-section', {
+ style: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ }, [
+ // mini logo
+ h('img', {
+ height: 24,
+ width: 24,
+ src: './images/icon-128.png',
+ }),
+ h(NetworkIndicator, {
+ network: network,
+ provider: provider,
+ onClick: (event) => {
+ event.preventDefault()
+ event.stopPropagation()
+ this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
+ },
+ }),
+ ]),
+ isUnlocked && h('div', {
+ style: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ }, [
+ h(AccountDropdowns, {
+ style: {},
+ enableAccountsSelector: true,
+ identities: identities,
+ selected: selectedAddress,
+ network,
+ keyrings,
+ }, []),
+ h(SandwichExpando, {
+ className: 'sandwich-expando',
+ width: 16,
+ barHeight: 2,
+ padding: 0,
+ isOpen: isMainMenuOpen,
+ color: 'rgb(247,146,30)',
+ onClick: () => {
+ this.setState({
+ isMainMenuOpen: !isMainMenuOpen,
+ })
+ },
+ }),
+ ]),
+ ]),
+ ])
+ )
+ }
+
+ renderNetworkDropdown () {
+ const {
+ dispatch,
+ frequentRpcList: rpcList,
+ provider,
+ } = this.props
+ const {
+ type: providerType,
+ rpcTarget: activeNetwork,
+ } = provider
+ const isOpen = this.state.isNetworkMenuOpen
+
+ return h(Dropdown, {
+ useCssTransition: true,
+ isOpen,
+ onClickOutside: (event) => {
+ const { classList } = event.target
+ const isNotToggleElement = [
+ classList.contains('menu-icon'),
+ classList.contains('network-name'),
+ classList.contains('network-indicator'),
+ ].filter(bool => bool).length === 0
+ // classes from three constituent nodes of the toggle element
+
+ if (isNotToggleElement) {
+ this.setState({ isNetworkMenuOpen: false })
+ }
+ },
+ zIndex: 11,
+ style: {
+ position: 'absolute',
+ left: '2px',
+ top: '64px',
+ },
+ innerStyle: {
+ padding: '2px 16px 2px 0px',
+ },
+ }, [
+ h(DropdownMenuItem, {
+ key: 'main',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('mainnet')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('.menu-icon.diamond'),
+ 'Main Ethereum Network',
+ providerType === 'mainnet'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ h(DropdownMenuItem, {
+ key: 'ropsten',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('ropsten')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('.menu-icon.red-dot'),
+ 'Ropsten Test Network',
+ providerType === 'ropsten'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ h(DropdownMenuItem, {
+ key: 'kovan',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('kovan')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('.menu-icon.hollow-diamond'),
+ 'Kovan Test Network',
+ providerType === 'kovan'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ h(DropdownMenuItem, {
+ key: 'rinkeby',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('rinkeby')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('.menu-icon.golden-square'),
+ 'Rinkeby Test Network',
+ providerType === 'rinkeby'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ h(DropdownMenuItem, {
+ key: 'default',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.setProviderType('localhost')),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ 'Localhost 8545',
+ activeNetwork === LOCALHOST_RPC_URL
+ ? h('.check', '✓')
+ : null,
+ ]),
+
+ this.renderCustomOption(provider),
+ this.renderCommonRpc(rpcList, provider),
+
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
+ onClick: () => dispatch(actions.showConfigPage()),
+ style: {
+ fontSize: '18px',
+ },
+ }, [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ 'Custom RPC',
+ activeNetwork === 'custom'
+ ? h('.check', '✓')
+ : null,
+ ]),
+ ])
+ }
+
+ renderCustomOption ({ rpcTarget, type }) {
+ const {dispatch} = this.props
+
+ if (type !== 'rpc') {
+ return null
+ }
+
+ // Concatenate long URLs
+ let label = rpcTarget
+ if (rpcTarget.length > 31) {
+ label = label.substr(0, 34) + '...'
+ }
+
+ switch (rpcTarget) {
+ case LOCALHOST_RPC_URL:
+ return null
+ default:
+ return h(DropdownMenuItem, {
+ key: rpcTarget,
+ onClick: () => dispatch(actions.setRpcTarget(rpcTarget)),
+ closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
+ }, [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ label,
+ h('.check', '✓'),
+ ])
+ }
+ }
+
+ renderCommonRpc (rpcList, {rpcTarget}) {
+ const {dispatch} = this.props
+
+ return rpcList.map((rpc) => {
+ if ((rpc === LOCALHOST_RPC_URL) || (rpc === rpcTarget)) {
+ return null
+ } else {
+ return h(DropdownMenuItem, {
+ key: `common${rpc}`,
+ closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
+ onClick: () => dispatch(actions.setRpcTarget(rpc)),
+ }, [
+ h('i.fa.fa-question-circle.fa-lg.menu-icon'),
+ rpc,
+ rpcTarget === rpc
+ ? h('.check', '✓')
+ : null,
+ ])
+ }
+ })
+ }
+
+ renderDropdown () {
+ const {dispatch} = this.props
+ const isOpen = this.state.isMainMenuOpen
+
+ return h(Dropdown, {
+ useCssTransition: true,
+ isOpen: isOpen,
+ zIndex: 11,
+ onClickOutside: (event) => {
+ const classList = event.target.classList
+ const parentClassList = event.target.parentElement.classList
+
+ const isToggleElement = classList.contains('sandwich-expando') ||
+ parentClassList.contains('sandwich-expando')
+
+ if (isOpen && !isToggleElement) {
+ this.setState({ isMainMenuOpen: false })
+ }
+ },
+ style: {
+ position: 'absolute',
+ right: '2px',
+ top: '66px',
+ },
+ innerStyle: {},
+ }, [
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ onClick: () => { dispatch(actions.showConfigPage()) },
+ }, 'Settings'),
+
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ onClick: () => { dispatch(actions.lockMetamask()) },
+ }, 'Log Out'),
+
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ onClick: () => { dispatch(actions.showInfoPage()) },
+ }, 'Info/Help'),
+
+ h(DropdownMenuItem, {
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ onClick: () => {
+ dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
+ },
+ }, 'Try Beta!'),
+ ])
+ }
+
+ render () {
+ return h('div.full-width', [
+ this.renderAppBar(),
+ this.renderNetworkDropdown(),
+ this.renderDropdown(),
+ ])
+ }
+}
diff --git a/old-ui/app/components/qr-code.js b/old-ui/app/components/qr-code.js
deleted file mode 100644
index 06b9aed9b..000000000
--- a/old-ui/app/components/qr-code.js
+++ /dev/null
@@ -1,79 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const qrCode = require('qrcode-npm').qrcode
-const inherits = require('util').inherits
-const connect = require('react-redux').connect
-const isHexPrefixed = require('ethereumjs-util').isHexPrefixed
-const CopyButton = require('./copyButton')
-
-module.exports = connect(mapStateToProps)(QrCodeView)
-
-function mapStateToProps (state) {
- return {
- Qr: state.appState.Qr,
- buyView: state.appState.buyView,
- warning: state.appState.warning,
- }
-}
-
-inherits(QrCodeView, Component)
-
-function QrCodeView () {
- Component.call(this)
-}
-
-QrCodeView.prototype.render = function () {
- const props = this.props
- const Qr = props.Qr
- const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}`
- const qrImage = qrCode(4, 'M')
- qrImage.addData(address)
- qrImage.make()
- return h('.main-container.flex-column', {
- key: 'qr',
- style: {
- justifyContent: 'center',
- paddingBottom: '45px',
- paddingLeft: '45px',
- paddingRight: '45px',
- alignItems: 'center',
- },
- }, [
- Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('.qr-header', Qr.message),
-
- this.props.warning ? this.props.warning && h('span.error.flex-center', {
- style: {
- textAlign: 'center',
- width: '229px',
- height: '82px',
- },
- },
- this.props.warning) : null,
-
- h('#qr-container.flex-column', {
- style: {
- marginTop: '25px',
- marginBottom: '15px',
- },
- dangerouslySetInnerHTML: {
- __html: qrImage.createTableTag(4),
- },
- }),
- h('.flex-row', [
- h('h3.ellip-address', {
- style: {
- width: '247px',
- },
- }, Qr.data),
- h(CopyButton, {
- value: Qr.data,
- }),
- ]),
- ])
-}
-
-QrCodeView.prototype.renderMultiMessage = function () {
- var Qr = this.props.Qr
- var multiMessage = Qr.message.map((message) => h('.qr-message', message))
- return multiMessage
-}
diff --git a/old-ui/app/components/shapeshift-form.js b/old-ui/app/components/shapeshift-form.js
index 97068db0a..14de309ab 100644
--- a/old-ui/app/components/shapeshift-form.js
+++ b/old-ui/app/components/shapeshift-form.js
@@ -3,7 +3,6 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../../ui/app/actions')
-const Qr = require('./qr-code')
const isValidAddress = require('../util').isValidAddress
module.exports = connect(mapStateToProps)(ShapeshiftForm)
@@ -11,7 +10,6 @@ function mapStateToProps (state) {
return {
warning: state.appState.warning,
isSubLoading: state.appState.isSubLoading,
- qrRequested: state.appState.qrRequested,
}
}
@@ -23,7 +21,7 @@ function ShapeshiftForm () {
}
ShapeshiftForm.prototype.render = function () {
- return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain()
+ return this.renderMain()
}
ShapeshiftForm.prototype.renderMain = function () {
diff --git a/old-ui/app/components/transaction-list-item.js b/old-ui/app/components/transaction-list-item.js
index f479ce666..015ef6ba6 100644
--- a/old-ui/app/components/transaction-list-item.js
+++ b/old-ui/app/components/transaction-list-item.js
@@ -36,7 +36,7 @@ TransactionListItem.prototype.showRetryButton = function () {
return false
}
- let currentTxIsLatest = false
+ let currentTxSharesEarliestNonce = false
const currentNonce = txParams.nonce
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
@@ -45,14 +45,14 @@ TransactionListItem.prototype.showRetryButton = function () {
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
lastSubmittedTxWithCurrentNonce.id === transaction.id
if (currentSubmittedTxs.length > 0) {
- const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
+ const earliestSubmitted = currentSubmittedTxs.reduce((tx1, tx2) => {
if (tx1.submittedTime < tx2.submittedTime) return tx1
return tx2
})
- currentTxIsLatest = lastTx.id === transaction.id
+ currentTxSharesEarliestNonce = currentNonce === earliestSubmitted.txParams.nonce
}
- return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000 && currentTxIsLatest
+ return currentTxSharesEarliestNonce && currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
}
TransactionListItem.prototype.render = function () {
diff --git a/old-ui/app/css/index.css b/old-ui/app/css/index.css
index 7af713336..d209b4754 100644
--- a/old-ui/app/css/index.css
+++ b/old-ui/app/css/index.css
@@ -720,7 +720,131 @@ div.message-container > div:first-child {
transform: scale(1.1);
}
-//Notification Modal
+.new-ui-announcement {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ background: white;
+ color: #4D4D4D;
+ font-family: Roboto, Arial, sans-serif;
+ padding: 1.5rem;
+}
+
+.new-ui-announcement__announcement-header {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ padding-bottom: 1rem;
+}
+
+.new-ui-announcement__announcement-header a.close {
+ cursor: pointer;
+ font-size: 32px;
+ line-height: 17px;
+}
+
+.new-ui-announcement__announcement-header a.close:hover {
+ color: inherit;
+}
+
+.new-ui-announcement__announcement-header h1 {
+ color: #33A4E7;
+ text-transform: uppercase;
+ font-size: 18px;
+ font-weight: 400;
+}
+
+.new-ui-announcement__body {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ font-size: 10.5pt;
+ font-weight: 300;
+}
+
+.new-ui-announcement__body h1 {
+ font-size: 22px;
+ font-weight: 600;
+ padding-bottom: 1rem;
+}
+
+.new-ui-announcement__body a {
+ color: #33A4E7;
+}
+
+.new-ui-announcement__body .updates-list {
+ padding: .5rem 1rem;
+}
+
+.new-ui-announcement__body .updates-list h2 {
+ font-weight: 600;
+}
+
+.new-ui-announcement__body .updates-list ul {
+ list-style: disc inside;
+}
+
+.new-ui-announcement__footer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.new-ui-announcement__footer h1 {
+ font-family: inherit;
+ font-weight: 600;
+}
+
+.new-ui-announcement__footer button:hover {
+ transform: none;
+}
+
+.new-ui-announcement__footer button.positive {
+ padding: 1rem;
+ margin: 1rem;
+ background: #33A4E7;
+ color: white;
+ text-transform: uppercase;
+ box-shadow: none;
+ border-radius: 5px;
+ font-family: inherit;
+ font-size: 13px;
+ font-weight: 400;
+ width: 90%;
+}
+
+.new-ui-announcement__footer button.negative {
+ margin: 0;
+ padding: 0;
+ background: white;
+ color: #33A4E7;
+ font-family: inherit;
+ font-size: 13px;
+ font-weight: 400;
+ box-shadow: none;
+}
+
+.app-bar {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.app-bar__new-ui-banner {
+ background: #33A4E7;
+ color: white;
+ font-size: 12px;
+ line-height: 12px;
+ padding: 8px;
+ font-family: Roboto, Arial, sans-serif;
+ font-weight: 400;
+ width: 100%;
+}
+
+.banner__link {
+ cursor: pointer;
+ text-decoration: underline;
+}
.notification-modal-wrapper {
display: flex;
@@ -812,4 +936,4 @@ div.message-container > div:first-child {
.notification-modal__link {
color: #2f9ae0;
-} \ No newline at end of file
+}
diff --git a/old-ui/app/new-ui-annoucement.js b/old-ui/app/new-ui-annoucement.js
new file mode 100644
index 000000000..59b126279
--- /dev/null
+++ b/old-ui/app/new-ui-annoucement.js
@@ -0,0 +1,85 @@
+const PropTypes = require('prop-types')
+const {PureComponent} = require('react')
+const h = require('react-hyperscript')
+const actions = require('../../ui/app/actions')
+
+module.exports = class NewUiAnnouncement extends PureComponent {
+ static propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ };
+
+ close = async () => {
+ await this.props.dispatch(actions.setFeatureFlag('skipAnnounceBetaUI', true))
+ }
+
+ switchToNewUi = async () => {
+ const flag = 'betaUI'
+ const enabled = true
+ await this.props.dispatch(actions.setFeatureFlag(
+ flag,
+ enabled,
+ ))
+ await this.close()
+ global.platform.openExtensionInBrowser()
+ }
+
+ render () {
+ return (
+ h('div.new-ui-announcement', [
+ h('section.new-ui-announcement__announcement-header', [
+ h('h1', 'Announcement'),
+ h('a.close', {
+ onClick: this.close,
+ }, '×'),
+ ]),
+ h('section.new-ui-announcement__body', [
+ h('h1', 'A New Version of MetaMask'),
+ h('p', [
+ "We're excited to announce a brand-new version of MetaMask with enhanced features and functionality.",
+ ]),
+ h('div.updates-list', [
+ h('h2', 'Updates include'),
+ h('ul', [
+ h('li', 'New user interface'),
+ h('li', 'Full-screen mode'),
+ h('li', 'Better token support'),
+ h('li', 'Better gas controls'),
+ h('li', 'Advanced features for developers'),
+ h('li', 'New confirmation screens'),
+ h('li', 'And more!'),
+ ]),
+ ]),
+ h('p', [
+ 'You can still use the current version of MetaMask. The new version is still in beta, ' +
+ 'however we encourage you to try it out as we transition into this exciting new update.',
+ h('span', {
+ dangerouslySetInnerHTML: {
+ __html: '&nbsp;',
+ },
+ }),
+ h('a', {
+ href: 'https://medium.com/metamask/74dba32cc7f7',
+ onClick ({target}) {
+ const url = target.href
+ global.platform.openWindow({
+ url,
+ })
+ },
+ }, [
+ 'Learn more.',
+ ]),
+ ]),
+ ]),
+ h('section.new-ui-announcement__footer', [
+ h('h1', 'Ready to try the new MetaMask?'),
+ h('button.positive', {
+ onClick: this.switchToNewUi,
+ }, 'Try it now'),
+ h('button.negative', {
+ onClick: this.close,
+ }, 'No thanks, maybe later'),
+ ]),
+ ])
+ )
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index 074e90cce..0efe9b481 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1986,14 +1986,6 @@
}
}
},
- "ansi-align": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz",
- "integrity": "sha1-LwwWWIKXOa3V67FeawxuNCPwFro=",
- "requires": {
- "string-width": "^1.0.1"
- }
- },
"ansi-colors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.0.1.tgz",
@@ -2318,11 +2310,6 @@
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
},
- "as-array": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/as-array/-/as-array-2.0.0.tgz",
- "integrity": "sha1-TwSAXYf4/OjlEbwhCPjl46KH1Uc="
- },
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -3970,19 +3957,6 @@
"integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
"dev": true
},
- "basic-auth": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
- "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
- "requires": {
- "safe-buffer": "5.1.1"
- }
- },
- "basic-auth-connect": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz",
- "integrity": "sha1-/bC0OWLKe0BFanwrtI/hc9otISI="
- },
"bcrypt-pbkdf": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
@@ -4251,29 +4225,6 @@
"integrity": "sha512-/gp96UlcFw5DbV2KQPCqTqi0Mb9gZRyDAHiDsGEH+4B/KOQjeoE5lM1PxlVX8DQDvfEfitmC1rW2Oy8fk/XBDg==",
"dev": true
},
- "boxen": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz",
- "integrity": "sha1-g2TUJIrDT/DvGy8r9JpsYM4NgbY=",
- "requires": {
- "ansi-align": "^1.1.0",
- "camelcase": "^2.1.0",
- "chalk": "^1.1.1",
- "cli-boxes": "^1.0.0",
- "filled-array": "^1.0.0",
- "object-assign": "^4.0.1",
- "repeating": "^2.0.0",
- "string-width": "^1.0.1",
- "widest-line": "^1.0.0"
- },
- "dependencies": {
- "camelcase": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
- "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
- }
- }
- },
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
@@ -5010,7 +4961,8 @@
"capture-stack-trace": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
- "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
+ "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=",
+ "dev": true
},
"case-sensitive-paths-webpack-plugin": {
"version": "2.1.2",
@@ -5084,11 +5036,6 @@
"resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
"integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
},
- "char-spinner": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/char-spinner/-/char-spinner-1.0.1.tgz",
- "integrity": "sha1-5upnvSR+EHESmDt6sEee02KAAIE="
- },
"character-entities": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz",
@@ -5169,16 +5116,16 @@
"dev": true
},
"chromedriver": {
- "version": "2.36.0",
- "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.36.0.tgz",
- "integrity": "sha512-Lq2HrigCJ4RVdIdCmchenv1rVrejNSJ7EUCQojycQo12ww3FedQx4nb+GgTdqMhjbOMTqq5+ziaiZlrEN2z1gQ==",
+ "version": "2.41.0",
+ "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.41.0.tgz",
+ "integrity": "sha512-6O9HxvrSuHqmRlIgMzi0/05GsDNHqs8kaF5gNTIyaZNwRzb/RBUWH1xNNXKNxyhXSnGSalH8hWsKP5mc/npSQQ==",
"dev": true,
"requires": {
"del": "^3.0.0",
- "extract-zip": "^1.6.5",
+ "extract-zip": "^1.6.7",
"kew": "^0.7.0",
"mkdirp": "^0.5.1",
- "request": "^2.83.0"
+ "request": "^2.87.0"
}
},
"cipher-base": {
@@ -5312,11 +5259,6 @@
"glob": "^7.1.1"
}
},
- "cli-boxes": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
- "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
- },
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -5810,14 +5752,6 @@
}
}
},
- "compare-semver": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/compare-semver/-/compare-semver-1.1.0.tgz",
- "integrity": "sha1-fAp5onu4C2xplERfgpWCWdPQIVM=",
- "requires": {
- "semver": "^5.0.1"
- }
- },
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
@@ -5839,6 +5773,7 @@
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.12.tgz",
"integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=",
+ "dev": true,
"requires": {
"mime-db": ">= 1.30.0 < 2"
}
@@ -5847,6 +5782,7 @@
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.1.tgz",
"integrity": "sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=",
+ "dev": true,
"requires": {
"accepts": "~1.3.4",
"bytes": "3.0.0",
@@ -5882,26 +5818,11 @@
"proto-list": "~1.2.1"
}
},
- "configstore": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz",
- "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=",
- "requires": {
- "dot-prop": "^3.0.0",
- "graceful-fs": "^4.1.2",
- "mkdirp": "^0.5.0",
- "object-assign": "^4.0.1",
- "os-tmpdir": "^1.0.0",
- "osenv": "^0.1.0",
- "uuid": "^2.0.1",
- "write-file-atomic": "^1.1.2",
- "xdg-basedir": "^2.0.0"
- }
- },
"connect": {
"version": "3.6.6",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
"integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=",
+ "dev": true,
"requires": {
"debug": "2.6.9",
"finalhandler": "1.1.0",
@@ -5909,21 +5830,6 @@
"utils-merge": "1.0.1"
}
},
- "connect-query": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/connect-query/-/connect-query-1.0.0.tgz",
- "integrity": "sha1-3kT1dyCdokBNH8BGktGkEY5YIRk=",
- "requires": {
- "qs": "~6.4.0"
- },
- "dependencies": {
- "qs": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
- "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
- }
- }
- },
"console-browserify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
@@ -6085,6 +5991,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
"integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+ "dev": true,
"requires": {
"capture-stack-trace": "^1.0.0"
}
@@ -7476,14 +7383,6 @@
"integrity": "sha1-9k0h7b5v8xFJlfEGGmGpNcMAIEs=",
"dev": true
},
- "dot-prop": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz",
- "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=",
- "requires": {
- "is-obj": "^1.0.0"
- }
- },
"dotenv": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
@@ -8681,12 +8580,13 @@
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"requires": {
+ "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
"ethereumjs-util": "^5.1.1"
},
"dependencies": {
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
- "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7",
+ "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"requires": {
"bn.js": "^4.10.0",
"ethereumjs-util": "^5.0.0"
@@ -9939,30 +9839,33 @@
}
},
"extract-zip": {
- "version": "1.6.6",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz",
- "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=",
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
+ "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
"dev": true,
"requires": {
- "concat-stream": "1.6.0",
+ "concat-stream": "1.6.2",
"debug": "2.6.9",
- "mkdirp": "0.5.0",
+ "mkdirp": "0.5.1",
"yauzl": "2.4.1"
},
"dependencies": {
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
- "mkdirp": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
- "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"dev": true,
"requires": {
- "minimist": "0.0.8"
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
}
}
}
@@ -10403,14 +10306,6 @@
"integrity": "sha512-h2avnhux4p3tXTA9xR7ntnQSFQdY4hAkyNj8wDXlVT2Die38JxVCInnrieuktdxzRevRWa3dBjN+SbQe1os0GQ==",
"dev": true
},
- "fast-url-parser": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
- "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=",
- "requires": {
- "punycode": "^1.3.2"
- }
- },
"fastparse": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz",
@@ -10501,6 +10396,12 @@
}
}
},
+ "file-size": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/file-size/-/file-size-0.0.5.tgz",
+ "integrity": "sha1-BX1Dw6Ptc12j+Q1gUqs4Dx5tXjs=",
+ "dev": true
+ },
"file-tree": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-tree/-/file-tree-1.0.0.tgz",
@@ -10584,11 +10485,6 @@
"repeat-string": "^1.5.2"
}
},
- "filled-array": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz",
- "integrity": "sha1-w8T2xmO5I0WamqKZEtLQMfFQf4Q="
- },
"finalhandler": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
@@ -11003,49 +10899,6 @@
"resolved": "https://registry.npmjs.org/flat/-/flat-1.0.0.tgz",
"integrity": "sha1-Ad/dW8vBScZrNe1AHh11PxqtjVk="
},
- "flat-arguments": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/flat-arguments/-/flat-arguments-1.0.2.tgz",
- "integrity": "sha1-m6p4Ct8FAfKC1ybJxqA426ROp28=",
- "requires": {
- "array-flatten": "^1.0.0",
- "as-array": "^1.0.0",
- "lodash.isarguments": "^3.0.0",
- "lodash.isobject": "^3.0.0"
- },
- "dependencies": {
- "as-array": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/as-array/-/as-array-1.0.0.tgz",
- "integrity": "sha1-KKbu6qVynx9OyiBH316d4avaDtE=",
- "requires": {
- "lodash.isarguments": "2.4.x",
- "lodash.isobject": "^2.4.1",
- "lodash.values": "^2.4.1"
- },
- "dependencies": {
- "lodash.isarguments": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-2.4.1.tgz",
- "integrity": "sha1-STGpwIJTrfCRrnyhkiWKlzh27Mo="
- },
- "lodash.isobject": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz",
- "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=",
- "requires": {
- "lodash._objecttypes": "~2.4.1"
- }
- }
- }
- },
- "lodash.isobject": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
- "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0="
- }
- }
- },
"flat-cache": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
@@ -12756,21 +12609,6 @@
"is-glob": "^2.0.0"
}
},
- "glob-slash": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/glob-slash/-/glob-slash-1.0.0.tgz",
- "integrity": "sha1-/lLvpDMjP3Si/mTHq7m8hIICq5U="
- },
- "glob-slasher": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/glob-slasher/-/glob-slasher-1.0.1.tgz",
- "integrity": "sha1-dHoOW7IiZC7hDT4FRD4QlJPLD44=",
- "requires": {
- "glob-slash": "^1.0.0",
- "lodash.isobject": "^2.4.1",
- "toxic": "^1.0.0"
- }
- },
"glob-stream": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
@@ -12918,56 +12756,6 @@
"sparkles": "^1.0.0"
}
},
- "got": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz",
- "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=",
- "requires": {
- "create-error-class": "^3.0.1",
- "duplexer2": "^0.1.4",
- "is-redirect": "^1.0.0",
- "is-retry-allowed": "^1.0.0",
- "is-stream": "^1.0.0",
- "lowercase-keys": "^1.0.0",
- "node-status-codes": "^1.0.0",
- "object-assign": "^4.0.1",
- "parse-json": "^2.1.0",
- "pinkie-promise": "^2.0.0",
- "read-all-stream": "^3.0.0",
- "readable-stream": "^2.0.5",
- "timed-out": "^3.0.0",
- "unzip-response": "^1.0.2",
- "url-parse-lax": "^1.0.0"
- },
- "dependencies": {
- "duplexer2": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
- "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
- "requires": {
- "readable-stream": "^2.0.2"
- }
- },
- "prepend-http": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
- "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
- },
- "timed-out": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz",
- "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc="
- },
- "url-parse-lax": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
- "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
- "requires": {
- "prepend-http": "^1.0.1"
- }
- }
- }
- },
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
@@ -14629,11 +14417,6 @@
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz",
"integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA="
},
- "home-dir": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/home-dir/-/home-dir-1.0.0.tgz",
- "integrity": "sha1-KRfrRL3JByztqUJXlUOEfjAX/k4="
- },
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -15892,11 +15675,6 @@
"resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
"integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI="
},
- "is-npm": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
- "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
- },
"is-number": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
@@ -16003,7 +15781,8 @@
"is-redirect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
- "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
+ "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
+ "dev": true
},
"is-regex": {
"version": "1.0.4",
@@ -16034,7 +15813,8 @@
"is-retry-allowed": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
- "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ="
+ "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=",
+ "dev": true
},
"is-root": {
"version": "1.0.0",
@@ -16109,11 +15889,6 @@
"unc-path-regex": "^0.1.2"
}
},
- "is-url": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
- "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
- },
"is-utf8": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
@@ -16333,16 +16108,6 @@
"raphael": "^2.2.0"
}
},
- "join-path": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/join-path/-/join-path-1.1.1.tgz",
- "integrity": "sha1-EFNaEm0ky9Zff/zfFe8uYxB2tQU=",
- "requires": {
- "as-array": "^2.0.0",
- "url-join": "0.0.1",
- "valid-url": "^1"
- }
- },
"js-base64": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz",
@@ -17720,19 +17485,6 @@
"es6-weak-map": "^2.0.1"
}
},
- "latest-version": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz",
- "integrity": "sha1-VvjWE5YghHuAF/jx9NeOIRMkFos=",
- "requires": {
- "package-json": "^2.0.0"
- }
- },
- "lazy-req": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz",
- "integrity": "sha1-va6+rTD42CQDnODOFJ1Nqge6H6w="
- },
"lazystream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
@@ -18398,16 +18150,6 @@
"resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
"integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw="
},
- "lodash._isnative": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz",
- "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw="
- },
- "lodash._objecttypes": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz",
- "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE="
- },
"lodash._reescape": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz",
@@ -18428,14 +18170,6 @@
"resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
"integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI="
},
- "lodash._shimkeys": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz",
- "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=",
- "requires": {
- "lodash._objecttypes": "~2.4.1"
- }
- },
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
@@ -18525,14 +18259,6 @@
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
},
- "lodash.isobject": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz",
- "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=",
- "requires": {
- "lodash._objecttypes": "~2.4.1"
- }
- },
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
@@ -18634,26 +18360,6 @@
"resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
"integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI="
},
- "lodash.values": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz",
- "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=",
- "requires": {
- "lodash.keys": "~2.4.1"
- },
- "dependencies": {
- "lodash.keys": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz",
- "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=",
- "requires": {
- "lodash._isnative": "~2.4.1",
- "lodash._shimkeys": "~2.4.1",
- "lodash.isobject": "~2.4.1"
- }
- }
- }
- },
"log-driver": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.5.tgz",
@@ -18952,7 +18658,8 @@
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
- "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
+ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+ "dev": true
},
"lru-cache": {
"version": "4.1.1",
@@ -20167,18 +19874,6 @@
"integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==",
"dev": true
},
- "morgan": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
- "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
- "requires": {
- "basic-auth": "~2.0.0",
- "debug": "2.6.9",
- "depd": "~1.1.1",
- "on-finished": "~2.3.0",
- "on-headers": "~1.0.1"
- }
- },
"mout": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/mout/-/mout-0.11.1.tgz",
@@ -20381,29 +20076,6 @@
}
}
},
- "nash": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/nash/-/nash-2.0.4.tgz",
- "integrity": "sha1-y5ZHkc79N21Zz6zYAQknRhaqFdI=",
- "requires": {
- "async": "^1.3.0",
- "flat-arguments": "^1.0.0",
- "lodash": "^3.10.0",
- "minimist": "^1.1.0"
- },
- "dependencies": {
- "async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
- },
- "lodash": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
- "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
- }
- }
- },
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -20717,7 +20389,8 @@
"node-status-codes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz",
- "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8="
+ "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=",
+ "dev": true
},
"node-uuid": {
"version": "1.4.8",
@@ -22595,7 +22268,8 @@
"on-headers": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
- "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
+ "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=",
+ "dev": true
},
"once": {
"version": "1.4.0",
@@ -22905,17 +22579,6 @@
"thunkify": "^2.1.2"
}
},
- "package-json": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz",
- "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=",
- "requires": {
- "got": "^5.0.0",
- "registry-auth-token": "^3.0.1",
- "registry-url": "^3.0.3",
- "semver": "^5.1.0"
- }
- },
"pako": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
@@ -25669,6 +25332,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "optional": true,
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@@ -25679,7 +25343,8 @@
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "optional": true
}
}
},
@@ -26252,6 +25917,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz",
"integrity": "sha1-NcPhd/IHjveJ7kv6+kNzB06u9Po=",
+ "dev": true,
"requires": {
"pinkie-promise": "^2.0.0",
"readable-stream": "^2.0.0"
@@ -26584,23 +26250,6 @@
"regjsparser": "^0.1.4"
}
},
- "registry-auth-token": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
- "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
- "requires": {
- "rc": "^1.1.6",
- "safe-buffer": "^5.0.1"
- }
- },
- "registry-url": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
- "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
- "requires": {
- "rc": "^1.0.1"
- }
- },
"regjsgen": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
@@ -27129,27 +26778,6 @@
"resolved": "https://registry.npmjs.org/rlp/-/rlp-2.0.0.tgz",
"integrity": "sha1-nbOE/0uJqPYVY9kjldhiWxjzr7A="
},
- "router": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/router/-/router-1.3.2.tgz",
- "integrity": "sha1-v6oWiIpSg9XuQNmZ2nqfoVKWpgw=",
- "requires": {
- "array-flatten": "2.1.1",
- "debug": "2.6.9",
- "methods": "~1.1.2",
- "parseurl": "~1.3.2",
- "path-to-regexp": "0.1.7",
- "setprototypeof": "1.1.0",
- "utils-merge": "1.0.1"
- },
- "dependencies": {
- "array-flatten": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz",
- "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY="
- }
- }
- },
"rst-selector-parser": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
@@ -27160,11 +26788,6 @@
"nearley": "^2.7.10"
}
},
- "rsvp": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz",
- "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw=="
- },
"run-async": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
@@ -27557,14 +27180,6 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
},
- "semver-diff": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
- "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
- "requires": {
- "semver": "^5.0.3"
- }
- },
"semver-greatest-satisfied-range": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz",
@@ -28014,7 +27629,8 @@
"slide": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
- "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
+ "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=",
+ "dev": true
},
"smart-buffer": {
"version": "1.1.15",
@@ -28661,6 +28277,70 @@
}
}
},
+ "static-server": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/static-server/-/static-server-2.2.1.tgz",
+ "integrity": "sha512-j5eeW6higxYNmXMIT8iHjsdiViTpQDthg7o+SHsRtqdbxscdHqBHXwrXjHC8hL3F0Tsu34ApUpDkwzMBPBsrLw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^0.5.1",
+ "commander": "^2.3.0",
+ "file-size": "0.0.5",
+ "mime": "^1.2.11",
+ "opn": "^5.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+ "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
+ "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+ "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^1.1.0",
+ "escape-string-regexp": "^1.0.0",
+ "has-ansi": "^0.1.0",
+ "strip-ansi": "^0.3.0",
+ "supports-color": "^0.2.0"
+ }
+ },
+ "has-ansi": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
+ "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^0.2.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+ "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^0.2.1"
+ }
+ },
+ "supports-color": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
+ "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
+ "dev": true
+ }
+ }
+ },
"statuses": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
@@ -28861,6 +28541,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz",
"integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=",
+ "dev": true,
"requires": {
"strip-ansi": "^3.0.0"
}
@@ -29566,63 +29247,6 @@
}
}
},
- "superstatic": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/superstatic/-/superstatic-5.0.2.tgz",
- "integrity": "sha1-186TKe4w2qp2KwHUO1SNC3MGulQ=",
- "requires": {
- "as-array": "^2.0.0",
- "async": "^1.5.2",
- "basic-auth-connect": "^1.0.0",
- "chalk": "^1.1.3",
- "char-spinner": "^1.0.1",
- "compare-semver": "^1.0.0",
- "compression": "^1.7.0",
- "connect": "^3.6.2",
- "connect-query": "^1.0.0",
- "destroy": "^1.0.4",
- "fast-url-parser": "^1.1.3",
- "fs-extra": "^0.30.0",
- "glob": "^7.1.2",
- "glob-slasher": "^1.0.1",
- "home-dir": "^1.0.0",
- "is-url": "^1.2.2",
- "join-path": "^1.1.1",
- "lodash": "^4.17.4",
- "mime-types": "^2.1.16",
- "minimatch": "^3.0.4",
- "morgan": "^1.8.2",
- "nash": "^2.0.4",
- "on-finished": "^2.2.0",
- "on-headers": "^1.0.0",
- "path-to-regexp": "^1.7.0",
- "router": "^1.3.1",
- "rsvp": "^3.6.2",
- "string-length": "^1.0.0",
- "try-require": "^1.0.0",
- "update-notifier": "^1.0.3"
- },
- "dependencies": {
- "async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
- },
- "isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
- },
- "path-to-regexp": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
- "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
- "requires": {
- "isarray": "0.0.1"
- }
- }
- }
- },
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
@@ -30485,14 +30109,6 @@
"punycode": "^1.4.1"
}
},
- "toxic": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toxic/-/toxic-1.0.1.tgz",
- "integrity": "sha512-WI3rIGdcaKULYg7KVoB0zcjikqvcYYvcuT6D89bFPz2rVR0Rl0PK6x8/X62rtdLtBKIE985NzVf/auTtGegIIg==",
- "requires": {
- "lodash": "^4.17.10"
- }
- },
"tr46": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
@@ -30626,11 +30242,6 @@
}
}
},
- "try-require": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/try-require/-/try-require-1.2.1.tgz",
- "integrity": "sha1-NEiaLKwMCcHMEO2RugEVlNQzO+I="
- },
"tslib": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz",
@@ -31149,7 +30760,8 @@
"unzip-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz",
- "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4="
+ "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=",
+ "dev": true
},
"upath": {
"version": "1.0.4",
@@ -31157,21 +30769,6 @@
"integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw==",
"dev": true
},
- "update-notifier": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz",
- "integrity": "sha1-j5LFFUgr1oMbfJMBPnD4dVLHz1o=",
- "requires": {
- "boxen": "^0.6.0",
- "chalk": "^1.0.0",
- "configstore": "^2.0.0",
- "is-npm": "^1.0.0",
- "latest-version": "^2.0.0",
- "lazy-req": "^1.1.0",
- "semver-diff": "^2.0.0",
- "xdg-basedir": "^2.0.0"
- }
- },
"upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
@@ -31214,11 +30811,6 @@
}
}
},
- "url-join": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz",
- "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g="
- },
"url-loader": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz",
@@ -33317,14 +32909,6 @@
"string-width": "^1.0.2"
}
},
- "widest-line": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz",
- "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=",
- "requires": {
- "string-width": "^1.0.1"
- }
- },
"window-size": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz",
@@ -33450,6 +33034,7 @@
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz",
"integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=",
+ "dev": true,
"requires": {
"graceful-fs": "^4.1.11",
"imurmurhash": "^0.1.4",
@@ -33483,14 +33068,6 @@
"resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz",
"integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI="
},
- "xdg-basedir": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz",
- "integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=",
- "requires": {
- "os-homedir": "^1.0.0"
- }
- },
"xhr": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/xhr/-/xhr-2.4.1.tgz",
diff --git a/package.json b/package.json
index 96b0829db..4536546f4 100644
--- a/package.json
+++ b/package.json
@@ -198,7 +198,6 @@
"semaphore": "^1.0.5",
"semver": "^5.4.1",
"shallow-copy": "0.0.1",
- "superstatic": "^5.0.2",
"sw-controller": "^1.0.3",
"sw-stream": "^2.0.2",
"textarea-caret": "^3.0.1",
@@ -228,7 +227,7 @@
"brfs": "^1.6.1",
"browserify": "^16.1.1",
"chai": "^4.1.0",
- "chromedriver": "2.36.0",
+ "chromedriver": "^2.41.0",
"clipboardy": "^1.2.3",
"compression": "^1.7.1",
"coveralls": "^3.0.0",
@@ -308,6 +307,7 @@
"shell-parallel": "^1.0.3",
"sinon": "^5.0.0",
"source-map": "^0.7.2",
+ "static-server": "^2.2.1",
"style-loader": "^0.21.0",
"stylelint-config-standard": "^18.2.0",
"tape": "^4.5.1",
diff --git a/test/e2e/beta/contract-test/contract.js b/test/e2e/beta/contract-test/contract.js
index 8af008dce..51891ea21 100644
--- a/test/e2e/beta/contract-test/contract.js
+++ b/test/e2e/beta/contract-test/contract.js
@@ -50,15 +50,20 @@ deployButton.addEventListener('click', async function (event) {
console.log(`contract`, contract)
+ document.getElementById('contractStatus').innerHTML = 'Deployed'
+
depositButton.addEventListener('click', function (event) {
+ document.getElementById('contractStatus').innerHTML = 'Deposit initiated'
contract.deposit({ from: web3.eth.accounts[0], value: '0x3782dace9d900000' }, function (result) {
console.log(result)
+ document.getElementById('contractStatus').innerHTML = 'Deposit completed'
})
})
withdrawButton.addEventListener('click', function (event) {
contract.withdraw('0xde0b6b3a7640000', { from: web3.eth.accounts[0] }, function (result) {
console.log(result)
+ document.getElementById('contractStatus').innerHTML = 'Withdrawn'
})
})
}
diff --git a/test/e2e/beta/contract-test/index.html b/test/e2e/beta/contract-test/index.html
index 0d63fd940..f6e6f44c7 100644
--- a/test/e2e/beta/contract-test/index.html
+++ b/test/e2e/beta/contract-test/index.html
@@ -10,6 +10,9 @@
<button id="depositButton">Deposit</button>
<button id="withdrawButton">Withdraw</button>
</div>
+ <div id="contractStatus" style="display: flex; font-size: 1rem;">
+ Not yet deployed
+ </div>
</div>
<div style="display: flex; flex-flow: column;">
<div style="display: flex; font-size: 1.25rem;">Send eth</div>
diff --git a/test/e2e/beta/from-import-beta-ui.spec.js b/test/e2e/beta/from-import-beta-ui.spec.js
index 32f57b157..5582d4697 100644
--- a/test/e2e/beta/from-import-beta-ui.spec.js
+++ b/test/e2e/beta/from-import-beta-ui.spec.js
@@ -12,9 +12,11 @@ const {
} = require('../func')
const {
checkBrowserForConsoleErrors,
+ closeAllWindowHandlesExcept,
verboseReportOnFailure,
findElement,
findElements,
+ loadExtension,
} = require('./helpers')
@@ -25,6 +27,7 @@ describe('Using MetaMask with an existing account', function () {
const testSeedPhrase = 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'
const testAddress = '0xE18035BF8712672935FDB4e5e431b1a0183d2DFC'
const testPrivateKey2 = '14abe6f4aab7f9f626fe981c864d0adeb5685f289ac9270c27b8fd790b4235d6'
+ const tinyDelayMs = 500
const regularDelayMs = 1000
const largeDelayMs = regularDelayMs * 2
@@ -74,37 +77,51 @@ describe('Using MetaMask with an existing account', function () {
describe('New UI setup', async function () {
it('switches to first tab', async function () {
+ await delay(tinyDelayMs)
const [firstTab] = await driver.getAllWindowHandles()
await driver.switchTo().window(firstTab)
await delay(regularDelayMs)
})
- it('use the local network', async function () {
- const networkSelector = await findElement(driver, By.css('#network_component'))
- await networkSelector.click()
- await delay(regularDelayMs)
-
- const [localhost] = await findElements(driver, By.xpath(`//li[contains(text(), 'Localhost')]`))
- await localhost.click()
- await delay(regularDelayMs)
- })
-
it('selects the new UI option', async () => {
- const button = await findElement(driver, By.xpath("//p[contains(text(), 'Try Beta Version')]"))
+ try {
+ const overlay = await findElement(driver, By.css('.full-flex-height'))
+ await driver.wait(until.stalenessOf(overlay))
+ } catch (e) {}
+
+ const button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]"))
await button.click()
await delay(regularDelayMs)
// Close all other tabs
- const [oldUi, infoPage, newUi] = await driver.getAllWindowHandles()
-
- const newUiOrInfoPage = newUi || infoPage
- await driver.switchTo().window(oldUi)
- await driver.close()
- if (infoPage !== newUiOrInfoPage) {
- await driver.switchTo().window(infoPage)
- await driver.close()
+ const [tab0, tab1, tab2] = await driver.getAllWindowHandles()
+ await driver.switchTo().window(tab0)
+ await delay(tinyDelayMs)
+
+ let selectedUrl = await driver.getCurrentUrl()
+ await delay(tinyDelayMs)
+ if (tab0 && selectedUrl.match(/popup.html/)) {
+ await closeAllWindowHandlesExcept(driver, tab0)
+ } else if (tab1) {
+ await driver.switchTo().window(tab1)
+ selectedUrl = await driver.getCurrentUrl()
+ await delay(tinyDelayMs)
+ if (selectedUrl.match(/popup.html/)) {
+ await closeAllWindowHandlesExcept(driver, tab1)
+ } else if (tab2) {
+ await driver.switchTo().window(tab2)
+ selectedUrl = await driver.getCurrentUrl()
+ selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2)
+ }
+ } else {
+ throw new Error('popup.html not found')
}
- await driver.switchTo().window(newUiOrInfoPage)
+ await delay(regularDelayMs)
+ const [appTab] = await driver.getAllWindowHandles()
+ await driver.switchTo().window(appTab)
+ await delay(tinyDelayMs)
+
+ await loadExtension(driver, extensionId)
await delay(regularDelayMs)
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
@@ -208,6 +225,16 @@ describe('Using MetaMask with an existing account', function () {
})
describe('Add an account', () => {
+ it('switches to localhost', async () => {
+ const networkDropdown = await findElement(driver, By.css('.network-name'))
+ await networkDropdown.click()
+ await delay(regularDelayMs)
+
+ const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`))
+ await localhost.click()
+ await delay(largeDelayMs)
+ })
+
it('choose Create Account from the account menu', async () => {
await driver.findElement(By.css('.account-menu__icon')).click()
await delay(regularDelayMs)
diff --git a/test/e2e/beta/metamask-beta-ui.spec.js b/test/e2e/beta/metamask-beta-ui.spec.js
index ca1977c5a..3ad5c2d61 100644
--- a/test/e2e/beta/metamask-beta-ui.spec.js
+++ b/test/e2e/beta/metamask-beta-ui.spec.js
@@ -75,30 +75,11 @@ describe('MetaMask', function () {
})
describe('New UI setup', async function () {
- let networkSelector
it('switches to first tab', async function () {
+ await delay(tinyDelayMs)
const [firstTab] = await driver.getAllWindowHandles()
await driver.switchTo().window(firstTab)
await delay(regularDelayMs)
- try {
- networkSelector = await findElement(driver, By.css('#network_component'))
- } catch (e) {
- await loadExtension(driver, extensionId)
- await delay(largeDelayMs * 2)
- networkSelector = await findElement(driver, By.css('#network_component'))
- }
- await delay(regularDelayMs)
- })
-
- it('uses the local network', async function () {
- await networkSelector.click()
- await delay(regularDelayMs)
-
- const networks = await findElements(driver, By.css('.dropdown-menu-item'))
- const localhost = networks[4]
- await driver.wait(until.elementTextMatches(localhost, /Localhost/))
- await localhost.click()
- await delay(regularDelayMs)
})
it('selects the new UI option', async () => {
@@ -107,27 +88,40 @@ describe('MetaMask', function () {
await driver.wait(until.stalenessOf(overlay))
} catch (e) {}
- const button = await findElement(driver, By.xpath("//p[contains(text(), 'Try Beta Version')]"))
+ const button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]"))
await button.click()
await delay(regularDelayMs)
// Close all other tabs
- const [oldUi, tab1, tab2] = await driver.getAllWindowHandles()
- await driver.switchTo().window(oldUi)
- await driver.close()
+ const [tab0, tab1, tab2] = await driver.getAllWindowHandles()
+ await driver.switchTo().window(tab0)
+ await delay(tinyDelayMs)
- await driver.switchTo().window(tab1)
- const tab1Url = await driver.getCurrentUrl()
- if (tab1Url.match(/metamask.io/)) {
- await driver.switchTo().window(tab1)
- await driver.close()
- await driver.switchTo().window(tab2)
- } else if (tab2) {
- await driver.switchTo().window(tab2)
- await driver.close()
+ let selectedUrl = await driver.getCurrentUrl()
+ await delay(tinyDelayMs)
+ if (tab0 && selectedUrl.match(/popup.html/)) {
+ await closeAllWindowHandlesExcept(driver, tab0)
+ } else if (tab1) {
await driver.switchTo().window(tab1)
+ selectedUrl = await driver.getCurrentUrl()
+ await delay(tinyDelayMs)
+ if (selectedUrl.match(/popup.html/)) {
+ await closeAllWindowHandlesExcept(driver, tab1)
+ } else if (tab2) {
+ await driver.switchTo().window(tab2)
+ selectedUrl = await driver.getCurrentUrl()
+ selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2)
+ }
+ } else {
+ throw new Error('popup.html not found')
}
await delay(regularDelayMs)
+ const [appTab] = await driver.getAllWindowHandles()
+ await driver.switchTo().window(appTab)
+ await delay(tinyDelayMs)
+
+ await loadExtension(driver, extensionId)
+ await delay(regularDelayMs)
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
await continueBtn.click()
@@ -201,7 +195,16 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
})
- async function retypeSeedPhrase (words, wasReloaded) {
+ async function clickWordAndWait (word) {
+ const xpathClass = 'backup-phrase__confirm-seed-option backup-phrase__confirm-seed-option--unselected'
+ const xpath = `//button[@class='${xpathClass}' and contains(text(), '${word}')]`
+ const word0 = await findElement(driver, By.xpath(xpath), 10000)
+
+ await word0.click()
+ await delay(tinyDelayMs)
+ }
+
+ async function retypeSeedPhrase (words, wasReloaded, count = 0) {
try {
if (wasReloaded) {
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button')
@@ -215,67 +218,26 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
}
- const word0 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[0]}')]`), 10000)
-
- await word0.click()
- await delay(tinyDelayMs)
-
- const word1 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[1]}')]`), 10000)
-
- await word1.click()
- await delay(tinyDelayMs)
-
- const word2 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[2]}')]`), 10000)
-
- await word2.click()
- await delay(tinyDelayMs)
-
- const word3 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[3]}')]`), 10000)
-
- await word3.click()
- await delay(tinyDelayMs)
+ await clickWordAndWait(words[0])
+ await clickWordAndWait(words[1])
+ await clickWordAndWait(words[2])
+ await clickWordAndWait(words[3])
+ await clickWordAndWait(words[4])
+ await clickWordAndWait(words[5])
+ await clickWordAndWait(words[6])
+ await clickWordAndWait(words[7])
+ await clickWordAndWait(words[8])
+ await clickWordAndWait(words[9])
+ await clickWordAndWait(words[10])
+ await clickWordAndWait(words[11])
- const word4 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[4]}')]`), 10000)
-
- await word4.click()
- await delay(tinyDelayMs)
-
- const word5 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[5]}')]`), 10000)
-
- await word5.click()
- await delay(tinyDelayMs)
-
- const word6 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[6]}')]`), 10000)
-
- await word6.click()
- await delay(tinyDelayMs)
-
- const word7 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[7]}')]`), 10000)
-
- await word7.click()
- await delay(tinyDelayMs)
-
- const word8 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[8]}')]`), 10000)
-
- await word8.click()
- await delay(tinyDelayMs)
-
- const word9 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[9]}')]`), 10000)
-
- await word9.click()
- await delay(tinyDelayMs)
-
- const word10 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[10]}')]`), 10000)
-
- await word10.click()
- await delay(tinyDelayMs)
-
- const word11 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[11]}')]`), 10000)
- await word11.click()
- await delay(tinyDelayMs)
} catch (e) {
- await loadExtension(driver, extensionId)
- await retypeSeedPhrase(words, true)
+ if (count > 2) {
+ throw e
+ } else {
+ await loadExtension(driver, extensionId)
+ await retypeSeedPhrase(words, true, count + 1)
+ }
}
}
@@ -389,6 +351,16 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
})
+ it('switches to localhost', async () => {
+ const networkDropdown = await findElement(driver, By.css('.network-name'))
+ await networkDropdown.click()
+ await delay(regularDelayMs)
+
+ const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`))
+ await localhost.click()
+ await delay(largeDelayMs * 2)
+ })
+
it('balance renders', async () => {
const balance = await findElement(driver, By.css('.balance-display .token-amount'))
await driver.wait(until.elementTextMatches(balance, /100.+ETH/))
@@ -512,7 +484,7 @@ describe('MetaMask', function () {
it('displays the contract creation data', async () => {
const dataTab = await findElement(driver, By.xpath(`//li[contains(text(), 'Data')]`))
- dataTab.click()
+ await dataTab.click()
await delay(regularDelayMs)
await findElement(driver, By.xpath(`//div[contains(text(), '127.0.0.1')]`))
@@ -522,7 +494,7 @@ describe('MetaMask', function () {
assert.equal(confirmDataText.match(/0x608060405234801561001057600080fd5b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff/))
const detailsTab = await findElement(driver, By.xpath(`//li[contains(text(), 'Details')]`))
- detailsTab.click()
+ await detailsTab.click()
await delay(regularDelayMs)
})
@@ -543,9 +515,15 @@ describe('MetaMask', function () {
await driver.switchTo().window(dapp)
await delay(regularDelayMs)
+ let contractStatus = await driver.findElement(By.css('#contractStatus'))
+ await driver.wait(until.elementTextMatches(contractStatus, /Deployed/))
+
const depositButton = await findElement(driver, By.css('#depositButton'))
await depositButton.click()
- await delay(regularDelayMs)
+ await delay(largeDelayMs)
+
+ contractStatus = await driver.findElement(By.css('#contractStatus'))
+ await driver.wait(until.elementTextMatches(contractStatus, /Deposit\sinitiated/))
await driver.switchTo().window(extension)
await delay(largeDelayMs)
diff --git a/test/e2e/beta/run-all.sh b/test/e2e/beta/run-all.sh
index 493e1360a..7da61e504 100755
--- a/test/e2e/beta/run-all.sh
+++ b/test/e2e/beta/run-all.sh
@@ -6,5 +6,5 @@ set -o pipefail
export PATH="$PATH:./node_modules/.bin"
-shell-parallel -s 'npm run ganache:start' -x 'sleep 5 && superstatic test/e2e/beta/contract-test/ --port 8080 --host 127.0.0.1' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
-shell-parallel -s 'npm run ganache:start -- -d' -x 'sleep 5 && superstatic test/e2e/beta/contract-test/ --port 8080 --host 127.0.0.1' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
+shell-parallel -s 'npm run ganache:start' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
+shell-parallel -s 'npm run ganache:start -- -d' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
diff --git a/test/e2e/metamask.spec.js b/test/e2e/metamask.spec.js
index b6efae5b3..ac7600f09 100644
--- a/test/e2e/metamask.spec.js
+++ b/test/e2e/metamask.spec.js
@@ -59,6 +59,13 @@ describe('Metamask popup page', function () {
await driver.switchTo().window(windowHandles[0])
})
+ it('does not select the new UI option', async () => {
+ await delay(300)
+ const button = await driver.findElement(By.xpath("//button[contains(text(), 'No thanks, maybe later')]"))
+ await button.click()
+ await delay(1000)
+ })
+
it('sets provider type to localhost', async function () {
await delay(300)
await setProviderType('localhost')
@@ -133,9 +140,9 @@ describe('Metamask popup page', function () {
})
it('adds a second account', async function () {
- await driver.findElement(By.css('#app-content > div > div.full-width > div > div:nth-child(2) > span > div')).click()
+ await driver.findElement(By.css('div.full-width > div > div:nth-child(2) > span > div')).click()
await delay(300)
- await driver.findElement(By.css('#app-content > div > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(3) > span')).click()
+ await driver.findElement(By.css('div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(3) > span')).click()
})
it('shows account address', async function () {
@@ -146,7 +153,7 @@ describe('Metamask popup page', function () {
it('logs out of the vault', async () => {
await driver.findElement(By.css('.sandwich-expando')).click()
await delay(500)
- const logoutButton = await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)'))
+ const logoutButton = await driver.findElement(By.css('.menu-droppo > li:nth-child(3)'))
assert.equal(await logoutButton.getText(), 'Log Out')
await logoutButton.click()
})
@@ -178,7 +185,7 @@ describe('Metamask popup page', function () {
it('logs out', async function () {
await driver.findElement(By.css('.sandwich-expando')).click()
await delay(200)
- const logOut = await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)'))
+ const logOut = await driver.findElement(By.css('.menu-droppo > li:nth-child(3)'))
assert.equal(await logOut.getText(), 'Log Out')
await logOut.click()
await delay(300)
diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js
index 052d89518..8cacd7f14 100644
--- a/test/integration/lib/first-time.js
+++ b/test/integration/lib/first-time.js
@@ -27,6 +27,11 @@ async function runFirstTimeUsageTest(assert, done) {
const app = $('#app-content')
+ // Selects new ui
+ const tryNewUIButton = (await findAsync(app, 'button.negative'))[0]
+ tryNewUIButton.click()
+ await timeout()
+
// recurse notices
while (true) {
const button = await findAsync(app, 'button')
diff --git a/test/unit/app/controllers/detect-tokens-test.js b/test/unit/app/controllers/detect-tokens-test.js
index 426ffe23a..d6c3fad8a 100644
--- a/test/unit/app/controllers/detect-tokens-test.js
+++ b/test/unit/app/controllers/detect-tokens-test.js
@@ -7,10 +7,11 @@ const PreferencesController = require('../../../../app/scripts/controllers/prefe
describe('DetectTokensController', () => {
const sandbox = sinon.createSandbox()
- let clock
- let keyringMemStore
- before(async () => {
+ let clock, keyringMemStore, network, preferences
+ beforeEach(async () => {
keyringMemStore = new ObservableStore({ isUnlocked: false})
+ network = new NetworkController({ provider: { type: 'mainnet' }})
+ preferences = new PreferencesController({ network })
})
after(() => {
sandbox.restore()
@@ -25,9 +26,7 @@ describe('DetectTokensController', () => {
it('should be called on every polling period', async () => {
clock = sandbox.useFakeTimers()
- const network = new NetworkController()
network.setProviderType('mainnet')
- const preferences = new PreferencesController()
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -45,9 +44,7 @@ describe('DetectTokensController', () => {
})
it('should not check tokens while in test network', async () => {
- const network = new NetworkController()
network.setProviderType('rinkeby')
- const preferences = new PreferencesController()
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -61,9 +58,7 @@ describe('DetectTokensController', () => {
})
it('should only check and add tokens while in main network', async () => {
- const network = new NetworkController()
network.setProviderType('mainnet')
- const preferences = new PreferencesController()
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -80,9 +75,7 @@ describe('DetectTokensController', () => {
})
it('should not detect same token while in main network', async () => {
- const network = new NetworkController()
network.setProviderType('mainnet')
- const preferences = new PreferencesController()
preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8)
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
@@ -100,9 +93,7 @@ describe('DetectTokensController', () => {
})
it('should trigger detect new tokens when change address', async () => {
- const network = new NetworkController()
network.setProviderType('mainnet')
- const preferences = new PreferencesController()
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -112,9 +103,7 @@ describe('DetectTokensController', () => {
})
it('should trigger detect new tokens when submit password', async () => {
- const network = new NetworkController()
network.setProviderType('mainnet')
- const preferences = new PreferencesController()
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.selectedAddress = '0x0'
@@ -124,9 +113,7 @@ describe('DetectTokensController', () => {
})
it('should not trigger detect new tokens when not open or not unlocked', async () => {
- const network = new NetworkController()
network.setProviderType('mainnet')
- const preferences = new PreferencesController()
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = false
diff --git a/test/unit/app/controllers/preferences-controller-test.js b/test/unit/app/controllers/preferences-controller-test.js
index e055500b1..9b2c846bd 100644
--- a/test/unit/app/controllers/preferences-controller-test.js
+++ b/test/unit/app/controllers/preferences-controller-test.js
@@ -1,11 +1,14 @@
const assert = require('assert')
+const ObservableStore = require('obs-store')
const PreferencesController = require('../../../../app/scripts/controllers/preferences')
describe('preferences controller', function () {
let preferencesController
+ let network
beforeEach(() => {
- preferencesController = new PreferencesController()
+ network = {providerStore: new ObservableStore({ type: 'mainnet' })}
+ preferencesController = new PreferencesController({ network })
})
describe('setAddresses', function () {
@@ -28,6 +31,20 @@ describe('preferences controller', function () {
})
})
+ it('should create account tokens for each account in the store', function () {
+ preferencesController.setAddresses([
+ '0xda22le',
+ '0x7e57e2',
+ ])
+
+ const accountTokens = preferencesController.store.getState().accountTokens
+
+ assert.deepEqual(accountTokens, {
+ '0xda22le': {},
+ '0x7e57e2': {},
+ })
+ })
+
it('should replace its list of addresses', function () {
preferencesController.setAddresses([
'0xda22le',
@@ -64,6 +81,17 @@ describe('preferences controller', function () {
assert.equal(preferencesController.store.getState().identities['0xda22le'], undefined)
})
+ it('should remove an address from state and respective tokens', function () {
+ preferencesController.setAddresses([
+ '0xda22le',
+ '0x7e57e2',
+ ])
+
+ preferencesController.removeAddress('0xda22le')
+
+ assert.equal(preferencesController.store.getState().accountTokens['0xda22le'], undefined)
+ })
+
it('should switch accounts if the selected address is removed', function () {
preferencesController.setAddresses([
'0xda22le',
@@ -158,6 +186,42 @@ describe('preferences controller', function () {
await preferencesController.addToken(address, symbol, decimals)
assert.equal(preferencesController.getTokens().length, 1, 'one token added for 2nd address')
})
+
+ it('should add token per account', async function () {
+ const addressFirst = '0xabcdef1234567'
+ const addressSecond = '0xabcdef1234568'
+ const symbolFirst = 'ABBR'
+ const symbolSecond = 'ABBB'
+ const decimals = 5
+
+ await preferencesController.setSelectedAddress('0x7e57e2')
+ await preferencesController.addToken(addressFirst, symbolFirst, decimals)
+ const tokensFirstAddress = preferencesController.getTokens()
+
+ await preferencesController.setSelectedAddress('0xda22le')
+ await preferencesController.addToken(addressSecond, symbolSecond, decimals)
+ const tokensSeconAddress = preferencesController.getTokens()
+
+ assert.notEqual(tokensFirstAddress, tokensSeconAddress, 'add different tokens for two account and tokens are equal')
+ })
+
+ it('should add token per network', async function () {
+ const addressFirst = '0xabcdef1234567'
+ const addressSecond = '0xabcdef1234568'
+ const symbolFirst = 'ABBR'
+ const symbolSecond = 'ABBB'
+ const decimals = 5
+
+ network.providerStore.updateState({ type: 'mainnet' })
+ await preferencesController.addToken(addressFirst, symbolFirst, decimals)
+ const tokensFirstAddress = preferencesController.getTokens()
+
+ network.providerStore.updateState({ type: 'rinkeby' })
+ await preferencesController.addToken(addressSecond, symbolSecond, decimals)
+ const tokensSeconAddress = preferencesController.getTokens()
+
+ assert.notEqual(tokensFirstAddress, tokensSeconAddress, 'add different tokens for two networks and tokens are equal')
+ })
})
describe('removeToken', function () {
@@ -182,6 +246,98 @@ describe('preferences controller', function () {
const [token1] = tokens
assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5})
})
+
+ it('should remove a token from its state on corresponding address', async function () {
+ await preferencesController.setSelectedAddress('0x7e57e2')
+ await preferencesController.addToken('0xa', 'A', 4)
+ await preferencesController.addToken('0xb', 'B', 5)
+ await preferencesController.setSelectedAddress('0x7e57e3')
+ await preferencesController.addToken('0xa', 'A', 4)
+ await preferencesController.addToken('0xb', 'B', 5)
+ const initialTokensSecond = preferencesController.getTokens()
+ await preferencesController.setSelectedAddress('0x7e57e2')
+ await preferencesController.removeToken('0xa')
+
+ const tokensFirst = preferencesController.getTokens()
+ assert.equal(tokensFirst.length, 1, 'one token removed in account')
+
+ const [token1] = tokensFirst
+ assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5})
+
+ await preferencesController.setSelectedAddress('0x7e57e3')
+ const tokensSecond = preferencesController.getTokens()
+ assert.deepEqual(tokensSecond, initialTokensSecond, 'token deleted for account')
+ })
+
+ it('should remove a token from its state on corresponding network', async function () {
+ network.providerStore.updateState({ type: 'mainnet' })
+ await preferencesController.addToken('0xa', 'A', 4)
+ await preferencesController.addToken('0xb', 'B', 5)
+ network.providerStore.updateState({ type: 'rinkeby' })
+ await preferencesController.addToken('0xa', 'A', 4)
+ await preferencesController.addToken('0xb', 'B', 5)
+ const initialTokensSecond = preferencesController.getTokens()
+ network.providerStore.updateState({ type: 'mainnet' })
+ await preferencesController.removeToken('0xa')
+
+ const tokensFirst = preferencesController.getTokens()
+ assert.equal(tokensFirst.length, 1, 'one token removed in network')
+
+ const [token1] = tokensFirst
+ assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5})
+
+ network.providerStore.updateState({ type: 'rinkeby' })
+ const tokensSecond = preferencesController.getTokens()
+ assert.deepEqual(tokensSecond, initialTokensSecond, 'token deleted for network')
+ })
+ })
+
+ describe('on setSelectedAddress', function () {
+ it('should update tokens from its state on corresponding address', async function () {
+ await preferencesController.setSelectedAddress('0x7e57e2')
+ await preferencesController.addToken('0xa', 'A', 4)
+ await preferencesController.addToken('0xb', 'B', 5)
+ await preferencesController.setSelectedAddress('0x7e57e3')
+ await preferencesController.addToken('0xa', 'C', 4)
+ await preferencesController.addToken('0xb', 'D', 5)
+
+ await preferencesController.setSelectedAddress('0x7e57e2')
+ const initialTokensFirst = preferencesController.getTokens()
+ await preferencesController.setSelectedAddress('0x7e57e3')
+ const initialTokensSecond = preferencesController.getTokens()
+
+ assert.notDeepEqual(initialTokensFirst, initialTokensSecond, 'tokens not equal for different accounts and tokens')
+
+ await preferencesController.setSelectedAddress('0x7e57e2')
+ const tokensFirst = preferencesController.getTokens()
+ await preferencesController.setSelectedAddress('0x7e57e3')
+ const tokensSecond = preferencesController.getTokens()
+
+ assert.deepEqual(tokensFirst, initialTokensFirst, 'tokens equal for same account')
+ assert.deepEqual(tokensSecond, initialTokensSecond, 'tokens equal for same account')
+ })
+ })
+
+ describe('on updateStateNetworkType', function () {
+ it('should remove a token from its state on corresponding network', async function () {
+ network.providerStore.updateState({ type: 'mainnet' })
+ await preferencesController.addToken('0xa', 'A', 4)
+ await preferencesController.addToken('0xb', 'B', 5)
+ const initialTokensFirst = preferencesController.getTokens()
+ network.providerStore.updateState({ type: 'rinkeby' })
+ await preferencesController.addToken('0xa', 'C', 4)
+ await preferencesController.addToken('0xb', 'D', 5)
+ const initialTokensSecond = preferencesController.getTokens()
+
+ assert.notDeepEqual(initialTokensFirst, initialTokensSecond, 'tokens not equal for different networks and tokens')
+
+ network.providerStore.updateState({ type: 'mainnet' })
+ const tokensFirst = preferencesController.getTokens()
+ network.providerStore.updateState({ type: 'rinkeby' })
+ const tokensSecond = preferencesController.getTokens()
+ assert.deepEqual(tokensFirst, initialTokensFirst, 'tokens equal for same network')
+ assert.deepEqual(tokensSecond, initialTokensSecond, 'tokens equal for same network')
+ })
})
})
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 04ad344cf..5cc7dc2fa 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -1486,11 +1486,12 @@ function showAccountDetail (address) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
log.debug(`background.setSelectedAddress`)
- background.setSelectedAddress(address, (err) => {
+ background.setSelectedAddress(address, (err, tokens) => {
dispatch(actions.hideLoadingIndication())
if (err) {
return dispatch(actions.displayWarning(err.message))
}
+ dispatch(updateTokens(tokens))
dispatch({
type: actions.SHOW_ACCOUNT_DETAIL,
value: address,
diff --git a/ui/app/components/confirm-page-container/confirm-page-container.component.js b/ui/app/components/confirm-page-container/confirm-page-container.component.js
index 93e4ae7bf..24ff05353 100644
--- a/ui/app/components/confirm-page-container/confirm-page-container.component.js
+++ b/ui/app/components/confirm-page-container/confirm-page-container.component.js
@@ -43,7 +43,7 @@ export default class ConfirmPageContainer extends Component {
// Footer
onCancel: PropTypes.func,
onSubmit: PropTypes.func,
- valid: PropTypes.bool,
+ disabled: PropTypes.bool,
}
render () {
@@ -54,7 +54,7 @@ export default class ConfirmPageContainer extends Component {
fromAddress,
toName,
toAddress,
- valid,
+ disabled,
errorKey,
errorMessage,
contentComponent,
@@ -110,7 +110,7 @@ export default class ConfirmPageContainer extends Component {
onSubmit={() => onSubmit()}
submitText={this.context.t('confirm')}
submitButtonType="confirm"
- disabled={!valid}
+ disabled={disabled}
/>
</div>
)
diff --git a/ui/app/components/dropdowns/components/dropdown.js b/ui/app/components/dropdowns/components/dropdown.js
index 0336dbb8b..149f063a7 100644
--- a/ui/app/components/dropdowns/components/dropdown.js
+++ b/ui/app/components/dropdowns/components/dropdown.js
@@ -87,7 +87,6 @@ class DropdownMenuItem extends Component {
padding: '8px 0px',
fontSize: '18px',
fontStyle: 'normal',
- fontFamily: 'Montserrat Regular',
cursor: 'pointer',
display: 'flex',
justifyContent: 'flex-start',
diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js
index dcd6b4370..e5363ff56 100644
--- a/ui/app/components/dropdowns/network-dropdown.js
+++ b/ui/app/components/dropdowns/network-dropdown.js
@@ -71,7 +71,6 @@ NetworkDropdown.prototype.render = function () {
const rpcList = props.frequentRpcList
const isOpen = this.props.networkDropdownOpen
const dropdownMenuItemStyle = {
- fontFamily: 'DIN OT',
fontSize: '16px',
lineHeight: '20px',
padding: '12px 0',
@@ -286,7 +285,6 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) {
closeMenu: () => this.props.hideNetworkDropdown(),
onClick: () => props.setRpcTarget(rpc),
style: {
- fontFamily: 'DIN OT',
fontSize: '16px',
lineHeight: '20px',
padding: '12px 0',
@@ -325,7 +323,6 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) {
onClick: () => props.setRpcTarget(rpcTarget),
closeMenu: () => this.props.hideNetworkDropdown(),
style: {
- fontFamily: 'DIN OT',
fontSize: '16px',
lineHeight: '20px',
padding: '12px 0',
diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js
index e1bf2210f..b170880b4 100644
--- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js
+++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.component.js
@@ -71,6 +71,10 @@ export default class ConfirmTransactionBase extends Component {
warning: PropTypes.string,
}
+ state = {
+ submitting: false,
+ }
+
componentDidUpdate () {
const {
transactionStatus,
@@ -258,15 +262,25 @@ export default class ConfirmTransactionBase extends Component {
handleSubmit () {
const { sendTransaction, clearConfirmTransaction, txData, history, onSubmit } = this.props
+ const { submitting } = this.state
+
+ if (submitting) {
+ return
+ }
+
+ this.setState({ submitting: true })
if (onSubmit) {
- onSubmit(txData)
+ Promise.resolve(onSubmit(txData))
+ .then(this.setState({ submitting: false }))
} else {
sendTransaction(txData)
.then(() => {
clearConfirmTransaction()
+ this.setState({ submitting: false })
history.push(DEFAULT_ROUTE)
})
+ .catch(() => this.setState({ submitting: false }))
}
}
@@ -280,7 +294,7 @@ export default class ConfirmTransactionBase extends Component {
methodData,
ethTransactionAmount,
fiatTransactionAmount,
- valid: propsValid,
+ valid: propsValid = true,
errorMessage,
errorKey: propsErrorKey,
currentCurrency,
@@ -295,6 +309,7 @@ export default class ConfirmTransactionBase extends Component {
nonce,
warning,
} = this.props
+ const { submitting } = this.state
const { name } = methodData
const fiatConvertedAmount = formatCurrency(fiatTransactionAmount, currentCurrency)
@@ -320,7 +335,7 @@ export default class ConfirmTransactionBase extends Component {
errorMessage={errorMessage}
errorKey={propsErrorKey || errorKey}
warning={warning}
- valid={propsValid || valid}
+ disabled={!propsValid || !valid || submitting}
onEdit={() => this.handleEdit()}
onCancel={() => this.handleCancel()}
onSubmit={() => this.handleSubmit()}
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index 1a639d0b9..7513ba267 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -213,7 +213,7 @@ TxListItem.prototype.showRetryButton = function () {
if (!txParams) {
return false
}
- let currentTxIsLatest = false
+ let currentTxSharesEarliestNonce = false
const currentNonce = txParams.nonce
const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
@@ -222,14 +222,14 @@ TxListItem.prototype.showRetryButton = function () {
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
lastSubmittedTxWithCurrentNonce.id === transactionId
if (currentSubmittedTxs.length > 0) {
- const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
+ const earliestSubmitted = currentSubmittedTxs.reduce((tx1, tx2) => {
if (tx1.submittedTime < tx2.submittedTime) return tx1
return tx2
})
- currentTxIsLatest = lastTx.id === transactionId
+ currentTxSharesEarliestNonce = currentNonce === earliestSubmitted.txParams.nonce
}
- return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000 && currentTxIsLatest
+ return currentTxSharesEarliestNonce && currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
}
TxListItem.prototype.setSelectedToken = function (tokenAddress) {
diff --git a/ui/app/css/itcss/components/network.scss b/ui/app/css/itcss/components/network.scss
index 545a2a940..b23876d01 100644
--- a/ui/app/css/itcss/components/network.scss
+++ b/ui/app/css/itcss/components/network.scss
@@ -76,7 +76,6 @@
}
.network-name-item {
- font-weight: 100;
flex: 1;
color: $dusty-gray;
text-overflow: ellipsis;