aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/_locales/en/messages.json2
-rw-r--r--app/scripts/controllers/network/enums.js26
-rw-r--r--app/scripts/controllers/network/network.js121
-rw-r--r--app/scripts/controllers/network/util.js36
-rw-r--r--app/scripts/controllers/transactions/lib/tx-state-history-helper.js21
-rw-r--r--app/scripts/controllers/transactions/tx-state-manager.js2
-rw-r--r--app/scripts/first-time-state.js9
-rw-r--r--app/scripts/lib/setupRaven.js42
-rw-r--r--app/scripts/metamask-controller.js1
-rw-r--r--docs/send-screen-QA-checklist.md96
-rw-r--r--mascara/src/app/first-time/create-password-screen.js133
-rw-r--r--mascara/src/app/first-time/import-seed-phrase-screen.js146
-rw-r--r--mascara/src/app/first-time/index.css33
-rw-r--r--old-ui/app/app.js3
-rw-r--r--package.json4
-rw-r--r--test/e2e/chrome/metamask.spec.js4
-rw-r--r--test/e2e/firefox/metamask.spec.js5
-rw-r--r--test/integration/lib/mascara-first-time.js29
-rw-r--r--test/screens/new-ui.js4
-rw-r--r--test/unit/network-contoller-test.js30
-rw-r--r--test/unit/tx-state-history-helper-test.js139
-rw-r--r--test/unit/tx-state-history-helper.js46
-rw-r--r--test/unit/tx-state-manager-test.js9
-rw-r--r--ui/app/actions.js18
-rw-r--r--ui/app/app.js13
-rw-r--r--ui/app/components/app-header/app-header.component.js44
-rw-r--r--ui/app/components/pages/settings/settings.js2
-rw-r--r--ui/app/components/pages/unlock-page/unlock-page.component.js1
-rw-r--r--ui/app/components/pages/unlock-page/unlock-page.container.js2
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js14
-rw-r--r--ui/app/components/text-field/text-field.component.js5
-rw-r--r--ui/app/css/itcss/components/account-menu.scss4
-rw-r--r--ui/app/css/itcss/components/header.scss4
-rw-r--r--ui/app/css/itcss/components/welcome-screen.scss97
-rw-r--r--ui/app/first-time/init-menu.js2
-rw-r--r--ui/app/select-app.js5
-rw-r--r--ui/index.js9
37 files changed, 594 insertions, 567 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/controllers/transactions/lib/tx-state-history-helper.js b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js
index 59a4b562c..4562568e9 100644
--- a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js
+++ b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js
@@ -25,26 +25,31 @@ function migrateFromSnapshotsToDiffs (longHistory) {
}
/**
- generates an array of history objects sense the previous state.
- The object has the keys opp(the operation preformed),
- path(the key and if a nested object then each key will be seperated with a `/`)
- value
- with the first entry having the note
+ Generates an array of history objects sense the previous state.
+ The object has the keys
+ op (the operation performed),
+ path (the key and if a nested object then each key will be seperated with a `/`)
+ value
+ with the first entry having the note and a timestamp when the change took place
@param previousState {object} - the previous state of the object
@param newState {object} - the update object
@param note {string} - a optional note for the state change
- @reurns {array}
+ @returns {array}
*/
function generateHistoryEntry (previousState, newState, note) {
const entry = jsonDiffer.compare(previousState, newState)
// Add a note to the first op, since it breaks if we append it to the entry
- if (note && entry[0]) entry[0].note = note
+ if (entry[0]) {
+ if (note) entry[0].note = note
+
+ entry[0].timestamp = Date.now()
+ }
return entry
}
/**
Recovers previous txMeta state obj
- @return {object}
+ @returns {object}
*/
function replayHistory (_shortHistory) {
const shortHistory = clone(_shortHistory)
diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js
index 380214c1d..00e837571 100644
--- a/app/scripts/controllers/transactions/tx-state-manager.js
+++ b/app/scripts/controllers/transactions/tx-state-manager.js
@@ -158,7 +158,7 @@ class TransactionStateManager extends EventEmitter {
/**
updates the txMeta in the list and adds a history entry
@param txMeta {Object} - the txMeta to update
- @param [note] {string} - a not about the update for history
+ @param [note] {string} - a note about the update for history
*/
updateTx (txMeta, note) {
// validate txParams
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/lib/setupRaven.js b/app/scripts/lib/setupRaven.js
index b1b67f771..d164827ab 100644
--- a/app/scripts/lib/setupRaven.js
+++ b/app/scripts/lib/setupRaven.js
@@ -25,7 +25,7 @@ function setupRaven(opts) {
const report = opts.data
try {
// handle error-like non-error exceptions
- nonErrorException(report)
+ rewriteErrorLikeExceptions(report)
// simplify certain complex error messages (e.g. Ethjs)
simplifyErrorMessages(report)
// modify report urls
@@ -42,27 +42,35 @@ function setupRaven(opts) {
return Raven
}
-function nonErrorException(report) {
- // handle errors that lost their error-ness in serialization
- if (report.message.includes('Non-Error exception captured with keys: message')) {
- if (!(report.extra && report.extra.__serialized__)) return
- report.message = `Non-Error Exception: ${report.extra.__serialized__.message}`
- }
+function rewriteErrorLikeExceptions(report) {
+ // handle errors that lost their error-ness in serialization (e.g. dnode)
+ rewriteErrorMessages(report, (errorMessage) => {
+ if (!errorMessage.includes('Non-Error exception captured with keys:')) return errorMessage
+ if (!(report.extra && report.extra.__serialized__ && report.extra.__serialized__.message)) return errorMessage
+ return `Non-Error Exception: ${report.extra.__serialized__.message}`
+ })
}
function simplifyErrorMessages(report) {
+ rewriteErrorMessages(report, (errorMessage) => {
+ // simplify ethjs error messages
+ errorMessage = extractEthjsErrorMessage(errorMessage)
+ // simplify 'Transaction Failed: known transaction'
+ if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) {
+ // cut the hash from the error message
+ errorMessage = 'Transaction Failed: known transaction'
+ }
+ return errorMessage
+ })
+}
+
+function rewriteErrorMessages(report, rewriteFn) {
+ // rewrite top level message
+ report.message = rewriteFn(report.message)
+ // rewrite each exception message
if (report.exception && report.exception.values) {
report.exception.values.forEach(item => {
- let errorMessage = item.value
- // simplify ethjs error messages
- errorMessage = extractEthjsErrorMessage(errorMessage)
- // simplify 'Transaction Failed: known transaction'
- if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) {
- // cut the hash from the error message
- errorMessage = 'Transaction Failed: known transaction'
- }
- // finalize
- item.value = errorMessage
+ item.value = rewriteFn(item.value)
})
}
}
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/docs/send-screen-QA-checklist.md b/docs/send-screen-QA-checklist.md
new file mode 100644
index 000000000..52167b50b
--- /dev/null
+++ b/docs/send-screen-QA-checklist.md
@@ -0,0 +1,96 @@
+# Send screen QA checklist:
+
+This checklist can be to guide QA of the send screen. It can also be used to guide e2e tests for the send screen.
+
+Once all of these are QA verified on master, resolutions to any bugs related to the send screen should include and update to this list.
+
+Additional features or functionality on the send screen should include an update to this list.
+
+## Send Eth mode
+ - [ ] **Header** _It should:_
+ - [ ] have title "Send ETH"
+ - [ ] have sub title "Only send ETH to an Ethereum address."
+ - [ ] return user to main screen when top right X is clicked
+ - [ ] **From row** _It should:_
+ - [ ] show the currently selected account by default
+ - [ ] show a dropdown with all of the users accounts
+ - [ ] contain the following info for each account: identicon, account name, balance in ETH, balance in current currency
+ - [ ] change the account selected in the dropdown (but not the app-wide selected account) when one in the dropdown is clicked
+ - [ ] close the dropdown, without changing the dropdown selected account, when the dropdown is open and then a click happens outside it
+ - [ ] **To row** _It should:_
+ - [ ] Show a placeholder with the text 'Recipient Address' by default
+ - [ ] Show, when clicked, a dropdown list of all 'to accounts': the users accounts, plus any other accounts they have previously sent to
+ - [ ] Show account address, and account name if it exists, of each item in the dropdown list
+ - [ ] Show a dropdown list of all to accounts (see above) whose address matches an address currently being typed in
+ - [ ] Set the input text to the address of an account clicked in the dropdown list, and also hide the dropdown
+ - [ ] Hide the dropdown without changing what is in the input if the user clicks outside the dropdown list while it is open
+ - [ ] Select the text in the input (i.e. the address) if an address is displayed and then clicked
+ - [ ] Show a 'required' error if the dropdown is opened but no account is selected
+ - [ ] Show an 'invalid address' error if text is entered in the input that cannot be a valid hex address or ens address
+ - [ ] Support ens names. (enter dinodan.eth on mainnet) After entering the plain text address, the hex address should appear in the input with a green checkmark beside
+ - [ ] Should show a 'no such address' error if a non-existent ens address is entered
+ - [ ] **Amount row** _It should:_
+ - [ ] allow user to enter any rational number >= 0
+ - [ ] allow user to copy and paste into the field
+ - [ ] show an insufficient funds error if an amount > balance - gas fee
+ - [ ] display 'ETH' after the number amount. The position of 'ETH' should change as the length of the input amount text changes
+ - [ ] display the value of the amount of ETH in the current currency, formatted in that currency
+ - [ ] show a 'max' but if amount < balance - gas fee
+ - [ ] show no max button or error if amount === balance - gas fee
+ - [ ] set the amount to balance - gas fee if the 'max' button is clicked
+ - [ ] **Gas Fee Display row** _It should:_
+ - [ ] Default to the fee given by the estimated gas price
+ - [ ] display the fee in ETH and the current currency
+ - [ ] update when changes are made using the customize gas modal
+ - [ ] **Cancel button** _It should:_
+ - [ ] Take the user back to the main screen
+ - [ ] **submit button** _It should:_
+ - [ ] be disabled if no recipient address is provided or if any field is in error
+ - [ ] sign a transaction with the info in the above form, and display the details of that transaction on the confirm screen
+
+## Send token mode
+- [ ] **Header** _It should:_
+ - [ ] have title "Send Tokens"
+ - [ ] have sub title "Only send [token symbol] to an Ethereum address."
+ - [ ] return user to main screen when top right X is clicked
+- [ ] **From row** _It should:_
+ - [ ] Behave the same as 'Send ETH mode' (see above)
+- [ ] **To row** _It should:_
+ - [ ] Behave the same as 'Send ETH mode' (see above)
+- [ ] **Amount row** _It should:_
+ - [ ] allow user to enter any rational number >= 0
+ - [ ] allow user to copy and paste into the field
+ - [ ] show an 'insufficient tokens' error if an amount > token balance
+ - [ ] show an 'insufficient funds' error if an gas fee > eth balance
+ - [ ] display [token symbol] after the number amount. The position of [token symbol] should change as the length of the input amount text changes
+ - [ ] display the value of the amount of tokens in the current currency, formatted in that currency
+ - [ ] show a 'max' but if amount < token balance
+ - [ ] show no max button or error if amount === token balance
+ - [ ] set the amount to token balance if the 'max' button is clicked
+- [ ] **Gas Fee Display row** _It should:_
+ - [ ] Behave the same as 'Send ETH mode' (see above)
+- [ ] **Cancel button** _It should:_
+ - [ ] Take the user back to the main screen
+- [ ] **submit button** _It should:_
+ - [ ] be disabled if no recipient address is provided or if any field is in error
+ - [ ] sign a token transaction with the info in the above form, and display the details of that transaction on the confirm screen
+
+## Edit send Eth mode
+ - [ ] Say 'Editing transaction' in the header
+ - [ ] display a button to go back to the confirmation screen without applying update
+ - [ ] say 'update transaction' on the submit button
+ - [ ] update the existing transaction, instead of signing a new one, when clicking the submit button
+ - [ ] Otherwise, behave the same as 'Send ETH mode' (see above)
+
+## Edit send token mode
+ - [ ] Behave the same as 'Edit send Eth mode' (see above)
+
+## Specific cases to test
+ - [ ] Send eth to a hex address
+ - [ ] Send eth to an ENS address
+ - [ ] Donate to the faucet at https://faucet.metamask.io/ and edit the transaction before confirming
+ - [ ] Send a token that is available on the 'Add Token' screen search to a hex address
+ - [ ] Create a custom token at https://tokenfactory.surge.sh/ and send it to a hex address
+ - [ ] Send a token to an ENS address
+ - [ ] Create a token transaction using https://tokenfactory.surge.sh/#/, and edit the transaction before confirming
+ - [ ] Send each of MKR, EOS and ICON using myetherwallet, and edit the transaction before confirming
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/test/unit/tx-state-history-helper-test.js b/test/unit/tx-state-history-helper-test.js
index 35e9ef188..5ad014dbb 100644
--- a/test/unit/tx-state-history-helper-test.js
+++ b/test/unit/tx-state-history-helper-test.js
@@ -1,26 +1,129 @@
const assert = require('assert')
-const clone = require('clone')
const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
+const testVault = require('../data/v17-long-history.json')
-describe('deepCloneFromTxMeta', function () {
- it('should clone deep', function () {
- const input = {
- foo: {
- bar: {
- bam: 'baz'
+describe ('Transaction state history helper', function () {
+
+ describe('#snapshotFromTxMeta', function () {
+ it('should clone deep', function () {
+ const input = {
+ foo: {
+ bar: {
+ bam: 'baz'
+ }
}
}
- }
- const output = txStateHistoryHelper.snapshotFromTxMeta(input)
- assert('foo' in output, 'has a foo key')
- assert('bar' in output.foo, 'has a bar key')
- assert('bam' in output.foo.bar, 'has a bar key')
- assert.equal(output.foo.bar.bam, 'baz', 'has a baz value')
+ const output = txStateHistoryHelper.snapshotFromTxMeta(input)
+ assert('foo' in output, 'has a foo key')
+ assert('bar' in output.foo, 'has a bar key')
+ assert('bam' in output.foo.bar, 'has a bar key')
+ assert.equal(output.foo.bar.bam, 'baz', 'has a baz value')
+ })
+
+ it('should remove the history key', function () {
+ const input = { foo: 'bar', history: 'remembered' }
+ const output = txStateHistoryHelper.snapshotFromTxMeta(input)
+ assert(typeof output.history, 'undefined', 'should remove history')
+ })
})
- it('should remove the history key', function () {
- const input = { foo: 'bar', history: 'remembered' }
- const output = txStateHistoryHelper.snapshotFromTxMeta(input)
- assert(typeof output.history, 'undefined', 'should remove history')
+ describe('#migrateFromSnapshotsToDiffs', function () {
+ it('migrates history to diffs and can recover original values', function () {
+ testVault.data.TransactionController.transactions.forEach((tx, index) => {
+ const newHistory = txStateHistoryHelper.migrateFromSnapshotsToDiffs(tx.history)
+ newHistory.forEach((newEntry, index) => {
+ if (index === 0) {
+ assert.equal(Array.isArray(newEntry), false, 'initial history item IS NOT a json patch obj')
+ } else {
+ assert.equal(Array.isArray(newEntry), true, 'non-initial history entry IS a json patch obj')
+ }
+ const oldEntry = tx.history[index]
+ const historySubset = newHistory.slice(0, index + 1)
+ const reconstructedValue = txStateHistoryHelper.replayHistory(historySubset)
+ assert.deepEqual(oldEntry, reconstructedValue, 'was able to reconstruct old entry from diffs')
+ })
+ })
+ })
+ })
+
+ describe('#replayHistory', function () {
+ it('replaying history does not mutate the original obj', function () {
+ const initialState = { test: true, message: 'hello', value: 1 }
+ const diff1 = [{
+ "op": "replace",
+ "path": "/message",
+ "value": "haay",
+ }]
+ const diff2 = [{
+ "op": "replace",
+ "path": "/value",
+ "value": 2,
+ }]
+ const history = [initialState, diff1, diff2]
+
+ const beforeStateSnapshot = JSON.stringify(initialState)
+ const latestState = txStateHistoryHelper.replayHistory(history)
+ const afterStateSnapshot = JSON.stringify(initialState)
+
+ assert.notEqual(initialState, latestState, 'initial state is not the same obj as the latest state')
+ assert.equal(beforeStateSnapshot, afterStateSnapshot, 'initial state is not modified during run')
+ })
+ })
+
+ describe('#generateHistoryEntry', function () {
+
+ function generateHistoryEntryTest(note) {
+
+ const prevState = {
+ someValue: 'value 1',
+ foo: {
+ bar: {
+ bam: 'baz'
+ }
+ }
+ }
+
+ const nextState = {
+ newPropRoot: 'new property - root',
+ someValue: 'value 2',
+ foo: {
+ newPropFirstLevel: 'new property - first level',
+ bar: {
+ bam: 'baz'
+ }
+ }
+ }
+
+ const before = new Date().getTime()
+ const result = txStateHistoryHelper.generateHistoryEntry(prevState, nextState, note)
+ const after = new Date().getTime()
+
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 3)
+
+ const expectedEntry1 = { op: 'add', path: '/foo/newPropFirstLevel', value: 'new property - first level' }
+ assert.equal(result[0].op, expectedEntry1.op)
+ assert.equal(result[0].path, expectedEntry1.path)
+ assert.equal(result[0].value, expectedEntry1.value)
+ assert.equal(result[0].value, expectedEntry1.value)
+ if (note)
+ assert.equal(result[0].note, note)
+
+ assert.ok(result[0].timestamp >= before && result[0].timestamp <= after)
+
+ const expectedEntry2 = { op: 'replace', path: '/someValue', value: 'value 2' }
+ assert.deepEqual(result[1], expectedEntry2)
+
+ const expectedEntry3 = { op: 'add', path: '/newPropRoot', value: 'new property - root' }
+ assert.deepEqual(result[2], expectedEntry3)
+ }
+
+ it('should generate history entries', function () {
+ generateHistoryEntryTest()
+ })
+
+ it('should add note to first entry', function () {
+ generateHistoryEntryTest('custom note')
+ })
})
-})
+}) \ No newline at end of file
diff --git a/test/unit/tx-state-history-helper.js b/test/unit/tx-state-history-helper.js
deleted file mode 100644
index 35f7dac57..000000000
--- a/test/unit/tx-state-history-helper.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const assert = require('assert')
-const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
-const testVault = require('../data/v17-long-history.json')
-
-
-describe('tx-state-history-helper', function () {
- it('migrates history to diffs and can recover original values', function () {
- testVault.data.TransactionController.transactions.forEach((tx, index) => {
- const newHistory = txStateHistoryHelper.migrateFromSnapshotsToDiffs(tx.history)
- newHistory.forEach((newEntry, index) => {
- if (index === 0) {
- assert.equal(Array.isArray(newEntry), false, 'initial history item IS NOT a json patch obj')
- } else {
- assert.equal(Array.isArray(newEntry), true, 'non-initial history entry IS a json patch obj')
- }
- const oldEntry = tx.history[index]
- const historySubset = newHistory.slice(0, index + 1)
- const reconstructedValue = txStateHistoryHelper.replayHistory(historySubset)
- assert.deepEqual(oldEntry, reconstructedValue, 'was able to reconstruct old entry from diffs')
- })
- })
- })
-
- it('replaying history does not mutate the original obj', function () {
- const initialState = { test: true, message: 'hello', value: 1 }
- const diff1 = [{
- "op": "replace",
- "path": "/message",
- "value": "haay",
- }]
- const diff2 = [{
- "op": "replace",
- "path": "/value",
- "value": 2,
- }]
- const history = [initialState, diff1, diff2]
-
- const beforeStateSnapshot = JSON.stringify(initialState)
- const latestState = txStateHistoryHelper.replayHistory(history)
- const afterStateSnapshot = JSON.stringify(initialState)
-
- assert.notEqual(initialState, latestState, 'initial state is not the same obj as the latest state')
- assert.equal(beforeStateSnapshot, afterStateSnapshot, 'initial state is not modified during run')
- })
-
-})
diff --git a/test/unit/tx-state-manager-test.js b/test/unit/tx-state-manager-test.js
index e5fe68d0b..179542f90 100644
--- a/test/unit/tx-state-manager-test.js
+++ b/test/unit/tx-state-manager-test.js
@@ -176,14 +176,21 @@ describe('TransactionStateManager', function () {
assert.deepEqual(updatedTx.history[0], txStateHistoryHelper.snapshotFromTxMeta(updatedTx), 'first history item is initial state')
// modify value and updateTx
updatedTx.txParams.gasPrice = desiredGasPrice
+ const before = new Date().getTime()
txStateManager.updateTx(updatedTx)
+ const after = new Date().getTime()
// check updated value
const result = txStateManager.getTx('1')
assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated')
// validate history was updated
assert.equal(result.history.length, 2, 'two history items (initial + diff)')
+ assert.equal(result.history[1].length, 1, 'two history state items (initial + diff)')
+
const expectedEntry = { op: 'replace', path: '/txParams/gasPrice', value: desiredGasPrice }
- assert.deepEqual(result.history[1], [expectedEntry], 'two history items (initial + diff)')
+ assert.deepEqual(result.history[1][0].op, expectedEntry.op, 'two history items (initial + diff) operation')
+ assert.deepEqual(result.history[1][0].path, expectedEntry.path, 'two history items (initial + diff) path')
+ assert.deepEqual(result.history[1][0].value, expectedEntry.value, 'two history items (initial + diff) value')
+ assert.ok(result.history[1][0].timestamp >= before && result.history[1][0].timestamp <= after)
})
})
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/app.js b/ui/app/app.js
index 5bc571c64..f840cc34e 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -1,7 +1,7 @@
const { Component } = require('react')
const PropTypes = require('prop-types')
const connect = require('react-redux').connect
-const { Route, Switch, withRouter, matchPath } = require('react-router-dom')
+const { Route, Switch, withRouter } = require('react-router-dom')
const { compose } = require('recompose')
const h = require('react-hyperscript')
const actions = require('./actions')
@@ -83,15 +83,6 @@ class App extends Component {
)
}
- renderAppHeader () {
- const { location } = this.props
- const isInitializing = matchPath(location.pathname, {
- path: INITIALIZE_ROUTE, exact: false,
- })
-
- return isInitializing ? null : h(AppHeader)
- }
-
render () {
const {
isLoading,
@@ -128,7 +119,7 @@ class App extends Component {
// global modal
h(Modal, {}, []),
- this.renderAppHeader(),
+ h(AppHeader),
// sidebar
this.renderSidebar(),
diff --git a/ui/app/components/app-header/app-header.component.js b/ui/app/components/app-header/app-header.component.js
index cf36e0d79..62b04562a 100644
--- a/ui/app/components/app-header/app-header.component.js
+++ b/ui/app/components/app-header/app-header.component.js
@@ -1,9 +1,13 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
+import { matchPath } from 'react-router-dom'
-const { ENVIRONMENT_TYPE_NOTIFICATION } = require('../../../../app/scripts/lib/enums')
-const { DEFAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE } = require('../../routes')
+const {
+ ENVIRONMENT_TYPE_NOTIFICATION,
+ ENVIRONMENT_TYPE_POPUP,
+} = require('../../../../app/scripts/lib/enums')
+const { DEFAULT_ROUTE, INITIALIZE_ROUTE, CONFIRM_TRANSACTION_ROUTE } = require('../../routes')
const Identicon = require('../identicon')
const NetworkIndicator = require('../network')
@@ -36,13 +40,23 @@ class AppHeader extends Component {
: hideNetworkDropdown()
}
+ isConfirming () {
+ const { location } = this.props
+
+ return Boolean(matchPath(location.pathname, {
+ path: CONFIRM_TRANSACTION_ROUTE, exact: false,
+ }))
+ }
+
renderAccountMenu () {
const { isUnlocked, toggleAccountMenu, selectedAddress } = this.props
return isUnlocked && (
<div
- className="account-menu__icon"
- onClick={toggleAccountMenu}
+ className={classnames('account-menu__icon', {
+ 'account-menu__icon--disabled': this.isConfirming(),
+ })}
+ onClick={() => this.isConfirming() || toggleAccountMenu()}
>
<Identicon
address={selectedAddress}
@@ -52,6 +66,26 @@ class AppHeader extends Component {
)
}
+ hideAppHeader () {
+ const { location } = this.props
+
+ const isInitializing = Boolean(matchPath(location.pathname, {
+ path: INITIALIZE_ROUTE, exact: false,
+ }))
+
+ if (isInitializing) {
+ return true
+ }
+
+ if (window.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
+ return true
+ }
+
+ if (window.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_POPUP && this.isConfirming()) {
+ return true
+ }
+ }
+
render () {
const {
network,
@@ -61,7 +95,7 @@ class AppHeader extends Component {
isUnlocked,
} = this.props
- if (window.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
+ if (this.hideAppHeader()) {
return null
}
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/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index 16dbd273b..c07c96ccc 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -28,6 +28,10 @@ const currencies = require('currency-formatter/currencies')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
+const {
+ ENVIRONMENT_TYPE_POPUP,
+ ENVIRONMENT_TYPE_NOTIFICATION,
+} = require('../../../../app/scripts/lib/enums')
ConfirmSendEther.contextTypes = {
t: PropTypes.func,
@@ -293,6 +297,14 @@ ConfirmSendEther.prototype.editTransaction = function (txMeta) {
history.push(SEND_ROUTE)
}
+ConfirmSendEther.prototype.renderNetworkDisplay = function () {
+ const windowType = window.METAMASK_UI_TYPE
+
+ return (windowType === ENVIRONMENT_TYPE_NOTIFICATION || windowType === ENVIRONMENT_TYPE_POPUP)
+ ? h(NetworkDisplay)
+ : null
+}
+
ConfirmSendEther.prototype.render = function () {
const {
currentCurrency,
@@ -358,7 +370,7 @@ ConfirmSendEther.prototype.render = function () {
visibility: !txMeta.lastGasPrice ? 'initial' : 'hidden',
},
}, 'Edit'),
- window.METAMASK_UI_TYPE === 'notification' && h(NetworkDisplay),
+ this.renderNetworkDisplay(),
]),
h('.page-container__title', title),
h('.page-container__subtitle', subtitle),
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/account-menu.scss b/ui/app/css/itcss/components/account-menu.scss
index 824b2ddb6..657760ab5 100644
--- a/ui/app/css/itcss/components/account-menu.scss
+++ b/ui/app/css/itcss/components/account-menu.scss
@@ -23,6 +23,10 @@
&__icon {
margin-left: 20px;
cursor: pointer;
+
+ &--disabled {
+ cursor: initial;
+ }
}
&__header {
diff --git a/ui/app/css/itcss/components/header.scss b/ui/app/css/itcss/components/header.scss
index cef61d0e2..3ccfd5c15 100644
--- a/ui/app/css/itcss/components/header.scss
+++ b/ui/app/css/itcss/components/header.scss
@@ -82,10 +82,6 @@
display: flex;
flex-flow: row nowrap;
align-items: center;
-
- .identicon {
- cursor: pointer;
- }
}
}
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