diff options
25 files changed, 272 insertions, 455 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 214355589..90beda418 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -724,7 +724,7 @@ "message": "New Password (min 8 chars)" }, "seedPhraseReq": { - "message": "seed phrases are 12 words long" + "message": "Seed phrases are 12 words long" }, "select": { "message": "Select" diff --git a/app/scripts/controllers/network/enums.js b/app/scripts/controllers/network/enums.js index 4f29e301b..9da7f309c 100644 --- a/app/scripts/controllers/network/enums.js +++ b/app/scripts/controllers/network/enums.js @@ -13,20 +13,6 @@ const RINKEBY_DISPLAY_NAME = 'Rinkeby' const KOVAN_DISPLAY_NAME = 'Kovan' const MAINNET_DISPLAY_NAME = 'Main Ethereum Network' -const MAINNET_RPC_URL = 'https://mainnet.infura.io/metamask' -const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask' -const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask' -const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask' -const LOCALHOST_RPC_URL = 'http://localhost:8545' - -const MAINNET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2' -const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2' -const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2' -const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2' - -const DEFAULT_NETWORK = 'rinkeby' -const OLD_UI_NETWORK_TYPE = 'network' -const BETA_UI_NETWORK_TYPE = 'networkBeta' module.exports = { ROPSTEN, @@ -41,16 +27,4 @@ module.exports = { RINKEBY_DISPLAY_NAME, KOVAN_DISPLAY_NAME, MAINNET_DISPLAY_NAME, - MAINNET_RPC_URL, - ROPSTEN_RPC_URL, - KOVAN_RPC_URL, - RINKEBY_RPC_URL, - LOCALHOST_RPC_URL, - MAINNET_RPC_URL_BETA, - ROPSTEN_RPC_URL_BETA, - KOVAN_RPC_URL_BETA, - RINKEBY_RPC_URL_BETA, - DEFAULT_NETWORK, - OLD_UI_NETWORK_TYPE, - BETA_UI_NETWORK_TYPE, } diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js index 2f5b81cd2..93fde7c57 100644 --- a/app/scripts/controllers/network/network.js +++ b/app/scripts/controllers/network/network.js @@ -14,52 +14,40 @@ const { RINKEBY, KOVAN, MAINNET, - OLD_UI_NETWORK_TYPE, - DEFAULT_NETWORK, + LOCALHOST, } = require('./enums') -const { getNetworkEndpoints } = require('./util') +const LOCALHOST_RPC_URL = 'http://localhost:8545' const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET] +const env = process.env.METAMASK_ENV +const METAMASK_DEBUG = process.env.METAMASK_DEBUG +const testMode = (METAMASK_DEBUG || env === 'test') + +const defaultProviderConfig = { + type: testMode ? RINKEBY : MAINNET, +} + module.exports = class NetworkController extends EventEmitter { - constructor (config) { + constructor (opts = {}) { super() - this._networkEndpointVersion = OLD_UI_NETWORK_TYPE - this._networkEndpoints = getNetworkEndpoints(OLD_UI_NETWORK_TYPE) - this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK] - - config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider) + // parse options + const providerConfig = opts.provider || defaultProviderConfig + // create stores + this.providerStore = new ObservableStore(providerConfig) this.networkStore = new ObservableStore('loading') - this.providerStore = new ObservableStore(config.provider) this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore }) + // create event emitter proxy this._proxy = createEventEmitterProxy() this.on('networkDidChange', this.lookupNetwork) } - async setNetworkEndpoints (version) { - if (version === this._networkEndpointVersion) { - return - } - - this._networkEndpointVersion = version - this._networkEndpoints = getNetworkEndpoints(version) - this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK] - const { type } = this.getProviderConfig() - - return this.setProviderType(type, true) - } - initializeProvider (_providerParams) { this._baseProviderParams = _providerParams const { type, rpcTarget } = this.providerStore.getState() - // map rpcTarget to rpcUrl - const opts = { - type, - rpcUrl: rpcTarget, - } - this._configureProvider(opts) + this._configureProvider({ type, rpcTarget }) this._proxy.on('block', this._logBlock.bind(this)) this._proxy.on('error', this.verifyNetwork.bind(this)) this.ethQuery = new EthQuery(this._proxy) @@ -96,45 +84,27 @@ module.exports = class NetworkController extends EventEmitter { }) } - setRpcTarget (rpcUrl) { - this.providerStore.updateState({ + setRpcTarget (rpcTarget) { + const providerConfig = { type: 'rpc', - rpcTarget: rpcUrl, - }) - this._switchNetwork({ rpcUrl }) - } - - getCurrentRpcAddress () { - const provider = this.getProviderConfig() - if (!provider) return null - return this.getRpcAddressForType(provider.type) - } - - async setProviderType (type, forceUpdate = false) { - assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`) - // skip if type already matches - if (type === this.getProviderConfig().type && !forceUpdate) { - return + rpcTarget, } + this.providerStore.updateState(providerConfig) + this._switchNetwork(providerConfig) + } - const rpcTarget = this.getRpcAddressForType(type) - assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) - this.providerStore.updateState({ type, rpcTarget }) - this._switchNetwork({ type }) + async setProviderType (type) { + assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`) + assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`) + const providerConfig = { type } + this.providerStore.updateState(providerConfig) + this._switchNetwork(providerConfig) } getProviderConfig () { return this.providerStore.getState() } - getRpcAddressForType (type, provider = this.getProviderConfig()) { - if (this._networkEndpoints[type]) { - return this._networkEndpoints[type] - } - - return provider && provider.rpcTarget ? provider.rpcTarget : this._defaultRpc - } - // // Private // @@ -146,32 +116,27 @@ module.exports = class NetworkController extends EventEmitter { } _configureProvider (opts) { - // type-based rpc endpoints - const { type } = opts - if (type) { - // type-based infura rpc endpoints - const isInfura = INFURA_PROVIDER_TYPES.includes(type) - opts.rpcUrl = this.getRpcAddressForType(type) - if (isInfura) { - this._configureInfuraProvider(opts) - // other type-based rpc endpoints - } else { - this._configureStandardProvider(opts) - } + const { type, rpcTarget } = opts + // infura type-based endpoints + const isInfura = INFURA_PROVIDER_TYPES.includes(type) + if (isInfura) { + this._configureInfuraProvider(opts) + // other type-based rpc endpoints + } else if (type === LOCALHOST) { + this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL }) // url-based rpc endpoints + } else if (type === 'rpc'){ + this._configureStandardProvider({ rpcUrl: rpcTarget }) } else { - this._configureStandardProvider(opts) + throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`) } } - _configureInfuraProvider (opts) { - log.info('_configureInfuraProvider', opts) - const infuraProvider = createInfuraProvider({ - network: opts.type, - }) + _configureInfuraProvider ({ type }) { + log.info('_configureInfuraProvider', type) + const infuraProvider = createInfuraProvider({ network: type }) const infuraSubprovider = new SubproviderFromProvider(infuraProvider) const providerParams = extend(this._baseProviderParams, { - rpcUrl: opts.rpcUrl, engineParams: { pollingInterval: 8000, blockTrackerProvider: infuraProvider, diff --git a/app/scripts/controllers/network/util.js b/app/scripts/controllers/network/util.js index 4f38ccda4..261dae721 100644 --- a/app/scripts/controllers/network/util.js +++ b/app/scripts/controllers/network/util.js @@ -3,7 +3,6 @@ const { RINKEBY, KOVAN, MAINNET, - LOCALHOST, ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE, @@ -11,17 +10,6 @@ const { RINKEBY_DISPLAY_NAME, KOVAN_DISPLAY_NAME, MAINNET_DISPLAY_NAME, - MAINNET_RPC_URL, - ROPSTEN_RPC_URL, - KOVAN_RPC_URL, - RINKEBY_RPC_URL, - LOCALHOST_RPC_URL, - MAINNET_RPC_URL_BETA, - ROPSTEN_RPC_URL_BETA, - KOVAN_RPC_URL_BETA, - RINKEBY_RPC_URL_BETA, - OLD_UI_NETWORK_TYPE, - BETA_UI_NETWORK_TYPE, } = require('./enums') const networkToNameMap = { @@ -34,32 +22,8 @@ const networkToNameMap = { [KOVAN_CODE]: KOVAN_DISPLAY_NAME, } -const networkEndpointsMap = { - [OLD_UI_NETWORK_TYPE]: { - [LOCALHOST]: LOCALHOST_RPC_URL, - [MAINNET]: MAINNET_RPC_URL, - [ROPSTEN]: ROPSTEN_RPC_URL, - [KOVAN]: KOVAN_RPC_URL, - [RINKEBY]: RINKEBY_RPC_URL, - }, - [BETA_UI_NETWORK_TYPE]: { - [LOCALHOST]: LOCALHOST_RPC_URL, - [MAINNET]: MAINNET_RPC_URL_BETA, - [ROPSTEN]: ROPSTEN_RPC_URL_BETA, - [KOVAN]: KOVAN_RPC_URL_BETA, - [RINKEBY]: RINKEBY_RPC_URL_BETA, - }, -} - const getNetworkDisplayName = key => networkToNameMap[key] -const getNetworkEndpoints = (networkType = OLD_UI_NETWORK_TYPE) => { - return { - ...networkEndpointsMap[networkType], - } -} - module.exports = { getNetworkDisplayName, - getNetworkEndpoints, } diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js index c49d89288..13119bc04 100644 --- a/app/scripts/first-time-state.js +++ b/app/scripts/first-time-state.js @@ -1,7 +1,3 @@ -// test and development environment variables -const env = process.env.METAMASK_ENV -const METAMASK_DEBUG = process.env.METAMASK_DEBUG -const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums') /** * @typedef {Object} FirstTimeState @@ -14,11 +10,6 @@ const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums') */ const initialState = { config: {}, - NetworkController: { - provider: { - type: (METAMASK_DEBUG || env === 'test') ? DEFAULT_NETWORK : MAINNET, - }, - }, } module.exports = initialState diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a90acb4d5..a6b5d3453 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -355,7 +355,6 @@ module.exports = class MetamaskController extends EventEmitter { submitPassword: nodeify(keyringController.submitPassword, keyringController), // network management - setNetworkEndpoints: nodeify(networkController.setNetworkEndpoints, networkController), setProviderType: nodeify(networkController.setProviderType, networkController), setCustomRpc: nodeify(this.setCustomRpc, this), diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js index 6ec05e11d..99d210ed1 100644 --- a/mascara/src/app/first-time/create-password-screen.js +++ b/mascara/src/app/first-time/create-password-screen.js @@ -13,8 +13,13 @@ import { INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, INITIALIZE_NOTICE_ROUTE, } from '../../../../ui/app/routes' +import TextField from '../../../../ui/app/components/text-field' class CreatePasswordScreen extends Component { + static contextTypes = { + t: PropTypes.func, + } + static propTypes = { isLoading: PropTypes.bool.isRequired, createAccount: PropTypes.func.isRequired, @@ -27,6 +32,8 @@ class CreatePasswordScreen extends Component { state = { password: '', confirmPassword: '', + passwordError: null, + confirmPasswordError: null, } constructor (props) { @@ -69,82 +76,37 @@ class CreatePasswordScreen extends Component { .then(() => history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)) } - renderFields () { - const { isMascara, history } = this.props + handlePasswordChange (password) { + const { confirmPassword } = this.state + let confirmPasswordError = null + let passwordError = null - return ( - <div className={classnames({ 'first-view-main-wrapper': !isMascara })}> - <div className={classnames({ - 'first-view-main': !isMascara, - 'first-view-main__mascara': isMascara, - })}> - {isMascara && <div className="mascara-info first-view-phone-invisible"> - <Mascot - animationEventEmitter={this.animationEventEmitter} - width="225" - height="225" - /> - <div className="info"> - MetaMask is a secure identity vault for Ethereum. - </div> - <div className="info"> - It allows you to hold ether & tokens, and interact with decentralized applications. - </div> - </div>} - <div className="create-password"> - <div className="create-password__title"> - Create Password - </div> - <input - className="first-time-flow__input" - type="password" - placeholder="New Password (min 8 characters)" - onChange={e => this.setState({password: e.target.value})} - /> - <input - className="first-time-flow__input create-password__confirm-input" - type="password" - placeholder="Confirm Password" - onChange={e => this.setState({confirmPassword: e.target.value})} - /> - <button - className="first-time-flow__button" - disabled={!this.isValid()} - onClick={this.createAccount} - > - Create - </button> - <a - href="" - className="first-time-flow__link create-password__import-link" - onClick={e => { - e.preventDefault() - history.push(INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE) - }} - > - Import with seed phrase - </a> - { /* } - <a - href="" - className="first-time-flow__link create-password__import-link" - onClick={e => { - e.preventDefault() - history.push(INITIALIZE_IMPORT_ACCOUNT_ROUTE) - }} - > - Import an account - </a> - { */ } - <Breadcrumbs total={3} currentIndex={0} /> - </div> - </div> - </div> - ) + if (password && password.length < 8) { + passwordError = this.context.t('passwordNotLongEnough') + } + + if (confirmPassword && password !== confirmPassword) { + confirmPasswordError = this.context.t('passwordsDontMatch') + } + + this.setState({ password, passwordError, confirmPasswordError }) + } + + handleConfirmPasswordChange (confirmPassword) { + const { password } = this.state + let confirmPasswordError = null + + if (password !== confirmPassword) { + confirmPasswordError = this.context.t('passwordsDontMatch') + } + + this.setState({ confirmPassword, confirmPasswordError }) } render () { const { history, isMascara } = this.props + const { passwordError, confirmPasswordError } = this.state + const { t } = this.context return ( <div className={classnames({ 'first-view-main-wrapper': !isMascara })}> @@ -169,17 +131,30 @@ class CreatePasswordScreen extends Component { <div className="create-password__title"> Create Password </div> - <input - className="first-time-flow__input" + <TextField + id="create-password" + label={t('newPassword')} type="password" - placeholder="New Password (min 8 characters)" - onChange={e => this.setState({password: e.target.value})} + className="first-time-flow__input" + value={this.state.password} + onChange={event => this.handlePasswordChange(event.target.value)} + error={passwordError} + autoFocus + autoComplete="new-password" + margin="normal" + fullWidth /> - <input - className="first-time-flow__input create-password__confirm-input" + <TextField + id="confirm-password" + label={t('confirmPassword')} type="password" - placeholder="Confirm Password" - onChange={e => this.setState({confirmPassword: e.target.value})} + className="first-time-flow__input" + value={this.state.confirmPassword} + onChange={event => this.handleConfirmPasswordChange(event.target.value)} + error={confirmPasswordError} + autoComplete="confirm-password" + margin="normal" + fullWidth /> <button className="first-time-flow__button" 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 5834919de..4fda2bbc1 100644 --- a/mascara/src/app/first-time/import-seed-phrase-screen.js +++ b/mascara/src/app/first-time/import-seed-phrase-screen.js @@ -1,29 +1,33 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import {connect} from 'react-redux' -import classnames from 'classnames' import { createNewVaultAndRestore, - hideWarning, - displayWarning, unMarkPasswordForgotten, } from '../../../../ui/app/actions' -import { DEFAULT_ROUTE, INITIALIZE_NOTICE_ROUTE } from '../../../../ui/app/routes' +import { INITIALIZE_NOTICE_ROUTE } from '../../../../ui/app/routes' +import TextField from '../../../../ui/app/components/text-field' class ImportSeedPhraseScreen extends Component { + static contextTypes = { + t: PropTypes.func, + } + static propTypes = { warning: PropTypes.string, createNewVaultAndRestore: PropTypes.func.isRequired, - hideWarning: PropTypes.func.isRequired, - displayWarning: PropTypes.func, leaveImportSeedScreenState: PropTypes.func, history: PropTypes.object, + isLoading: PropTypes.bool, }; state = { seedPhrase: '', password: '', confirmPassword: '', + seedPhraseError: null, + passwordError: null, + confirmPasswordError: null, } parseSeedPhrase = (seedPhrase) => { @@ -32,39 +36,47 @@ class ImportSeedPhraseScreen extends Component { .join(' ') } - onChange = ({ seedPhrase, password, confirmPassword }) => { - const { - password: prevPassword, - confirmPassword: prevConfirmPassword, - } = this.state - const { displayWarning, hideWarning } = this.props - - let warning = null + handleSeedPhraseChange (seedPhrase) { + let seedPhraseError = null if (seedPhrase && this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) { - warning = 'Seed Phrases are 12 words long' - } else if (password && password.length < 8) { - warning = 'Passwords require a mimimum length of 8' - } else if ((password || prevPassword) !== (confirmPassword || prevConfirmPassword)) { - warning = 'Confirmed password does not match' + seedPhraseError = this.context.t('seedPhraseReq') } - if (warning) { - displayWarning(warning) - } else { - hideWarning() + this.setState({ seedPhrase, seedPhraseError }) + } + + handlePasswordChange (password) { + const { confirmPassword } = this.state + let confirmPasswordError = null + let passwordError = null + + if (password && password.length < 8) { + passwordError = this.context.t('passwordNotLongEnough') + } + + if (confirmPassword && password !== confirmPassword) { + confirmPasswordError = this.context.t('passwordsDontMatch') + } + + this.setState({ password, passwordError, confirmPasswordError }) + } + + handleConfirmPasswordChange (confirmPassword) { + const { password } = this.state + let confirmPasswordError = null + + if (password !== confirmPassword) { + confirmPasswordError = this.context.t('passwordsDontMatch') } - seedPhrase && this.setState({ seedPhrase }) - password && this.setState({ password }) - confirmPassword && this.setState({ confirmPassword }) + this.setState({ confirmPassword, confirmPasswordError }) } onClick = () => { const { password, seedPhrase } = this.state const { createNewVaultAndRestore, - displayWarning, leaveImportSeedScreenState, history, } = this.props @@ -74,10 +86,23 @@ class ImportSeedPhraseScreen extends Component { .then(() => history.push(INITIALIZE_NOTICE_ROUTE)) } + hasError () { + const { passwordError, confirmPasswordError, seedPhraseError } = this.state + return passwordError || confirmPasswordError || seedPhraseError + } + render () { - const { seedPhrase, password, confirmPassword } = this.state - const { warning, isLoading } = this.props - const importDisabled = warning || !seedPhrase || !password || !confirmPassword || isLoading + const { + seedPhrase, + password, + confirmPassword, + seedPhraseError, + passwordError, + confirmPasswordError, + } = this.state + const { t } = this.context + const { isLoading } = this.props + const disabled = !seedPhrase || !password || !confirmPassword || isLoading || this.hasError() return ( <div className="first-view-main-wrapper"> @@ -103,45 +128,40 @@ class ImportSeedPhraseScreen extends Component { <label className="import-account__input-label">Wallet Seed</label> <textarea className="import-account__secret-phrase" - onChange={e => this.onChange({seedPhrase: e.target.value})} + onChange={e => this.handleSeedPhraseChange(e.target.value)} value={this.state.seedPhrase} placeholder="Separate each word with a single space" /> </div> - <span - className="error" - > - {this.props.warning} + <span className="error"> + { seedPhraseError } </span> - <div className="import-account__input-wrapper"> - <label className="import-account__input-label">New Password</label> - <input - className="first-time-flow__input" - type="password" - placeholder="New Password (min 8 characters)" - onChange={e => this.onChange({password: e.target.value})} - /> - </div> - <div className="import-account__input-wrapper"> - <label - className={classnames('import-account__input-label', { - 'import-account__input-label__disabled': password.length < 8, - })} - >Confirm Password</label> - <input - className={classnames('first-time-flow__input', { - 'first-time-flow__input__disabled': password.length < 8, - })} - type="password" - placeholder="Confirm Password" - onChange={e => this.onChange({confirmPassword: e.target.value})} - disabled={password.length < 8} - /> - </div> + <TextField + id="password" + label={t('newPassword')} + type="password" + className="first-time-flow__input" + value={this.state.password} + onChange={event => this.handlePasswordChange(event.target.value)} + error={passwordError} + autoComplete="new-password" + margin="normal" + /> + <TextField + id="confirm-password" + label={t('confirmPassword')} + type="password" + className="first-time-flow__input" + value={this.state.confirmPassword} + onChange={event => this.handleConfirmPasswordChange(event.target.value)} + error={confirmPasswordError} + autoComplete="confirm-password" + margin="normal" + /> <button className="first-time-flow__button" - onClick={() => !importDisabled && this.onClick()} - disabled={importDisabled} + onClick={() => !disabled && this.onClick()} + disabled={disabled} > Import </button> @@ -159,7 +179,5 @@ export default connect( dispatch(unMarkPasswordForgotten()) }, createNewVaultAndRestore: (pw, seed) => dispatch(createNewVaultAndRestore(pw, seed)), - displayWarning: (warning) => dispatch(displayWarning(warning)), - hideWarning: () => dispatch(hideWarning()), }) )(ImportSeedPhraseScreen) diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css index dffc21017..25e60b84a 100644 --- a/mascara/src/app/first-time/index.css +++ b/mascara/src/app/first-time/index.css @@ -174,10 +174,7 @@ } .first-time-flow__input { - width: initial !important; - font-size: 14px !important; - line-height: 18px !important; - padding: 12px !important; + width: 100%; } .tou__body { @@ -248,7 +245,7 @@ } .create-password__confirm-input { - margin-top: 15px; + margin-top: 16px; } .create-password__import-link { @@ -520,10 +517,6 @@ button.backup-phrase__confirm-seed-option:hover { margin-top: 30px; } -.first-time-flow__input--error { - border: 1px solid #FF001F !important; -} - .import-account__input-error-message { margin-top: 10px; width: 422px; @@ -544,7 +537,13 @@ button.backup-phrase__confirm-seed-option:hover { } .import-account__input { - width: 325px !important; + width: 350px; +} + +@media only screen and (max-width: 575px) { + .import-account__input { + width: 100%; + } } .import-account__file-input { @@ -681,20 +680,6 @@ button.backup-phrase__confirm-seed-option:hover { .first-time-flow__input { width: 350px; - font-size: 18px; - line-height: 24px; - padding: 15px; - border: 1px solid #CDCDCD; - background-color: #FFFFFF; -} - -.first-time-flow__input__disabled { - opacity: 0.5; -} - -.first-time-flow__input::placeholder { - color: #9B9B9B; - font-weight: 200; } .first-time-flow__button { diff --git a/old-ui/app/app.js b/old-ui/app/app.js index 3aa845b3a..2378a1a0a 100644 --- a/old-ui/app/app.js +++ b/old-ui/app/app.js @@ -35,7 +35,6 @@ 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 -const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums') module.exports = connect(mapStateToProps)(App) @@ -409,7 +408,6 @@ App.prototype.renderDropdown = function () { closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }), onClick: () => { this.props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL')) - .then(() => this.props.dispatch(actions.setNetworkEndpoints(BETA_UI_NETWORK_TYPE))) }, }, 'Try Beta!'), ]) @@ -472,7 +470,6 @@ App.prototype.renderPrimary = function () { onClick: () => { global.platform.openExtensionInBrowser() props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL')) - .then(() => props.dispatch(actions.setNetworkEndpoints(BETA_UI_NETWORK_TYPE))) }, style: { fontSize: '0.8em', diff --git a/package.json b/package.json index d666dbb87..f6338c542 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "test:integration:build": "gulp build:scss", "test:e2e:chrome": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:e2e:run:chrome'", "test:e2e:firefox": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:e2e:run:firefox'", - "test:e2e:run:chrome": "SELENIUM_BROWSER=chrome mocha test/e2e/chrome/metamask.spec --recursive", - "test:e2e:run:firefox": "SELENIUM_BROWSER=firefox mocha test/e2e/firefox/metamask.spec --recursive", + "test:e2e:run:chrome": "SELENIUM_BROWSER=chrome mocha test/e2e/chrome/metamask.spec --bail --recursive", + "test:e2e:run:firefox": "SELENIUM_BROWSER=firefox mocha test/e2e/firefox/metamask.spec --bail --recursive", "test:screens": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:screens:run'", "test:screens:run": "node test/screens/new-ui.js", "test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload", diff --git a/test/e2e/chrome/metamask.spec.js b/test/e2e/chrome/metamask.spec.js index d72ebe1a9..b17d4c818 100644 --- a/test/e2e/chrome/metamask.spec.js +++ b/test/e2e/chrome/metamask.spec.js @@ -237,7 +237,7 @@ describe('Metamask popup page', function () { it('confirms transaction in MetaMask popup', async function () { const windowHandles = await driver.getAllWindowHandles() - await driver.switchTo().window(windowHandles[2]) + await driver.switchTo().window(windowHandles[windowHandles.length - 1]) const metamaskSubmit = await driver.findElement(By.css('#pending-tx-form > div.flex-row.flex-space-around.conf-buttons > input')) await metamaskSubmit.click() await delay(1000) @@ -291,7 +291,7 @@ describe('Metamask popup page', function () { }) async function getExtensionId () { - const extension = await driver.executeScript('return document.querySelector("extensions-manager").shadowRoot.querySelector("extensions-view-manager extensions-item-list").shadowRoot.querySelector("#container > div.items-container > extensions-item:nth-child(2)").getAttribute("id")') + const extension = await driver.executeScript('return document.querySelector("extensions-manager").shadowRoot.querySelector("extensions-view-manager extensions-item-list").shadowRoot.querySelector("extensions-item:nth-child(2)").getAttribute("id")') return extension } diff --git a/test/e2e/firefox/metamask.spec.js b/test/e2e/firefox/metamask.spec.js index 20b8a5092..c75b1a9b5 100644 --- a/test/e2e/firefox/metamask.spec.js +++ b/test/e2e/firefox/metamask.spec.js @@ -59,6 +59,7 @@ describe('', function () { }) it('shows privacy notice', async () => { + await delay(300) const privacy = await driver.findElement(By.css('.terms-header')).getText() assert.equal(privacy, 'PRIVACY NOTICE', 'shows privacy notice') await driver.findElement(By.css('button')).click() @@ -125,7 +126,7 @@ describe('', function () { it('accepts account password after lock', async () => { await delay(500) await driver.findElement(By.id('password-box')).sendKeys('123456789') - await driver.findElement(By.css('button')).click() + await driver.findElement(By.id('password-box')).sendKeys(webdriver.Key.ENTER) await delay(500) }) @@ -238,7 +239,7 @@ describe('', function () { // There is an issue with blank confirmation window, but the button is still there and the driver is able to clicked (?.?) it('confirms transaction in MetaMask popup', async function () { const windowHandles = await driver.getAllWindowHandles() - await driver.switchTo().window(windowHandles[2]) + await driver.switchTo().window(windowHandles[windowHandles.length - 1]) const metamaskSubmit = await driver.findElement(By.css('#pending-tx-form > div.flex-row.flex-space-around.conf-buttons > input')) await metamaskSubmit.click() await delay(1000) diff --git a/test/integration/lib/mascara-first-time.js b/test/integration/lib/mascara-first-time.js index d86703277..f43a30c74 100644 --- a/test/integration/lib/mascara-first-time.js +++ b/test/integration/lib/mascara-first-time.js @@ -1,5 +1,4 @@ const PASSWORD = 'password123' -const reactTriggerChange = require('react-trigger-change') const { timeout, findAsync, @@ -11,6 +10,11 @@ async function runFirstTimeUsageTest (assert, done) { const app = await queryAsync($, '#app-content') + // Used to set values on TextField input component + const nativeInputValueSetter = Object.getOwnPropertyDescriptor( + window.HTMLInputElement.prototype, 'value' + ).set + await skipNotices(app) const welcomeButton = (await findAsync(app, '.welcome-screen__button'))[0] @@ -21,12 +25,14 @@ async function runFirstTimeUsageTest (assert, done) { assert.equal(title, 'Create Password', 'create password screen') // enter password - const pwBox = (await findAsync(app, '.first-time-flow__input'))[0] - const confBox = (await findAsync(app, '.first-time-flow__input'))[1] - pwBox.value = PASSWORD - confBox.value = PASSWORD - reactTriggerChange(pwBox) - reactTriggerChange(confBox) + const pwBox = (await findAsync(app, '#create-password'))[0] + const confBox = (await findAsync(app, '#confirm-password'))[0] + + nativeInputValueSetter.call(pwBox, PASSWORD) + pwBox.dispatchEvent(new Event('input', { bubbles: true})) + + nativeInputValueSetter.call(confBox, PASSWORD) + confBox.dispatchEvent(new Event('input', { bubbles: true})) // Create Password const createButton = (await findAsync(app, 'button.first-time-flow__button'))[0] @@ -77,15 +83,8 @@ async function runFirstTimeUsageTest (assert, done) { pwBox2.focus() await timeout(1000) - // Used to set values on TextField input component - const nativeInputValueSetter = Object.getOwnPropertyDescriptor( - window.HTMLInputElement.prototype, 'value' - ).set - nativeInputValueSetter.call(pwBox2, PASSWORD) - - var ev2 = new Event('input', { bubbles: true}) - pwBox2.dispatchEvent(ev2) + pwBox2.dispatchEvent(new Event('input', { bubbles: true})) const createButton2 = (await findAsync(app, 'button[type="submit"]'))[0] createButton2.click() diff --git a/test/screens/new-ui.js b/test/screens/new-ui.js index e176da529..6a8822eb3 100644 --- a/test/screens/new-ui.js +++ b/test/screens/new-ui.js @@ -74,8 +74,8 @@ async function captureAllScreens() { await driver.findElement(By.css('button')).click() await captureLanguageScreenShots('create password') - const passwordBox = await driver.findElement(By.css('input[type=password]:nth-of-type(1)')) - const passwordBoxConfirm = await driver.findElement(By.css('input[type=password]:nth-of-type(2)')) + const passwordBox = await driver.findElement(By.css('input#create-password')) + const passwordBoxConfirm = await driver.findElement(By.css('input#confirm-password')) passwordBox.sendKeys('123456789') passwordBoxConfirm.sendKeys('123456789') await delay(500) diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js index 2b905718b..2d590a3f6 100644 --- a/test/unit/network-contoller-test.js +++ b/test/unit/network-contoller-test.js @@ -3,17 +3,15 @@ const nock = require('nock') const NetworkController = require('../../app/scripts/controllers/network') const { getNetworkDisplayName, - getNetworkEndpoints, } = require('../../app/scripts/controllers/network/util') const { createTestProviderTools } = require('../stub/provider') const providerResultStub = {} -const provider = createTestProviderTools({ scaffold: providerResultStub }).provider describe('# Network Controller', function () { let networkController const noop = () => {} - const networkControllerProviderInit = { + const networkControllerProviderConfig = { getAccounts: noop, } @@ -24,11 +22,9 @@ describe('# Network Controller', function () { .post('/metamask') .reply(200) - networkController = new NetworkController({ - provider, - }) + networkController = new NetworkController() - networkController.initializeProvider(networkControllerProviderInit, provider) + networkController.initializeProvider(networkControllerProviderConfig) }) afterEach(function () { @@ -38,7 +34,7 @@ describe('# Network Controller', function () { describe('network', function () { describe('#provider', function () { it('provider should be updatable without reassignment', function () { - networkController.initializeProvider(networkControllerProviderInit, provider) + networkController.initializeProvider(networkControllerProviderConfig) const proxy = networkController._proxy proxy.setTarget({ test: true, on: () => {} }) assert.ok(proxy.test) @@ -59,12 +55,6 @@ describe('# Network Controller', function () { }) }) - describe('#getRpcAddressForType', function () { - it('should return the right rpc address', function () { - const rpcTarget = networkController.getRpcAddressForType('mainnet') - assert.equal(rpcTarget, 'https://mainnet.infura.io/metamask', 'returns the right rpcAddress') - }) - }) describe('#setProviderType', function () { it('should update provider.type', function () { networkController.setProviderType('mainnet') @@ -76,16 +66,11 @@ describe('# Network Controller', function () { const loading = networkController.isNetworkLoading() assert.ok(loading, 'network is loading') }) - it('should set the right rpcTarget', function () { - networkController.setProviderType('mainnet') - const rpcTarget = networkController.getProviderConfig().rpcTarget - assert.equal(rpcTarget, 'https://mainnet.infura.io/metamask', 'returns the right rpcAddress') - }) }) }) }) -describe('# Network utils', () => { +describe('Network utils', () => { it('getNetworkDisplayName should return the correct network name', () => { const tests = [ { @@ -114,9 +99,4 @@ describe('# Network utils', () => { tests.forEach(({ input, expected }) => assert.equal(getNetworkDisplayName(input), expected)) }) - - it('getNetworkEndpoints should return the correct endpoints', () => { - assert.equal(getNetworkEndpoints('networkBeta').ropsten, 'https://ropsten.infura.io/metamask2') - assert.equal(getNetworkEndpoints('network').rinkeby, 'https://rinkeby.infura.io/metamask') - }) }) diff --git a/ui/app/actions.js b/ui/app/actions.js index 9c4de9551..2d238b2f8 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -271,7 +271,6 @@ var actions = { SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE', // Network - setNetworkEndpoints, updateNetworkEndpointType, UPDATE_NETWORK_ENDPOINT_TYPE: 'UPDATE_NETWORK_ENDPOINT_TYPE', @@ -1924,23 +1923,6 @@ function setLocaleMessages (localeMessages) { } } -function setNetworkEndpoints (networkEndpointType) { - return dispatch => { - log.debug('background.setNetworkEndpoints') - return new Promise((resolve, reject) => { - background.setNetworkEndpoints(networkEndpointType, err => { - if (err) { - dispatch(actions.displayWarning(err.message)) - return reject(err) - } - - dispatch(actions.updateNetworkEndpointType(networkEndpointType)) - resolve(networkEndpointType) - }) - }) - } -} - function updateNetworkEndpointType (networkEndpointType) { return { type: actions.UPDATE_NETWORK_ENDPOINT_TYPE, diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js index bdefe56f8..f58ac7ddf 100644 --- a/ui/app/components/pages/settings/settings.js +++ b/ui/app/components/pages/settings/settings.js @@ -12,7 +12,6 @@ const SimpleDropdown = require('../../dropdowns/simple-dropdown') const ToggleButton = require('react-toggle-button') const { REVEAL_SEED_ROUTE } = require('../../../routes') const locales = require('../../../../../app/_locales/index.json') -const { OLD_UI_NETWORK_TYPE } = require('../../../../../app/scripts/controllers/network/enums') const getInfuraCurrencyOptions = () => { const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => { @@ -349,7 +348,6 @@ const mapDispatchToProps = dispatch => { updateCurrentLocale: key => dispatch(actions.updateCurrentLocale(key)), setFeatureFlagToBeta: () => { return dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) - .then(() => dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE))) }, showResetAccountConfirmationModal: () => { return dispatch(actions.showModal({ name: 'CONFIRM_RESET_ACCOUNT' })) diff --git a/ui/app/components/pages/unlock-page/unlock-page.component.js b/ui/app/components/pages/unlock-page/unlock-page.component.js index d5e2a3224..0976d9506 100644 --- a/ui/app/components/pages/unlock-page/unlock-page.component.js +++ b/ui/app/components/pages/unlock-page/unlock-page.component.js @@ -175,7 +175,6 @@ UnlockPage.propTypes = { isUnlocked: PropTypes.bool, t: PropTypes.func, useOldInterface: PropTypes.func, - setNetworkEndpoints: PropTypes.func, } export default UnlockPage diff --git a/ui/app/components/pages/unlock-page/unlock-page.container.js b/ui/app/components/pages/unlock-page/unlock-page.container.js index 9788a18ea..18fed9b2e 100644 --- a/ui/app/components/pages/unlock-page/unlock-page.container.js +++ b/ui/app/components/pages/unlock-page/unlock-page.container.js @@ -6,7 +6,6 @@ const { tryUnlockMetamask, forgotPassword, markPasswordForgotten, - setNetworkEndpoints, } = require('../../../actions') import UnlockPage from './unlock-page.component' @@ -23,7 +22,6 @@ const mapDispatchToProps = dispatch => { forgotPassword: () => dispatch(forgotPassword()), tryUnlockMetamask: password => dispatch(tryUnlockMetamask(password)), markPasswordForgotten: () => dispatch(markPasswordForgotten()), - setNetworkEndpoints: type => dispatch(setNetworkEndpoints(type)), } } diff --git a/ui/app/components/text-field/text-field.component.js b/ui/app/components/text-field/text-field.component.js index 4a02f76d8..6fd3b82b4 100644 --- a/ui/app/components/text-field/text-field.component.js +++ b/ui/app/components/text-field/text-field.component.js @@ -8,6 +8,9 @@ const styles = { '&$cssFocused': { color: '#aeaeae', }, + '&$cssError': { + color: '#aeaeae', + }, fontWeight: '400', color: '#aeaeae', }, @@ -17,6 +20,7 @@ const styles = { backgroundColor: '#f7861c', }, }, + cssError: {}, } const TextField = props => { @@ -30,6 +34,7 @@ const TextField = props => { FormLabelClasses: { root: classes.cssLabel, focused: classes.cssFocused, + error: classes.cssError, }, }} InputProps={{ diff --git a/ui/app/css/itcss/components/welcome-screen.scss b/ui/app/css/itcss/components/welcome-screen.scss index bfd174ad9..af1d67398 100644 --- a/ui/app/css/itcss/components/welcome-screen.scss +++ b/ui/app/css/itcss/components/welcome-screen.scss @@ -1,59 +1,60 @@ .welcome-screen { + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; + font-family: Roboto; + font-weight: 400; + width: 100%; + flex: 1 0 auto; + padding: 70px 0; + background: $white; + + @media screen and (max-width: 575px) { + padding: 0; + } + + &__info { display: flex; flex-flow: column; - justify-content: center; - align-items: center; - font-family: Roboto; - font-weight: 400; width: 100%; - flex: 1 0 auto; - padding: 70px 0; - background: $white; - - @media screen and (max-width: 575px) { - padding: 0; - } - - &__info { - display: flex; - flex-flow: column; - width: 100%; - height: 100%; - align-items: center; - - &__header { - font-size: 1.65em; - margin-bottom: 14px; - - @media screen and (max-width: 575px) { - font-size: 1.5em; - } - } + height: 100%; + align-items: center; + justify-content: center; - &__copy { - font-size: 1em; - width: 400px; - max-width: 90vw; - text-align: center; + &__header { + font-size: 1.65em; + margin-bottom: 14px; - @media screen and (max-width: 575px) { - font-size: 0.9em; - } - } + @media screen and (max-width: 575px) { + font-size: 1.5em; + } } - &__button { - height: 54px; - width: 198px; - box-shadow: 0 2px 4px 0 rgba(0,0,0,0.14); - color: #FFFFFF; - font-size: 20px; - font-weight: 500; - line-height: 26px; + &__copy { + font-size: 1em; + width: 400px; + max-width: 90vw; text-align: center; - text-transform: uppercase; - margin: 35px 0 14px; - transition: 200ms ease-in-out; - background-color: rgba(247, 134, 28, 0.9); + + @media screen and (max-width: 575px) { + font-size: .9em; + } } + } + + &__button { + height: 54px; + width: 198px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .14); + color: #fff; + font-size: 20px; + font-weight: 500; + line-height: 26px; + text-align: center; + text-transform: uppercase; + margin: 35px 0 14px; + transition: 200ms ease-in-out; + background-color: rgba(247, 134, 28, .9); + } } diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js index 6cb548bb9..c20ba2d77 100644 --- a/ui/app/first-time/init-menu.js +++ b/ui/app/first-time/init-menu.js @@ -10,7 +10,6 @@ const getCaretCoordinates = require('textarea-caret') const { RESTORE_VAULT_ROUTE, DEFAULT_ROUTE } = require('../routes') const { getEnvironmentType } = require('../../../app/scripts/lib/util') const { ENVIRONMENT_TYPE_POPUP } = require('../../../app/scripts/lib/enums') -const { OLD_UI_NETWORK_TYPE } = require('../../../app/scripts/controllers/network/enums') class InitializeMenuScreen extends Component { constructor (props) { @@ -190,7 +189,6 @@ class InitializeMenuScreen extends Component { showOldUI () { this.props.dispatch(actions.setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL')) - .then(() => this.props.dispatch(actions.setNetworkEndpoints(OLD_UI_NETWORK_TYPE))) } } diff --git a/ui/app/select-app.js b/ui/app/select-app.js index 808f9ba56..f2e8e8d10 100644 --- a/ui/app/select-app.js +++ b/ui/app/select-app.js @@ -6,8 +6,7 @@ const { HashRouter } = require('react-router-dom') const App = require('./app') const OldApp = require('../../old-ui/app/app') const { autoAddToBetaUI } = require('./selectors') -const { setFeatureFlag, setNetworkEndpoints } = require('./actions') -const { BETA_UI_NETWORK_TYPE } = require('../../app/scripts/controllers/network/enums') +const { setFeatureFlag } = require('./actions') const I18nProvider = require('./i18n-provider') function mapStateToProps (state) { @@ -24,11 +23,9 @@ function mapDispatchToProps (dispatch) { return { setFeatureFlagWithModal: () => { return dispatch(setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL')) - .then(() => dispatch(setNetworkEndpoints(BETA_UI_NETWORK_TYPE))) }, setFeatureFlagWithoutModal: () => { return dispatch(setFeatureFlag('betaUI', true)) - .then(() => dispatch(setNetworkEndpoints(BETA_UI_NETWORK_TYPE))) }, } } diff --git a/ui/index.js b/ui/index.js index 075faf66d..bd9ecc28b 100644 --- a/ui/index.js +++ b/ui/index.js @@ -5,11 +5,6 @@ const actions = require('./app/actions') const configureStore = require('./app/store') const txHelper = require('./lib/tx-helper') const { fetchLocale } = require('./i18n-helper') -const { - OLD_UI_NETWORK_TYPE, - BETA_UI_NETWORK_TYPE, -} = require('../app/scripts/controllers/network/enums') - const log = require('loglevel') module.exports = launchMetamaskUi @@ -55,10 +50,6 @@ async function startApp (metamaskState, accountManager, opts) { networkVersion: opts.networkVersion, }) - const useBetaUi = metamaskState.featureFlags.betaUI - const networkEndpointType = useBetaUi ? BETA_UI_NETWORK_TYPE : OLD_UI_NETWORK_TYPE - store.dispatch(actions.setNetworkEndpoints(networkEndpointType)) - // if unconfirmed txs, start on txConf page const unapprovedTxsAll = txHelper(metamaskState.unapprovedTxs, metamaskState.unapprovedMsgs, metamaskState.unapprovedPersonalMsgs, metamaskState.unapprovedTypedMessages, metamaskState.network) const numberOfUnapprivedTx = unapprovedTxsAll.length |