mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'develop' into WatchTokenFeature
This commit is contained in:
commit
3f57d5f66b
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
- [#4884](https://github.com/MetaMask/metamask-extension/pull/4884): Allow to have tokens per account and network.
|
||||||
|
|
||||||
|
## 4.9.0 Tue Aug 07 2018
|
||||||
|
|
||||||
- Add new tokens auto detection
|
- Add new tokens auto detection
|
||||||
- Remove rejected transactions from transaction history
|
- Remove rejected transactions from transaction history
|
||||||
- Add Trezor Support
|
- Add Trezor Support
|
||||||
|
@ -27,8 +27,9 @@ If you're a web dapp developer, we've got two types of guides for you:
|
|||||||
## Building locally
|
## Building locally
|
||||||
|
|
||||||
- Install [Node.js](https://nodejs.org/en/) version 8.11.3 and npm version 6.1.0
|
- Install [Node.js](https://nodejs.org/en/) version 8.11.3 and npm version 6.1.0
|
||||||
- Install dependencies:
|
- If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you.
|
||||||
- If you are using nvm (recommended) running `nvm use` will automatically choose the right node version for you.
|
- Select npm 6.1.0: ```npm install -g npm@6.1.0```
|
||||||
|
- Install dependencies: ```npm install```
|
||||||
- Install gulp globally with `npm install -g gulp-cli`.
|
- Install gulp globally with `npm install -g gulp-cli`.
|
||||||
- Build the project to the `./dist/` folder with `gulp build`.
|
- Build the project to the `./dist/` folder with `gulp build`.
|
||||||
- Optionally, to rebuild on file changes, run `gulp dev`.
|
- Optionally, to rebuild on file changes, run `gulp dev`.
|
||||||
|
@ -513,6 +513,9 @@
|
|||||||
"invalidRPC": {
|
"invalidRPC": {
|
||||||
"message": "Invalid RPC URI"
|
"message": "Invalid RPC URI"
|
||||||
},
|
},
|
||||||
|
"invalidSeedPhrase": {
|
||||||
|
"message": "Invalid seed phrase"
|
||||||
|
},
|
||||||
"jsonFail": {
|
"jsonFail": {
|
||||||
"message": "Something went wrong. Please make sure your JSON file is properly formatted."
|
"message": "Something went wrong. Please make sure your JSON file is properly formatted."
|
||||||
},
|
},
|
||||||
|
@ -3,26 +3,29 @@
|
|||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Phishing Warning</title>
|
<title>Dangerous Website Warning</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background: #c50000;
|
background: #c50000;
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
.centered {
|
|
||||||
display: flex;
|
.centered {
|
||||||
flex-direction: column;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
color: white;
|
justify-content: center;
|
||||||
max-width: 600px;
|
color: white;
|
||||||
}
|
max-width: 600px;
|
||||||
a {
|
}
|
||||||
color: white;
|
|
||||||
}
|
a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -50,10 +53,10 @@ a {
|
|||||||
|
|
||||||
<img src="/images/ethereum-metamask-chrome.png" style="width:100%">
|
<img src="/images/ethereum-metamask-chrome.png" style="width:100%">
|
||||||
<h3>ATTENTION</h3>
|
<h3>ATTENTION</h3>
|
||||||
<p>MetaMask believes this domain to have malicious intent and has prevented you from interacting with it.</p>
|
<p>MetaMask believes this domain could currently compromise your security and has prevented you from interacting with it.</p>
|
||||||
<p>This is because the site tested positive on the <a href="https://github.com/metamask/eth-phishing-detect">Ethereum Phishing Detector</a>.</p>
|
<p>This is because the site tested positive on the <a href="https://github.com/metamask/eth-phishing-detect">Ethereum Phishing Detector</a>. This includes outright malicious websites and legitimate websites that have been compromised by a malicious actor.</p>
|
||||||
<p>You can turn MetaMask off to interact with this site, but it's advised not to.</p>
|
<p>You can turn MetaMask off to interact with this site, but it is advised not to.</p>
|
||||||
<p>If you think this domain is incorrectly flagged, <a href="https://github.com/metamask/eth-phishing-detect/issues/new">please file an issue</a>.</p>
|
<p>If you think this domain is incorrectly flagged or if a blocked legitimate website has resolved its security issues, <a href="https://github.com/metamask/eth-phishing-detect/issues/new">please file an issue</a>.</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
@ -85,7 +85,7 @@ class DetectTokensController {
|
|||||||
set preferences (preferences) {
|
set preferences (preferences) {
|
||||||
if (!preferences) { return }
|
if (!preferences) { return }
|
||||||
this._preferences = preferences
|
this._preferences = preferences
|
||||||
preferences.store.subscribe(({ tokens }) => { this.tokenAddresses = tokens.map((obj) => { return obj.address }) })
|
preferences.store.subscribe(({ tokens = [] }) => { this.tokenAddresses = tokens.map((obj) => { return obj.address }) })
|
||||||
preferences.store.subscribe(({ selectedAddress }) => {
|
preferences.store.subscribe(({ selectedAddress }) => {
|
||||||
if (this.selectedAddress !== selectedAddress) {
|
if (this.selectedAddress !== selectedAddress) {
|
||||||
this.selectedAddress = selectedAddress
|
this.selectedAddress = selectedAddress
|
||||||
|
@ -14,6 +14,7 @@ class PreferencesController {
|
|||||||
* @property {array} store.frequentRpcList A list of custom rpcs to provide the user
|
* @property {array} store.frequentRpcList A list of custom rpcs to provide the user
|
||||||
* @property {string} store.currentAccountTab Indicates the selected tab in the ui
|
* @property {string} store.currentAccountTab Indicates the selected tab in the ui
|
||||||
* @property {array} store.tokens The tokens the user wants display in their token lists
|
* @property {array} store.tokens The tokens the user wants display in their token lists
|
||||||
|
* @property {object} store.accountTokens The tokens stored per account and then per network type
|
||||||
* @property {boolean} store.useBlockie The users preference for blockie identicons within the UI
|
* @property {boolean} store.useBlockie The users preference for blockie identicons within the UI
|
||||||
* @property {object} store.featureFlags A key-boolean map, where keys refer to features and booleans to whether the
|
* @property {object} store.featureFlags A key-boolean map, where keys refer to features and booleans to whether the
|
||||||
* user wishes to see that feature
|
* user wishes to see that feature
|
||||||
@ -25,6 +26,7 @@ class PreferencesController {
|
|||||||
const initState = extend({
|
const initState = extend({
|
||||||
frequentRpcList: [],
|
frequentRpcList: [],
|
||||||
currentAccountTab: 'history',
|
currentAccountTab: 'history',
|
||||||
|
accountTokens: {},
|
||||||
tokens: [],
|
tokens: [],
|
||||||
suggestedTokens: {},
|
suggestedTokens: {},
|
||||||
useBlockie: false,
|
useBlockie: false,
|
||||||
@ -35,9 +37,10 @@ class PreferencesController {
|
|||||||
}, opts.initState)
|
}, opts.initState)
|
||||||
|
|
||||||
this.diagnostics = opts.diagnostics
|
this.diagnostics = opts.diagnostics
|
||||||
|
this.network = opts.network
|
||||||
this.store = new ObservableStore(initState)
|
this.store = new ObservableStore(initState)
|
||||||
this.showAddTokenUi = opts.showAddTokenUi
|
this.showAddTokenUi = opts.showAddTokenUi
|
||||||
|
this._subscribeProviderType()
|
||||||
}
|
}
|
||||||
// PUBLIC METHODS
|
// PUBLIC METHODS
|
||||||
|
|
||||||
@ -121,12 +124,19 @@ class PreferencesController {
|
|||||||
*/
|
*/
|
||||||
setAddresses (addresses) {
|
setAddresses (addresses) {
|
||||||
const oldIdentities = this.store.getState().identities
|
const oldIdentities = this.store.getState().identities
|
||||||
|
const oldAccountTokens = this.store.getState().accountTokens
|
||||||
|
|
||||||
const identities = addresses.reduce((ids, address, index) => {
|
const identities = addresses.reduce((ids, address, index) => {
|
||||||
const oldId = oldIdentities[address] || {}
|
const oldId = oldIdentities[address] || {}
|
||||||
ids[address] = {name: `Account ${index + 1}`, address, ...oldId}
|
ids[address] = {name: `Account ${index + 1}`, address, ...oldId}
|
||||||
return ids
|
return ids
|
||||||
}, {})
|
}, {})
|
||||||
this.store.updateState({ identities })
|
const accountTokens = addresses.reduce((tokens, address) => {
|
||||||
|
const oldTokens = oldAccountTokens[address] || {}
|
||||||
|
tokens[address] = oldTokens
|
||||||
|
return tokens
|
||||||
|
}, {})
|
||||||
|
this.store.updateState({ identities, accountTokens })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,11 +147,13 @@ class PreferencesController {
|
|||||||
*/
|
*/
|
||||||
removeAddress (address) {
|
removeAddress (address) {
|
||||||
const identities = this.store.getState().identities
|
const identities = this.store.getState().identities
|
||||||
|
const accountTokens = this.store.getState().accountTokens
|
||||||
if (!identities[address]) {
|
if (!identities[address]) {
|
||||||
throw new Error(`${address} can't be deleted cause it was not found`)
|
throw new Error(`${address} can't be deleted cause it was not found`)
|
||||||
}
|
}
|
||||||
delete identities[address]
|
delete identities[address]
|
||||||
this.store.updateState({ identities })
|
delete accountTokens[address]
|
||||||
|
this.store.updateState({ identities, accountTokens })
|
||||||
|
|
||||||
// If the selected account is no longer valid,
|
// If the selected account is no longer valid,
|
||||||
// select an arbitrary other account:
|
// select an arbitrary other account:
|
||||||
@ -161,14 +173,17 @@ class PreferencesController {
|
|||||||
*/
|
*/
|
||||||
addAddresses (addresses) {
|
addAddresses (addresses) {
|
||||||
const identities = this.store.getState().identities
|
const identities = this.store.getState().identities
|
||||||
|
const accountTokens = this.store.getState().accountTokens
|
||||||
addresses.forEach((address) => {
|
addresses.forEach((address) => {
|
||||||
// skip if already exists
|
// skip if already exists
|
||||||
if (identities[address]) return
|
if (identities[address]) return
|
||||||
// add missing identity
|
// add missing identity
|
||||||
const identityCount = Object.keys(identities).length
|
const identityCount = Object.keys(identities).length
|
||||||
|
|
||||||
|
accountTokens[address] = {}
|
||||||
identities[address] = { name: `Account ${identityCount + 1}`, address }
|
identities[address] = { name: `Account ${identityCount + 1}`, address }
|
||||||
})
|
})
|
||||||
this.store.updateState({ identities })
|
this.store.updateState({ identities, accountTokens })
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -226,15 +241,15 @@ class PreferencesController {
|
|||||||
* Setter for the `selectedAddress` property
|
* Setter for the `selectedAddress` property
|
||||||
*
|
*
|
||||||
* @param {string} _address A new hex address for an account
|
* @param {string} _address A new hex address for an account
|
||||||
* @returns {Promise<void>} Promise resolves with undefined
|
* @returns {Promise<void>} Promise resolves with tokens
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
setSelectedAddress (_address) {
|
setSelectedAddress (_address) {
|
||||||
return new Promise((resolve, reject) => {
|
const address = normalizeAddress(_address)
|
||||||
const address = normalizeAddress(_address)
|
this._updateTokens(address)
|
||||||
this.store.updateState({ selectedAddress: address })
|
this.store.updateState({ selectedAddress: address })
|
||||||
resolve()
|
const tokens = this.store.getState().tokens
|
||||||
})
|
return Promise.resolve(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -283,9 +298,7 @@ class PreferencesController {
|
|||||||
} else {
|
} else {
|
||||||
tokens.push(newEntry)
|
tokens.push(newEntry)
|
||||||
}
|
}
|
||||||
|
this._updateAccountTokens(tokens)
|
||||||
this.store.updateState({ tokens })
|
|
||||||
|
|
||||||
return Promise.resolve(tokens)
|
return Promise.resolve(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,10 +311,8 @@ class PreferencesController {
|
|||||||
*/
|
*/
|
||||||
removeToken (rawAddress) {
|
removeToken (rawAddress) {
|
||||||
const tokens = this.store.getState().tokens
|
const tokens = this.store.getState().tokens
|
||||||
|
|
||||||
const updatedTokens = tokens.filter(token => token.address !== rawAddress)
|
const updatedTokens = tokens.filter(token => token.address !== rawAddress)
|
||||||
|
this._updateAccountTokens(updatedTokens)
|
||||||
this.store.updateState({ tokens: updatedTokens })
|
|
||||||
return Promise.resolve(updatedTokens)
|
return Promise.resolve(updatedTokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,6 +454,57 @@ class PreferencesController {
|
|||||||
const numDecimals = parseInt(decimals, 10)
|
const numDecimals = parseInt(decimals, 10)
|
||||||
if (isNaN(numDecimals) || numDecimals > 18 || numDecimals < 0) throw new Error(`Invalid decimals ${decimals}`)
|
if (isNaN(numDecimals) || numDecimals > 18 || numDecimals < 0) throw new Error(`Invalid decimals ${decimals}`)
|
||||||
if (!isValidAddress(rawAddress)) throw new Error(`Invalid address ${rawAddress}`)
|
if (!isValidAddress(rawAddress)) throw new Error(`Invalid address ${rawAddress}`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscription to network provider type.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_subscribeProviderType () {
|
||||||
|
this.network.providerStore.subscribe(() => {
|
||||||
|
const { tokens } = this._getTokenRelatedStates()
|
||||||
|
this.store.updateState({ tokens })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates `accountTokens` and `tokens` of current account and network according to it.
|
||||||
|
*
|
||||||
|
* @param {array} tokens Array of tokens to be updated.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_updateAccountTokens (tokens) {
|
||||||
|
const { accountTokens, providerType, selectedAddress } = this._getTokenRelatedStates()
|
||||||
|
accountTokens[selectedAddress][providerType] = tokens
|
||||||
|
this.store.updateState({ accountTokens, tokens })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates `tokens` of current account and network.
|
||||||
|
*
|
||||||
|
* @param {string} selectedAddress Account address to be updated with.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_updateTokens (selectedAddress) {
|
||||||
|
const { tokens } = this._getTokenRelatedStates(selectedAddress)
|
||||||
|
this.store.updateState({ tokens })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A getter for `tokens` and `accountTokens` related states.
|
||||||
|
*
|
||||||
|
* @param {string} selectedAddress A new hex address for an account
|
||||||
|
* @returns {Object.<array, object, string, string>} States to interact with tokens in `accountTokens`
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_getTokenRelatedStates (selectedAddress) {
|
||||||
|
const accountTokens = this.store.getState().accountTokens
|
||||||
|
if (!selectedAddress) selectedAddress = this.store.getState().selectedAddress
|
||||||
|
const providerType = this.network.providerStore.getState().type
|
||||||
|
if (!(selectedAddress in accountTokens)) accountTokens[selectedAddress] = {}
|
||||||
|
if (!(providerType in accountTokens[selectedAddress])) accountTokens[selectedAddress][providerType] = []
|
||||||
|
const tokens = accountTokens[selectedAddress][providerType]
|
||||||
|
return { tokens, accountTokens, providerType, selectedAddress }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
initState: initState.PreferencesController,
|
initState: initState.PreferencesController,
|
||||||
initLangCode: opts.initLangCode,
|
initLangCode: opts.initLangCode,
|
||||||
showAddTokenUi: opts.showAddTokenUi,
|
showAddTokenUi: opts.showAddTokenUi,
|
||||||
|
network: this.networkController,
|
||||||
})
|
})
|
||||||
|
|
||||||
// currency controller
|
// currency controller
|
||||||
@ -1439,7 +1440,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A method for activating the retrieval of price data and auto detect tokens,
|
* A method for activating the retrieval of price data,
|
||||||
* which should only be fetched when the UI is visible.
|
* which should only be fetched when the UI is visible.
|
||||||
* @private
|
* @private
|
||||||
* @param {boolean} active - True if price data should be getting fetched.
|
* @param {boolean} active - True if price data should be getting fetched.
|
||||||
|
40
app/scripts/migrations/028.js
Normal file
40
app/scripts/migrations/028.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// next version number
|
||||||
|
const version = 28
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
normalizes txParams on unconfirmed txs
|
||||||
|
|
||||||
|
*/
|
||||||
|
const clone = require('clone')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
version,
|
||||||
|
|
||||||
|
migrate: async function (originalVersionedData) {
|
||||||
|
const versionedData = clone(originalVersionedData)
|
||||||
|
versionedData.meta.version = version
|
||||||
|
const state = versionedData.data
|
||||||
|
const newState = transformState(state)
|
||||||
|
versionedData.data = newState
|
||||||
|
return versionedData
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformState (state) {
|
||||||
|
const newState = state
|
||||||
|
|
||||||
|
if (newState.PreferencesController) {
|
||||||
|
if (newState.PreferencesController.tokens) {
|
||||||
|
const identities = newState.TransactionController.identities
|
||||||
|
const tokens = newState.PreferencesController.tokens
|
||||||
|
newState.PreferencesController.accountTokens = {}
|
||||||
|
for (const identity in identities) {
|
||||||
|
newState.PreferencesController.accountTokens[identity] = {'mainnet': tokens}
|
||||||
|
}
|
||||||
|
newState.PreferencesController.tokens = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newState
|
||||||
|
}
|
@ -38,4 +38,5 @@ module.exports = [
|
|||||||
require('./025'),
|
require('./025'),
|
||||||
require('./026'),
|
require('./026'),
|
||||||
require('./027'),
|
require('./027'),
|
||||||
|
require('./028'),
|
||||||
]
|
]
|
||||||
|
@ -106,6 +106,7 @@ class ConfirmSeedScreen extends Component {
|
|||||||
key={i}
|
key={i}
|
||||||
className={classnames('backup-phrase__confirm-seed-option', {
|
className={classnames('backup-phrase__confirm-seed-option', {
|
||||||
'backup-phrase__confirm-seed-option--selected': isSelected,
|
'backup-phrase__confirm-seed-option--selected': isSelected,
|
||||||
|
'backup-phrase__confirm-seed-option--unselected': !isSelected,
|
||||||
})}
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!isSelected) {
|
if (!isSelected) {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import {validateMnemonic} from 'bip39'
|
||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
@ -39,8 +40,12 @@ class ImportSeedPhraseScreen extends Component {
|
|||||||
handleSeedPhraseChange (seedPhrase) {
|
handleSeedPhraseChange (seedPhrase) {
|
||||||
let seedPhraseError = null
|
let seedPhraseError = null
|
||||||
|
|
||||||
if (seedPhrase && this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) {
|
if (seedPhrase) {
|
||||||
seedPhraseError = this.context.t('seedPhraseReq')
|
if (this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) {
|
||||||
|
seedPhraseError = this.context.t('seedPhraseReq')
|
||||||
|
} else if (!validateMnemonic(seedPhrase)) {
|
||||||
|
seedPhraseError = this.context.t('invalidSeedPhrase')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ seedPhrase, seedPhraseError })
|
this.setState({ seedPhrase, seedPhraseError })
|
||||||
|
@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
|
|||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import { withRouter, Switch, Route } from 'react-router-dom'
|
import { withRouter, Switch, Route } from 'react-router-dom'
|
||||||
import { compose } from 'recompose'
|
import { compose } from 'recompose'
|
||||||
import classnames from 'classnames'
|
|
||||||
|
|
||||||
import CreatePasswordScreen from './create-password-screen'
|
import CreatePasswordScreen from './create-password-screen'
|
||||||
import UniqueImageScreen from './unique-image-screen'
|
import UniqueImageScreen from './unique-image-screen'
|
||||||
@ -44,28 +43,9 @@ class FirstTimeFlow extends Component {
|
|||||||
noActiveNotices: false,
|
noActiveNotices: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAppBar () {
|
|
||||||
const { welcomeScreenSeen } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="alpha-warning__container">
|
|
||||||
<h2 className={classnames({
|
|
||||||
'alpha-warning': welcomeScreenSeen,
|
|
||||||
'alpha-warning-welcome-screen': !welcomeScreenSeen,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
Please be aware that this version is still under development
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { isPopup } = this.props
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-column flex-grow">
|
<div className="flex-column flex-grow">
|
||||||
{ !isPopup && this.renderAppBar() }
|
|
||||||
<div className="first-time-flow">
|
<div className="first-time-flow">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path={INITIALIZE_IMPORT_ACCOUNT_ROUTE} component={ImportAccountScreen} />
|
<Route exact path={INITIALIZE_IMPORT_ACCOUNT_ROUTE} component={ImportAccountScreen} />
|
||||||
|
86
old-ui/app/account-qr.js
Normal file
86
old-ui/app/account-qr.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
const PropTypes = require('prop-types')
|
||||||
|
const {PureComponent} = require('react')
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const {qrcode: qrCode} = require('qrcode-npm')
|
||||||
|
const {connect} = require('react-redux')
|
||||||
|
const {isHexPrefixed} = require('ethereumjs-util')
|
||||||
|
const actions = require('../../ui/app/actions')
|
||||||
|
const CopyButton = require('./components/copyButton')
|
||||||
|
|
||||||
|
class AccountQrScreen extends PureComponent {
|
||||||
|
static defaultProps = {
|
||||||
|
warning: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
buyView: PropTypes.any.isRequired,
|
||||||
|
Qr: PropTypes.object.isRequired,
|
||||||
|
selectedAddress: PropTypes.string.isRequired,
|
||||||
|
warning: PropTypes.node,
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const {dispatch, Qr, selectedAddress, warning} = this.props
|
||||||
|
const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}`
|
||||||
|
const qrImage = qrCode(4, 'M')
|
||||||
|
|
||||||
|
qrImage.addData(address)
|
||||||
|
qrImage.make()
|
||||||
|
|
||||||
|
return h('div.flex-column.full-width', {
|
||||||
|
style: {
|
||||||
|
alignItems: 'center',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
padding: '50px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('div.flex-row.full-width', {
|
||||||
|
style: {
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
|
||||||
|
onClick () {
|
||||||
|
dispatch(actions.backToAccountDetail(selectedAddress))
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
h('div.qr-header', Qr.message),
|
||||||
|
warning && h('span.error.flex-center', {
|
||||||
|
style: {
|
||||||
|
textAlign: 'center',
|
||||||
|
width: '229px',
|
||||||
|
height: '82px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
this.props.warning,
|
||||||
|
]),
|
||||||
|
h('div#qr-container.flex-column', {
|
||||||
|
style: {
|
||||||
|
marginTop: '25px',
|
||||||
|
marginBottom: '15px',
|
||||||
|
},
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: qrImage.createTableTag(4),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
h('div.flex-row.full-width', [
|
||||||
|
h('h3.ellip-address.grow-tenx', Qr.data),
|
||||||
|
h(CopyButton, {
|
||||||
|
value: Qr.data,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
Qr: state.appState.Qr,
|
||||||
|
buyView: state.appState.buyView,
|
||||||
|
warning: state.appState.warning,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(AccountQrScreen)
|
@ -14,6 +14,7 @@ const NewKeyChainScreen = require('./new-keychain')
|
|||||||
const UnlockScreen = require('./unlock')
|
const UnlockScreen = require('./unlock')
|
||||||
// accounts
|
// accounts
|
||||||
const AccountDetailScreen = require('./account-detail')
|
const AccountDetailScreen = require('./account-detail')
|
||||||
|
const AccountQrScreen = require('./account-qr')
|
||||||
const SendTransactionScreen = require('./send')
|
const SendTransactionScreen = require('./send')
|
||||||
const ConfirmTxScreen = require('./conf-tx')
|
const ConfirmTxScreen = require('./conf-tx')
|
||||||
// notice
|
// notice
|
||||||
@ -25,17 +26,13 @@ const AddTokenScreen = require('./add-token')
|
|||||||
const AddSuggestedTokenScreen = require('./add-suggested-token')
|
const AddSuggestedTokenScreen = require('./add-suggested-token')
|
||||||
const Import = require('./accounts/import')
|
const Import = require('./accounts/import')
|
||||||
const InfoScreen = require('./info')
|
const InfoScreen = require('./info')
|
||||||
|
const NewUiAnnouncement = require('./new-ui-annoucement')
|
||||||
|
const AppBar = require('./components/app-bar')
|
||||||
const Loading = require('./components/loading')
|
const Loading = require('./components/loading')
|
||||||
const SandwichExpando = require('sandwich-expando')
|
|
||||||
const Dropdown = require('./components/dropdown').Dropdown
|
|
||||||
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
|
|
||||||
const NetworkIndicator = require('./components/network')
|
|
||||||
const BuyView = require('./components/buy-button-subview')
|
const BuyView = require('./components/buy-button-subview')
|
||||||
const QrView = require('./components/qr-code')
|
|
||||||
const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
|
const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
|
||||||
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
|
||||||
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
|
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
|
||||||
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(App)
|
module.exports = connect(mapStateToProps)(App)
|
||||||
|
|
||||||
@ -88,13 +85,29 @@ function mapStateToProps (state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
App.prototype.render = function () {
|
App.prototype.render = function () {
|
||||||
var props = this.props
|
const {
|
||||||
const { isLoading, loadingMessage, transForward, network } = props
|
currentView,
|
||||||
const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
|
dispatch,
|
||||||
const loadMessage = loadingMessage || isLoadingNetwork ?
|
isLoading,
|
||||||
`Connecting to ${this.getNetworkName()}` : null
|
loadingMessage,
|
||||||
|
transForward,
|
||||||
|
network,
|
||||||
|
featureFlags,
|
||||||
|
} = this.props
|
||||||
|
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
|
||||||
|
const loadMessage = loadingMessage || isLoadingNetwork
|
||||||
|
? `Connecting to ${this.getNetworkName()}`
|
||||||
|
: null
|
||||||
log.debug('Main ui render function')
|
log.debug('Main ui render function')
|
||||||
|
|
||||||
|
if (!featureFlags.skipAnnounceBetaUI) {
|
||||||
|
return (
|
||||||
|
h(NewUiAnnouncement, {
|
||||||
|
dispatch,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('.flex-column.full-height', {
|
h('.flex-column.full-height', {
|
||||||
style: {
|
style: {
|
||||||
@ -104,12 +117,9 @@ App.prototype.render = function () {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
|
h(AppBar, {
|
||||||
// app bar
|
...this.props,
|
||||||
this.renderAppBar(),
|
}),
|
||||||
this.renderNetworkDropdown(),
|
|
||||||
this.renderDropdown(),
|
|
||||||
|
|
||||||
this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
|
this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
|
||||||
|
|
||||||
// panel content
|
// panel content
|
||||||
@ -123,299 +133,6 @@ App.prototype.render = function () {
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
App.prototype.renderAppBar = function () {
|
|
||||||
if (window.METAMASK_UI_TYPE === 'notification') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = this.props
|
|
||||||
const state = this.state || {}
|
|
||||||
const isNetworkMenuOpen = state.isNetworkMenuOpen || false
|
|
||||||
const {isMascara, isOnboarding} = props
|
|
||||||
|
|
||||||
// Do not render header if user is in mascara onboarding
|
|
||||||
if (isMascara && isOnboarding) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not render header if user is in mascara buy ether
|
|
||||||
if (isMascara && props.currentView.name === 'buyEth') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
h('.full-width', {
|
|
||||||
height: '38px',
|
|
||||||
}, [
|
|
||||||
|
|
||||||
h('.app-header.flex-row.flex-space-between', {
|
|
||||||
style: {
|
|
||||||
alignItems: 'center',
|
|
||||||
visibility: props.isUnlocked ? 'visible' : 'none',
|
|
||||||
background: props.isUnlocked ? 'white' : 'none',
|
|
||||||
height: '38px',
|
|
||||||
position: 'relative',
|
|
||||||
zIndex: 12,
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
|
|
||||||
h('div.left-menu-section', {
|
|
||||||
style: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
|
|
||||||
// mini logo
|
|
||||||
h('img', {
|
|
||||||
height: 24,
|
|
||||||
width: 24,
|
|
||||||
src: './images/icon-128.png',
|
|
||||||
}),
|
|
||||||
|
|
||||||
h(NetworkIndicator, {
|
|
||||||
network: this.props.network,
|
|
||||||
provider: this.props.provider,
|
|
||||||
onClick: (event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
props.isUnlocked && h('div', {
|
|
||||||
style: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
|
|
||||||
props.isUnlocked && h(AccountDropdowns, {
|
|
||||||
style: {},
|
|
||||||
enableAccountsSelector: true,
|
|
||||||
identities: this.props.identities,
|
|
||||||
selected: this.props.selectedAddress,
|
|
||||||
network: this.props.network,
|
|
||||||
keyrings: this.props.keyrings,
|
|
||||||
}, []),
|
|
||||||
|
|
||||||
// hamburger
|
|
||||||
props.isUnlocked && h(SandwichExpando, {
|
|
||||||
className: 'sandwich-expando',
|
|
||||||
width: 16,
|
|
||||||
barHeight: 2,
|
|
||||||
padding: 0,
|
|
||||||
isOpen: state.isMainMenuOpen,
|
|
||||||
color: 'rgb(247,146,30)',
|
|
||||||
onClick: () => {
|
|
||||||
this.setState({
|
|
||||||
isMainMenuOpen: !state.isMainMenuOpen,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
App.prototype.renderNetworkDropdown = function () {
|
|
||||||
const props = this.props
|
|
||||||
const { provider: { type: providerType, rpcTarget: activeNetwork } } = props
|
|
||||||
const rpcList = props.frequentRpcList
|
|
||||||
const state = this.state || {}
|
|
||||||
const isOpen = state.isNetworkMenuOpen
|
|
||||||
|
|
||||||
return h(Dropdown, {
|
|
||||||
useCssTransition: true,
|
|
||||||
isOpen,
|
|
||||||
onClickOutside: (event) => {
|
|
||||||
const { classList } = event.target
|
|
||||||
const isNotToggleElement = [
|
|
||||||
classList.contains('menu-icon'),
|
|
||||||
classList.contains('network-name'),
|
|
||||||
classList.contains('network-indicator'),
|
|
||||||
].filter(bool => bool).length === 0
|
|
||||||
// classes from three constituent nodes of the toggle element
|
|
||||||
|
|
||||||
if (isNotToggleElement) {
|
|
||||||
this.setState({ isNetworkMenuOpen: false })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
zIndex: 11,
|
|
||||||
style: {
|
|
||||||
position: 'absolute',
|
|
||||||
left: '2px',
|
|
||||||
top: '36px',
|
|
||||||
},
|
|
||||||
innerStyle: {
|
|
||||||
padding: '2px 16px 2px 0px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
|
|
||||||
h(
|
|
||||||
DropdownMenuItem,
|
|
||||||
{
|
|
||||||
key: 'main',
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
|
||||||
onClick: () => props.dispatch(actions.setProviderType('mainnet')),
|
|
||||||
style: {
|
|
||||||
fontSize: '18px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('.menu-icon.diamond'),
|
|
||||||
'Main Ethereum Network',
|
|
||||||
providerType === 'mainnet' ? h('.check', '✓') : null,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
|
|
||||||
h(
|
|
||||||
DropdownMenuItem,
|
|
||||||
{
|
|
||||||
key: 'ropsten',
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
|
||||||
onClick: () => props.dispatch(actions.setProviderType('ropsten')),
|
|
||||||
style: {
|
|
||||||
fontSize: '18px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('.menu-icon.red-dot'),
|
|
||||||
'Ropsten Test Network',
|
|
||||||
providerType === 'ropsten' ? h('.check', '✓') : null,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
|
|
||||||
h(
|
|
||||||
DropdownMenuItem,
|
|
||||||
{
|
|
||||||
key: 'kovan',
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
|
||||||
onClick: () => props.dispatch(actions.setProviderType('kovan')),
|
|
||||||
style: {
|
|
||||||
fontSize: '18px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('.menu-icon.hollow-diamond'),
|
|
||||||
'Kovan Test Network',
|
|
||||||
providerType === 'kovan' ? h('.check', '✓') : null,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
|
|
||||||
h(
|
|
||||||
DropdownMenuItem,
|
|
||||||
{
|
|
||||||
key: 'rinkeby',
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
|
||||||
onClick: () => props.dispatch(actions.setProviderType('rinkeby')),
|
|
||||||
style: {
|
|
||||||
fontSize: '18px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('.menu-icon.golden-square'),
|
|
||||||
'Rinkeby Test Network',
|
|
||||||
providerType === 'rinkeby' ? h('.check', '✓') : null,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
|
|
||||||
h(
|
|
||||||
DropdownMenuItem,
|
|
||||||
{
|
|
||||||
key: 'default',
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
|
||||||
onClick: () => props.dispatch(actions.setProviderType('localhost')),
|
|
||||||
style: {
|
|
||||||
fontSize: '18px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
|
|
||||||
'Localhost 8545',
|
|
||||||
activeNetwork === 'http://localhost:8545' ? h('.check', '✓') : null,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
|
|
||||||
this.renderCustomOption(props.provider),
|
|
||||||
this.renderCommonRpc(rpcList, props.provider),
|
|
||||||
|
|
||||||
h(
|
|
||||||
DropdownMenuItem,
|
|
||||||
{
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
|
||||||
onClick: () => this.props.dispatch(actions.showConfigPage()),
|
|
||||||
style: {
|
|
||||||
fontSize: '18px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
|
|
||||||
'Custom RPC',
|
|
||||||
activeNetwork === 'custom' ? h('.check', '✓') : null,
|
|
||||||
]
|
|
||||||
),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
App.prototype.renderDropdown = function () {
|
|
||||||
const state = this.state || {}
|
|
||||||
const isOpen = state.isMainMenuOpen
|
|
||||||
|
|
||||||
return h(Dropdown, {
|
|
||||||
useCssTransition: true,
|
|
||||||
isOpen: isOpen,
|
|
||||||
zIndex: 11,
|
|
||||||
onClickOutside: (event) => {
|
|
||||||
const classList = event.target.classList
|
|
||||||
const parentClassList = event.target.parentElement.classList
|
|
||||||
|
|
||||||
const isToggleElement = classList.contains('sandwich-expando') ||
|
|
||||||
parentClassList.contains('sandwich-expando')
|
|
||||||
|
|
||||||
if (isOpen && !isToggleElement) {
|
|
||||||
this.setState({ isMainMenuOpen: false })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
position: 'absolute',
|
|
||||||
right: '2px',
|
|
||||||
top: '38px',
|
|
||||||
},
|
|
||||||
innerStyle: {},
|
|
||||||
}, [
|
|
||||||
h(DropdownMenuItem, {
|
|
||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
|
||||||
onClick: () => { this.props.dispatch(actions.showConfigPage()) },
|
|
||||||
}, 'Settings'),
|
|
||||||
|
|
||||||
h(DropdownMenuItem, {
|
|
||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
|
||||||
onClick: () => { this.props.dispatch(actions.lockMetamask()) },
|
|
||||||
}, 'Log Out'),
|
|
||||||
|
|
||||||
h(DropdownMenuItem, {
|
|
||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
|
||||||
onClick: () => { this.props.dispatch(actions.showInfoPage()) },
|
|
||||||
}, 'Info/Help'),
|
|
||||||
|
|
||||||
h(DropdownMenuItem, {
|
|
||||||
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
|
||||||
onClick: () => {
|
|
||||||
this.props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
|
||||||
},
|
|
||||||
}, 'Try Beta!'),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork, loadMessage }) {
|
App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork, loadMessage }) {
|
||||||
const { isMascara } = this.props
|
const { isMascara } = this.props
|
||||||
|
|
||||||
@ -427,25 +144,6 @@ App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
App.prototype.renderBackButton = function (style, justArrow = false) {
|
|
||||||
var props = this.props
|
|
||||||
return (
|
|
||||||
h('.flex-row', {
|
|
||||||
key: 'leftArrow',
|
|
||||||
style: style,
|
|
||||||
onClick: () => props.dispatch(actions.goBackToInitView()),
|
|
||||||
}, [
|
|
||||||
h('i.fa.fa-arrow-left.cursor-pointer'),
|
|
||||||
justArrow ? null : h('div.cursor-pointer', {
|
|
||||||
style: {
|
|
||||||
marginLeft: '3px',
|
|
||||||
},
|
|
||||||
onClick: () => props.dispatch(actions.goBackToInitView()),
|
|
||||||
}, 'BACK'),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
App.prototype.renderPrimary = function () {
|
App.prototype.renderPrimary = function () {
|
||||||
log.debug('rendering primary')
|
log.debug('rendering primary')
|
||||||
var props = this.props
|
var props = this.props
|
||||||
@ -467,22 +165,6 @@ App.prototype.renderPrimary = function () {
|
|||||||
key: 'NoticeScreen',
|
key: 'NoticeScreen',
|
||||||
onConfirm: () => props.dispatch(actions.markNoticeRead(props.nextUnreadNotice)),
|
onConfirm: () => props.dispatch(actions.markNoticeRead(props.nextUnreadNotice)),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
!props.isInitialized && h('.flex-row.flex-center.flex-grow', [
|
|
||||||
h('p.pointer', {
|
|
||||||
onClick: () => {
|
|
||||||
global.platform.openExtensionInBrowser()
|
|
||||||
props.dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
fontSize: '0.8em',
|
|
||||||
color: '#aeaeae',
|
|
||||||
textDecoration: 'underline',
|
|
||||||
marginTop: '32px',
|
|
||||||
},
|
|
||||||
}, 'Try Beta Version'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
])
|
])
|
||||||
} else if (props.lostAccounts && props.lostAccounts.length > 0) {
|
} else if (props.lostAccounts && props.lostAccounts.length > 0) {
|
||||||
log.debug('rendering notice screen for lost accounts view.')
|
log.debug('rendering notice screen for lost accounts view.')
|
||||||
@ -586,31 +268,10 @@ App.prototype.renderPrimary = function () {
|
|||||||
|
|
||||||
case 'qr':
|
case 'qr':
|
||||||
log.debug('rendering show qr screen')
|
log.debug('rendering show qr screen')
|
||||||
return h('div', {
|
return h(AccountQrScreen, {
|
||||||
style: {
|
key: 'account-qr',
|
||||||
position: 'absolute',
|
selectedAddress: props.selectedAddress,
|
||||||
height: '100%',
|
})
|
||||||
top: '0px',
|
|
||||||
left: '0px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
|
|
||||||
onClick: () => props.dispatch(actions.backToAccountDetail(props.selectedAddress)),
|
|
||||||
style: {
|
|
||||||
marginLeft: '10px',
|
|
||||||
marginTop: '50px',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
position: 'absolute',
|
|
||||||
left: '44px',
|
|
||||||
width: '285px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h(QrView, {key: 'qr'}),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.debug('rendering default, account detail screen')
|
log.debug('rendering default, account detail screen')
|
||||||
@ -629,41 +290,6 @@ App.prototype.toggleMetamaskActive = function () {
|
|||||||
this.props.dispatch(actions.lockMetamask(false))
|
this.props.dispatch(actions.lockMetamask(false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
App.prototype.renderCustomOption = function (provider) {
|
|
||||||
const { rpcTarget, type } = provider
|
|
||||||
const props = this.props
|
|
||||||
|
|
||||||
if (type !== 'rpc') return null
|
|
||||||
|
|
||||||
// Concatenate long URLs
|
|
||||||
let label = rpcTarget
|
|
||||||
if (rpcTarget.length > 31) {
|
|
||||||
label = label.substr(0, 34) + '...'
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (rpcTarget) {
|
|
||||||
|
|
||||||
case 'http://localhost:8545':
|
|
||||||
return null
|
|
||||||
|
|
||||||
default:
|
|
||||||
return h(
|
|
||||||
DropdownMenuItem,
|
|
||||||
{
|
|
||||||
key: rpcTarget,
|
|
||||||
onClick: () => props.dispatch(actions.setRpcTarget(rpcTarget)),
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
|
|
||||||
label,
|
|
||||||
h('.check', '✓'),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
App.prototype.getNetworkName = function () {
|
App.prototype.getNetworkName = function () {
|
||||||
const { provider } = this.props
|
const { provider } = this.props
|
||||||
const providerName = provider.type
|
const providerName = provider.type
|
||||||
@ -684,28 +310,3 @@ App.prototype.getNetworkName = function () {
|
|||||||
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
App.prototype.renderCommonRpc = function (rpcList, provider) {
|
|
||||||
const props = this.props
|
|
||||||
const rpcTarget = provider.rpcTarget
|
|
||||||
|
|
||||||
return rpcList.map((rpc) => {
|
|
||||||
if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) {
|
|
||||||
return null
|
|
||||||
} else {
|
|
||||||
return h(
|
|
||||||
DropdownMenuItem,
|
|
||||||
{
|
|
||||||
key: `common${rpc}`,
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
|
||||||
onClick: () => props.dispatch(actions.setRpcTarget(rpc)),
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
|
|
||||||
rpc,
|
|
||||||
rpcTarget === rpc ? h('.check', '✓') : null,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
432
old-ui/app/components/app-bar.js
Normal file
432
old-ui/app/components/app-bar.js
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
const PropTypes = require('prop-types')
|
||||||
|
const {Component} = require('react')
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const actions = require('../../../ui/app/actions')
|
||||||
|
const SandwichExpando = require('sandwich-expando')
|
||||||
|
const {Dropdown} = require('./dropdown')
|
||||||
|
const {DropdownMenuItem} = require('./dropdown')
|
||||||
|
const NetworkIndicator = require('./network')
|
||||||
|
const {AccountDropdowns} = require('./account-dropdowns')
|
||||||
|
|
||||||
|
const LOCALHOST_RPC_URL = 'http://localhost:8545'
|
||||||
|
|
||||||
|
module.exports = class AppBar extends Component {
|
||||||
|
static defaultProps = {
|
||||||
|
selectedAddress: undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
frequentRpcList: PropTypes.array.isRequired,
|
||||||
|
isMascara: PropTypes.bool.isRequired,
|
||||||
|
isOnboarding: PropTypes.bool.isRequired,
|
||||||
|
identities: PropTypes.any.isRequired,
|
||||||
|
selectedAddress: PropTypes.string,
|
||||||
|
isUnlocked: PropTypes.bool.isRequired,
|
||||||
|
network: PropTypes.any.isRequired,
|
||||||
|
keyrings: PropTypes.any.isRequired,
|
||||||
|
provider: PropTypes.any.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
static renderSpace () {
|
||||||
|
return (
|
||||||
|
h('span', {
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: ' ',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
isNetworkMenuOpen: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAppBar () {
|
||||||
|
if (window.METAMASK_UI_TYPE === 'notification') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = this.props
|
||||||
|
const {isMascara, isOnboarding} = props
|
||||||
|
|
||||||
|
// Do not render header if user is in mascara onboarding
|
||||||
|
if (isMascara && isOnboarding) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not render header if user is in mascara buy ether
|
||||||
|
if (isMascara && props.currentView.name === 'buyEth') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('div.app-bar', [
|
||||||
|
this.renderAppBarNewUiNotice(),
|
||||||
|
this.renderAppBarAppHeader(),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAppBarNewUiNotice () {
|
||||||
|
const {dispatch} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('div.app-bar__new-ui-banner', {
|
||||||
|
style: {
|
||||||
|
height: '28px',
|
||||||
|
zIndex: 12,
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
'Try the New MetaMask',
|
||||||
|
AppBar.renderSpace(),
|
||||||
|
h('span.banner__link', {
|
||||||
|
async onClick () {
|
||||||
|
await dispatch(actions.setFeatureFlag('betaUI', true))
|
||||||
|
global.platform.openExtensionInBrowser()
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
'Now',
|
||||||
|
]),
|
||||||
|
AppBar.renderSpace(),
|
||||||
|
'or',
|
||||||
|
AppBar.renderSpace(),
|
||||||
|
h('span.banner__link', {
|
||||||
|
onClick () {
|
||||||
|
global.platform.openWindow({
|
||||||
|
url: 'https://medium.com/metamask/74dba32cc7f7',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
'Learn More',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAppBarAppHeader () {
|
||||||
|
const {
|
||||||
|
identities,
|
||||||
|
selectedAddress,
|
||||||
|
isUnlocked,
|
||||||
|
network,
|
||||||
|
keyrings,
|
||||||
|
provider,
|
||||||
|
} = this.props
|
||||||
|
const {
|
||||||
|
isNetworkMenuOpen,
|
||||||
|
isMainMenuOpen,
|
||||||
|
} = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('.full-width', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '38px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('.app-header.flex-row.flex-space-between', {
|
||||||
|
style: {
|
||||||
|
alignItems: 'center',
|
||||||
|
visibility: isUnlocked ? 'visible' : 'none',
|
||||||
|
background: isUnlocked ? 'white' : 'none',
|
||||||
|
height: '38px',
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 12,
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('div.left-menu-section', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
// mini logo
|
||||||
|
h('img', {
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
src: './images/icon-128.png',
|
||||||
|
}),
|
||||||
|
h(NetworkIndicator, {
|
||||||
|
network: network,
|
||||||
|
provider: provider,
|
||||||
|
onClick: (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
isUnlocked && h('div', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h(AccountDropdowns, {
|
||||||
|
style: {},
|
||||||
|
enableAccountsSelector: true,
|
||||||
|
identities: identities,
|
||||||
|
selected: selectedAddress,
|
||||||
|
network,
|
||||||
|
keyrings,
|
||||||
|
}, []),
|
||||||
|
h(SandwichExpando, {
|
||||||
|
className: 'sandwich-expando',
|
||||||
|
width: 16,
|
||||||
|
barHeight: 2,
|
||||||
|
padding: 0,
|
||||||
|
isOpen: isMainMenuOpen,
|
||||||
|
color: 'rgb(247,146,30)',
|
||||||
|
onClick: () => {
|
||||||
|
this.setState({
|
||||||
|
isMainMenuOpen: !isMainMenuOpen,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderNetworkDropdown () {
|
||||||
|
const {
|
||||||
|
dispatch,
|
||||||
|
frequentRpcList: rpcList,
|
||||||
|
provider,
|
||||||
|
} = this.props
|
||||||
|
const {
|
||||||
|
type: providerType,
|
||||||
|
rpcTarget: activeNetwork,
|
||||||
|
} = provider
|
||||||
|
const isOpen = this.state.isNetworkMenuOpen
|
||||||
|
|
||||||
|
return h(Dropdown, {
|
||||||
|
useCssTransition: true,
|
||||||
|
isOpen,
|
||||||
|
onClickOutside: (event) => {
|
||||||
|
const { classList } = event.target
|
||||||
|
const isNotToggleElement = [
|
||||||
|
classList.contains('menu-icon'),
|
||||||
|
classList.contains('network-name'),
|
||||||
|
classList.contains('network-indicator'),
|
||||||
|
].filter(bool => bool).length === 0
|
||||||
|
// classes from three constituent nodes of the toggle element
|
||||||
|
|
||||||
|
if (isNotToggleElement) {
|
||||||
|
this.setState({ isNetworkMenuOpen: false })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
zIndex: 11,
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
left: '2px',
|
||||||
|
top: '64px',
|
||||||
|
},
|
||||||
|
innerStyle: {
|
||||||
|
padding: '2px 16px 2px 0px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
key: 'main',
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
||||||
|
onClick: () => dispatch(actions.setProviderType('mainnet')),
|
||||||
|
style: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('.menu-icon.diamond'),
|
||||||
|
'Main Ethereum Network',
|
||||||
|
providerType === 'mainnet'
|
||||||
|
? h('.check', '✓')
|
||||||
|
: null,
|
||||||
|
]),
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
key: 'ropsten',
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
||||||
|
onClick: () => dispatch(actions.setProviderType('ropsten')),
|
||||||
|
style: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('.menu-icon.red-dot'),
|
||||||
|
'Ropsten Test Network',
|
||||||
|
providerType === 'ropsten'
|
||||||
|
? h('.check', '✓')
|
||||||
|
: null,
|
||||||
|
]),
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
key: 'kovan',
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
||||||
|
onClick: () => dispatch(actions.setProviderType('kovan')),
|
||||||
|
style: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('.menu-icon.hollow-diamond'),
|
||||||
|
'Kovan Test Network',
|
||||||
|
providerType === 'kovan'
|
||||||
|
? h('.check', '✓')
|
||||||
|
: null,
|
||||||
|
]),
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
key: 'rinkeby',
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
||||||
|
onClick: () => dispatch(actions.setProviderType('rinkeby')),
|
||||||
|
style: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('.menu-icon.golden-square'),
|
||||||
|
'Rinkeby Test Network',
|
||||||
|
providerType === 'rinkeby'
|
||||||
|
? h('.check', '✓')
|
||||||
|
: null,
|
||||||
|
]),
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
key: 'default',
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
||||||
|
onClick: () => dispatch(actions.setProviderType('localhost')),
|
||||||
|
style: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
|
||||||
|
'Localhost 8545',
|
||||||
|
activeNetwork === LOCALHOST_RPC_URL
|
||||||
|
? h('.check', '✓')
|
||||||
|
: null,
|
||||||
|
]),
|
||||||
|
|
||||||
|
this.renderCustomOption(provider),
|
||||||
|
this.renderCommonRpc(rpcList, provider),
|
||||||
|
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
|
||||||
|
onClick: () => dispatch(actions.showConfigPage()),
|
||||||
|
style: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
|
||||||
|
'Custom RPC',
|
||||||
|
activeNetwork === 'custom'
|
||||||
|
? h('.check', '✓')
|
||||||
|
: null,
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCustomOption ({ rpcTarget, type }) {
|
||||||
|
const {dispatch} = this.props
|
||||||
|
|
||||||
|
if (type !== 'rpc') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate long URLs
|
||||||
|
let label = rpcTarget
|
||||||
|
if (rpcTarget.length > 31) {
|
||||||
|
label = label.substr(0, 34) + '...'
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rpcTarget) {
|
||||||
|
case LOCALHOST_RPC_URL:
|
||||||
|
return null
|
||||||
|
default:
|
||||||
|
return h(DropdownMenuItem, {
|
||||||
|
key: rpcTarget,
|
||||||
|
onClick: () => dispatch(actions.setRpcTarget(rpcTarget)),
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
|
||||||
|
label,
|
||||||
|
h('.check', '✓'),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCommonRpc (rpcList, {rpcTarget}) {
|
||||||
|
const {dispatch} = this.props
|
||||||
|
|
||||||
|
return rpcList.map((rpc) => {
|
||||||
|
if ((rpc === LOCALHOST_RPC_URL) || (rpc === rpcTarget)) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
return h(DropdownMenuItem, {
|
||||||
|
key: `common${rpc}`,
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
|
onClick: () => dispatch(actions.setRpcTarget(rpc)),
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
|
||||||
|
rpc,
|
||||||
|
rpcTarget === rpc
|
||||||
|
? h('.check', '✓')
|
||||||
|
: null,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDropdown () {
|
||||||
|
const {dispatch} = this.props
|
||||||
|
const isOpen = this.state.isMainMenuOpen
|
||||||
|
|
||||||
|
return h(Dropdown, {
|
||||||
|
useCssTransition: true,
|
||||||
|
isOpen: isOpen,
|
||||||
|
zIndex: 11,
|
||||||
|
onClickOutside: (event) => {
|
||||||
|
const classList = event.target.classList
|
||||||
|
const parentClassList = event.target.parentElement.classList
|
||||||
|
|
||||||
|
const isToggleElement = classList.contains('sandwich-expando') ||
|
||||||
|
parentClassList.contains('sandwich-expando')
|
||||||
|
|
||||||
|
if (isOpen && !isToggleElement) {
|
||||||
|
this.setState({ isMainMenuOpen: false })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
right: '2px',
|
||||||
|
top: '66px',
|
||||||
|
},
|
||||||
|
innerStyle: {},
|
||||||
|
}, [
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
|
onClick: () => { dispatch(actions.showConfigPage()) },
|
||||||
|
}, 'Settings'),
|
||||||
|
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
|
onClick: () => { dispatch(actions.lockMetamask()) },
|
||||||
|
}, 'Log Out'),
|
||||||
|
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
|
onClick: () => { dispatch(actions.showInfoPage()) },
|
||||||
|
}, 'Info/Help'),
|
||||||
|
|
||||||
|
h(DropdownMenuItem, {
|
||||||
|
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
|
||||||
|
onClick: () => {
|
||||||
|
dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
|
||||||
|
},
|
||||||
|
}, 'Try Beta!'),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return h('div.full-width', [
|
||||||
|
this.renderAppBar(),
|
||||||
|
this.renderNetworkDropdown(),
|
||||||
|
this.renderDropdown(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
@ -1,79 +0,0 @@
|
|||||||
const Component = require('react').Component
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const qrCode = require('qrcode-npm').qrcode
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const isHexPrefixed = require('ethereumjs-util').isHexPrefixed
|
|
||||||
const CopyButton = require('./copyButton')
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(QrCodeView)
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
return {
|
|
||||||
Qr: state.appState.Qr,
|
|
||||||
buyView: state.appState.buyView,
|
|
||||||
warning: state.appState.warning,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(QrCodeView, Component)
|
|
||||||
|
|
||||||
function QrCodeView () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
QrCodeView.prototype.render = function () {
|
|
||||||
const props = this.props
|
|
||||||
const Qr = props.Qr
|
|
||||||
const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}`
|
|
||||||
const qrImage = qrCode(4, 'M')
|
|
||||||
qrImage.addData(address)
|
|
||||||
qrImage.make()
|
|
||||||
return h('.main-container.flex-column', {
|
|
||||||
key: 'qr',
|
|
||||||
style: {
|
|
||||||
justifyContent: 'center',
|
|
||||||
paddingBottom: '45px',
|
|
||||||
paddingLeft: '45px',
|
|
||||||
paddingRight: '45px',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('.qr-header', Qr.message),
|
|
||||||
|
|
||||||
this.props.warning ? this.props.warning && h('span.error.flex-center', {
|
|
||||||
style: {
|
|
||||||
textAlign: 'center',
|
|
||||||
width: '229px',
|
|
||||||
height: '82px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
this.props.warning) : null,
|
|
||||||
|
|
||||||
h('#qr-container.flex-column', {
|
|
||||||
style: {
|
|
||||||
marginTop: '25px',
|
|
||||||
marginBottom: '15px',
|
|
||||||
},
|
|
||||||
dangerouslySetInnerHTML: {
|
|
||||||
__html: qrImage.createTableTag(4),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
h('.flex-row', [
|
|
||||||
h('h3.ellip-address', {
|
|
||||||
style: {
|
|
||||||
width: '247px',
|
|
||||||
},
|
|
||||||
}, Qr.data),
|
|
||||||
h(CopyButton, {
|
|
||||||
value: Qr.data,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
QrCodeView.prototype.renderMultiMessage = function () {
|
|
||||||
var Qr = this.props.Qr
|
|
||||||
var multiMessage = Qr.message.map((message) => h('.qr-message', message))
|
|
||||||
return multiMessage
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ const h = require('react-hyperscript')
|
|||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('../../../ui/app/actions')
|
const actions = require('../../../ui/app/actions')
|
||||||
const Qr = require('./qr-code')
|
|
||||||
const isValidAddress = require('../util').isValidAddress
|
const isValidAddress = require('../util').isValidAddress
|
||||||
module.exports = connect(mapStateToProps)(ShapeshiftForm)
|
module.exports = connect(mapStateToProps)(ShapeshiftForm)
|
||||||
|
|
||||||
@ -11,7 +10,6 @@ function mapStateToProps (state) {
|
|||||||
return {
|
return {
|
||||||
warning: state.appState.warning,
|
warning: state.appState.warning,
|
||||||
isSubLoading: state.appState.isSubLoading,
|
isSubLoading: state.appState.isSubLoading,
|
||||||
qrRequested: state.appState.qrRequested,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,7 +21,7 @@ function ShapeshiftForm () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShapeshiftForm.prototype.render = function () {
|
ShapeshiftForm.prototype.render = function () {
|
||||||
return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain()
|
return this.renderMain()
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeshiftForm.prototype.renderMain = function () {
|
ShapeshiftForm.prototype.renderMain = function () {
|
||||||
|
@ -36,7 +36,7 @@ TransactionListItem.prototype.showRetryButton = function () {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentTxIsLatest = false
|
let currentTxSharesEarliestNonce = false
|
||||||
const currentNonce = txParams.nonce
|
const currentNonce = txParams.nonce
|
||||||
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
|
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
|
||||||
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
||||||
@ -45,14 +45,14 @@ TransactionListItem.prototype.showRetryButton = function () {
|
|||||||
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
||||||
lastSubmittedTxWithCurrentNonce.id === transaction.id
|
lastSubmittedTxWithCurrentNonce.id === transaction.id
|
||||||
if (currentSubmittedTxs.length > 0) {
|
if (currentSubmittedTxs.length > 0) {
|
||||||
const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
|
const earliestSubmitted = currentSubmittedTxs.reduce((tx1, tx2) => {
|
||||||
if (tx1.submittedTime < tx2.submittedTime) return tx1
|
if (tx1.submittedTime < tx2.submittedTime) return tx1
|
||||||
return tx2
|
return tx2
|
||||||
})
|
})
|
||||||
currentTxIsLatest = lastTx.id === transaction.id
|
currentTxSharesEarliestNonce = currentNonce === earliestSubmitted.txParams.nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000 && currentTxIsLatest
|
return currentTxSharesEarliestNonce && currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionListItem.prototype.render = function () {
|
TransactionListItem.prototype.render = function () {
|
||||||
|
@ -720,7 +720,131 @@ div.message-container > div:first-child {
|
|||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Notification Modal
|
.new-ui-announcement {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
background: white;
|
||||||
|
color: #4D4D4D;
|
||||||
|
font-family: Roboto, Arial, sans-serif;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__announcement-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__announcement-header a.close {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__announcement-header a.close:hover {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__announcement-header h1 {
|
||||||
|
color: #33A4E7;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__body {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 10.5pt;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__body h1 {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__body a {
|
||||||
|
color: #33A4E7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__body .updates-list {
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__body .updates-list h2 {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__body .updates-list ul {
|
||||||
|
list-style: disc inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__footer h1 {
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__footer button:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__footer button.positive {
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1rem;
|
||||||
|
background: #33A4E7;
|
||||||
|
color: white;
|
||||||
|
text-transform: uppercase;
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 400;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-ui-announcement__footer button.negative {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: white;
|
||||||
|
color: #33A4E7;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 400;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-bar {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-bar__new-ui-banner {
|
||||||
|
background: #33A4E7;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
padding: 8px;
|
||||||
|
font-family: Roboto, Arial, sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner__link {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
.notification-modal-wrapper {
|
.notification-modal-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
85
old-ui/app/new-ui-annoucement.js
Normal file
85
old-ui/app/new-ui-annoucement.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
const PropTypes = require('prop-types')
|
||||||
|
const {PureComponent} = require('react')
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const actions = require('../../ui/app/actions')
|
||||||
|
|
||||||
|
module.exports = class NewUiAnnouncement extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
close = async () => {
|
||||||
|
await this.props.dispatch(actions.setFeatureFlag('skipAnnounceBetaUI', true))
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToNewUi = async () => {
|
||||||
|
const flag = 'betaUI'
|
||||||
|
const enabled = true
|
||||||
|
await this.props.dispatch(actions.setFeatureFlag(
|
||||||
|
flag,
|
||||||
|
enabled,
|
||||||
|
))
|
||||||
|
await this.close()
|
||||||
|
global.platform.openExtensionInBrowser()
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
h('div.new-ui-announcement', [
|
||||||
|
h('section.new-ui-announcement__announcement-header', [
|
||||||
|
h('h1', 'Announcement'),
|
||||||
|
h('a.close', {
|
||||||
|
onClick: this.close,
|
||||||
|
}, '×'),
|
||||||
|
]),
|
||||||
|
h('section.new-ui-announcement__body', [
|
||||||
|
h('h1', 'A New Version of MetaMask'),
|
||||||
|
h('p', [
|
||||||
|
"We're excited to announce a brand-new version of MetaMask with enhanced features and functionality.",
|
||||||
|
]),
|
||||||
|
h('div.updates-list', [
|
||||||
|
h('h2', 'Updates include'),
|
||||||
|
h('ul', [
|
||||||
|
h('li', 'New user interface'),
|
||||||
|
h('li', 'Full-screen mode'),
|
||||||
|
h('li', 'Better token support'),
|
||||||
|
h('li', 'Better gas controls'),
|
||||||
|
h('li', 'Advanced features for developers'),
|
||||||
|
h('li', 'New confirmation screens'),
|
||||||
|
h('li', 'And more!'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
h('p', [
|
||||||
|
'You can still use the current version of MetaMask. The new version is still in beta, ' +
|
||||||
|
'however we encourage you to try it out as we transition into this exciting new update.',
|
||||||
|
h('span', {
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: ' ',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
h('a', {
|
||||||
|
href: 'https://medium.com/metamask/74dba32cc7f7',
|
||||||
|
onClick ({target}) {
|
||||||
|
const url = target.href
|
||||||
|
global.platform.openWindow({
|
||||||
|
url,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
'Learn more.',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
h('section.new-ui-announcement__footer', [
|
||||||
|
h('h1', 'Ready to try the new MetaMask?'),
|
||||||
|
h('button.positive', {
|
||||||
|
onClick: this.switchToNewUi,
|
||||||
|
}, 'Try it now'),
|
||||||
|
h('button.negative', {
|
||||||
|
onClick: this.close,
|
||||||
|
}, 'No thanks, maybe later'),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
663
package-lock.json
generated
663
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -198,7 +198,6 @@
|
|||||||
"semaphore": "^1.0.5",
|
"semaphore": "^1.0.5",
|
||||||
"semver": "^5.4.1",
|
"semver": "^5.4.1",
|
||||||
"shallow-copy": "0.0.1",
|
"shallow-copy": "0.0.1",
|
||||||
"superstatic": "^5.0.2",
|
|
||||||
"sw-controller": "^1.0.3",
|
"sw-controller": "^1.0.3",
|
||||||
"sw-stream": "^2.0.2",
|
"sw-stream": "^2.0.2",
|
||||||
"textarea-caret": "^3.0.1",
|
"textarea-caret": "^3.0.1",
|
||||||
@ -228,7 +227,7 @@
|
|||||||
"brfs": "^1.6.1",
|
"brfs": "^1.6.1",
|
||||||
"browserify": "^16.1.1",
|
"browserify": "^16.1.1",
|
||||||
"chai": "^4.1.0",
|
"chai": "^4.1.0",
|
||||||
"chromedriver": "2.36.0",
|
"chromedriver": "^2.41.0",
|
||||||
"clipboardy": "^1.2.3",
|
"clipboardy": "^1.2.3",
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
"coveralls": "^3.0.0",
|
"coveralls": "^3.0.0",
|
||||||
@ -308,6 +307,7 @@
|
|||||||
"shell-parallel": "^1.0.3",
|
"shell-parallel": "^1.0.3",
|
||||||
"sinon": "^5.0.0",
|
"sinon": "^5.0.0",
|
||||||
"source-map": "^0.7.2",
|
"source-map": "^0.7.2",
|
||||||
|
"static-server": "^2.2.1",
|
||||||
"style-loader": "^0.21.0",
|
"style-loader": "^0.21.0",
|
||||||
"stylelint-config-standard": "^18.2.0",
|
"stylelint-config-standard": "^18.2.0",
|
||||||
"tape": "^4.5.1",
|
"tape": "^4.5.1",
|
||||||
|
@ -50,15 +50,20 @@ deployButton.addEventListener('click', async function (event) {
|
|||||||
|
|
||||||
console.log(`contract`, contract)
|
console.log(`contract`, contract)
|
||||||
|
|
||||||
|
document.getElementById('contractStatus').innerHTML = 'Deployed'
|
||||||
|
|
||||||
depositButton.addEventListener('click', function (event) {
|
depositButton.addEventListener('click', function (event) {
|
||||||
|
document.getElementById('contractStatus').innerHTML = 'Deposit initiated'
|
||||||
contract.deposit({ from: web3.eth.accounts[0], value: '0x3782dace9d900000' }, function (result) {
|
contract.deposit({ from: web3.eth.accounts[0], value: '0x3782dace9d900000' }, function (result) {
|
||||||
console.log(result)
|
console.log(result)
|
||||||
|
document.getElementById('contractStatus').innerHTML = 'Deposit completed'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
withdrawButton.addEventListener('click', function (event) {
|
withdrawButton.addEventListener('click', function (event) {
|
||||||
contract.withdraw('0xde0b6b3a7640000', { from: web3.eth.accounts[0] }, function (result) {
|
contract.withdraw('0xde0b6b3a7640000', { from: web3.eth.accounts[0] }, function (result) {
|
||||||
console.log(result)
|
console.log(result)
|
||||||
|
document.getElementById('contractStatus').innerHTML = 'Withdrawn'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
<button id="depositButton">Deposit</button>
|
<button id="depositButton">Deposit</button>
|
||||||
<button id="withdrawButton">Withdraw</button>
|
<button id="withdrawButton">Withdraw</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="contractStatus" style="display: flex; font-size: 1rem;">
|
||||||
|
Not yet deployed
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; flex-flow: column;">
|
<div style="display: flex; flex-flow: column;">
|
||||||
<div style="display: flex; font-size: 1.25rem;">Send eth</div>
|
<div style="display: flex; font-size: 1.25rem;">Send eth</div>
|
||||||
|
@ -12,9 +12,11 @@ const {
|
|||||||
} = require('../func')
|
} = require('../func')
|
||||||
const {
|
const {
|
||||||
checkBrowserForConsoleErrors,
|
checkBrowserForConsoleErrors,
|
||||||
|
closeAllWindowHandlesExcept,
|
||||||
verboseReportOnFailure,
|
verboseReportOnFailure,
|
||||||
findElement,
|
findElement,
|
||||||
findElements,
|
findElements,
|
||||||
|
loadExtension,
|
||||||
} = require('./helpers')
|
} = require('./helpers')
|
||||||
|
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ describe('Using MetaMask with an existing account', function () {
|
|||||||
const testSeedPhrase = 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'
|
const testSeedPhrase = 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent'
|
||||||
const testAddress = '0xE18035BF8712672935FDB4e5e431b1a0183d2DFC'
|
const testAddress = '0xE18035BF8712672935FDB4e5e431b1a0183d2DFC'
|
||||||
const testPrivateKey2 = '14abe6f4aab7f9f626fe981c864d0adeb5685f289ac9270c27b8fd790b4235d6'
|
const testPrivateKey2 = '14abe6f4aab7f9f626fe981c864d0adeb5685f289ac9270c27b8fd790b4235d6'
|
||||||
|
const tinyDelayMs = 500
|
||||||
const regularDelayMs = 1000
|
const regularDelayMs = 1000
|
||||||
const largeDelayMs = regularDelayMs * 2
|
const largeDelayMs = regularDelayMs * 2
|
||||||
|
|
||||||
@ -74,37 +77,51 @@ describe('Using MetaMask with an existing account', function () {
|
|||||||
|
|
||||||
describe('New UI setup', async function () {
|
describe('New UI setup', async function () {
|
||||||
it('switches to first tab', async function () {
|
it('switches to first tab', async function () {
|
||||||
|
await delay(tinyDelayMs)
|
||||||
const [firstTab] = await driver.getAllWindowHandles()
|
const [firstTab] = await driver.getAllWindowHandles()
|
||||||
await driver.switchTo().window(firstTab)
|
await driver.switchTo().window(firstTab)
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('use the local network', async function () {
|
|
||||||
const networkSelector = await findElement(driver, By.css('#network_component'))
|
|
||||||
await networkSelector.click()
|
|
||||||
await delay(regularDelayMs)
|
|
||||||
|
|
||||||
const [localhost] = await findElements(driver, By.xpath(`//li[contains(text(), 'Localhost')]`))
|
|
||||||
await localhost.click()
|
|
||||||
await delay(regularDelayMs)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('selects the new UI option', async () => {
|
it('selects the new UI option', async () => {
|
||||||
const button = await findElement(driver, By.xpath("//p[contains(text(), 'Try Beta Version')]"))
|
try {
|
||||||
|
const overlay = await findElement(driver, By.css('.full-flex-height'))
|
||||||
|
await driver.wait(until.stalenessOf(overlay))
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]"))
|
||||||
await button.click()
|
await button.click()
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
// Close all other tabs
|
// Close all other tabs
|
||||||
const [oldUi, infoPage, newUi] = await driver.getAllWindowHandles()
|
const [tab0, tab1, tab2] = await driver.getAllWindowHandles()
|
||||||
|
await driver.switchTo().window(tab0)
|
||||||
|
await delay(tinyDelayMs)
|
||||||
|
|
||||||
const newUiOrInfoPage = newUi || infoPage
|
let selectedUrl = await driver.getCurrentUrl()
|
||||||
await driver.switchTo().window(oldUi)
|
await delay(tinyDelayMs)
|
||||||
await driver.close()
|
if (tab0 && selectedUrl.match(/popup.html/)) {
|
||||||
if (infoPage !== newUiOrInfoPage) {
|
await closeAllWindowHandlesExcept(driver, tab0)
|
||||||
await driver.switchTo().window(infoPage)
|
} else if (tab1) {
|
||||||
await driver.close()
|
await driver.switchTo().window(tab1)
|
||||||
|
selectedUrl = await driver.getCurrentUrl()
|
||||||
|
await delay(tinyDelayMs)
|
||||||
|
if (selectedUrl.match(/popup.html/)) {
|
||||||
|
await closeAllWindowHandlesExcept(driver, tab1)
|
||||||
|
} else if (tab2) {
|
||||||
|
await driver.switchTo().window(tab2)
|
||||||
|
selectedUrl = await driver.getCurrentUrl()
|
||||||
|
selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('popup.html not found')
|
||||||
}
|
}
|
||||||
await driver.switchTo().window(newUiOrInfoPage)
|
await delay(regularDelayMs)
|
||||||
|
const [appTab] = await driver.getAllWindowHandles()
|
||||||
|
await driver.switchTo().window(appTab)
|
||||||
|
await delay(tinyDelayMs)
|
||||||
|
|
||||||
|
await loadExtension(driver, extensionId)
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
|
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
|
||||||
@ -208,6 +225,16 @@ describe('Using MetaMask with an existing account', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('Add an account', () => {
|
describe('Add an account', () => {
|
||||||
|
it('switches to localhost', async () => {
|
||||||
|
const networkDropdown = await findElement(driver, By.css('.network-name'))
|
||||||
|
await networkDropdown.click()
|
||||||
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
|
const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`))
|
||||||
|
await localhost.click()
|
||||||
|
await delay(largeDelayMs)
|
||||||
|
})
|
||||||
|
|
||||||
it('choose Create Account from the account menu', async () => {
|
it('choose Create Account from the account menu', async () => {
|
||||||
await driver.findElement(By.css('.account-menu__icon')).click()
|
await driver.findElement(By.css('.account-menu__icon')).click()
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
@ -75,30 +75,11 @@ describe('MetaMask', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('New UI setup', async function () {
|
describe('New UI setup', async function () {
|
||||||
let networkSelector
|
|
||||||
it('switches to first tab', async function () {
|
it('switches to first tab', async function () {
|
||||||
|
await delay(tinyDelayMs)
|
||||||
const [firstTab] = await driver.getAllWindowHandles()
|
const [firstTab] = await driver.getAllWindowHandles()
|
||||||
await driver.switchTo().window(firstTab)
|
await driver.switchTo().window(firstTab)
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
try {
|
|
||||||
networkSelector = await findElement(driver, By.css('#network_component'))
|
|
||||||
} catch (e) {
|
|
||||||
await loadExtension(driver, extensionId)
|
|
||||||
await delay(largeDelayMs * 2)
|
|
||||||
networkSelector = await findElement(driver, By.css('#network_component'))
|
|
||||||
}
|
|
||||||
await delay(regularDelayMs)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('uses the local network', async function () {
|
|
||||||
await networkSelector.click()
|
|
||||||
await delay(regularDelayMs)
|
|
||||||
|
|
||||||
const networks = await findElements(driver, By.css('.dropdown-menu-item'))
|
|
||||||
const localhost = networks[4]
|
|
||||||
await driver.wait(until.elementTextMatches(localhost, /Localhost/))
|
|
||||||
await localhost.click()
|
|
||||||
await delay(regularDelayMs)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('selects the new UI option', async () => {
|
it('selects the new UI option', async () => {
|
||||||
@ -107,27 +88,40 @@ describe('MetaMask', function () {
|
|||||||
await driver.wait(until.stalenessOf(overlay))
|
await driver.wait(until.stalenessOf(overlay))
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
const button = await findElement(driver, By.xpath("//p[contains(text(), 'Try Beta Version')]"))
|
const button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]"))
|
||||||
await button.click()
|
await button.click()
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
// Close all other tabs
|
// Close all other tabs
|
||||||
const [oldUi, tab1, tab2] = await driver.getAllWindowHandles()
|
const [tab0, tab1, tab2] = await driver.getAllWindowHandles()
|
||||||
await driver.switchTo().window(oldUi)
|
await driver.switchTo().window(tab0)
|
||||||
await driver.close()
|
await delay(tinyDelayMs)
|
||||||
|
|
||||||
await driver.switchTo().window(tab1)
|
let selectedUrl = await driver.getCurrentUrl()
|
||||||
const tab1Url = await driver.getCurrentUrl()
|
await delay(tinyDelayMs)
|
||||||
if (tab1Url.match(/metamask.io/)) {
|
if (tab0 && selectedUrl.match(/popup.html/)) {
|
||||||
await driver.switchTo().window(tab1)
|
await closeAllWindowHandlesExcept(driver, tab0)
|
||||||
await driver.close()
|
} else if (tab1) {
|
||||||
await driver.switchTo().window(tab2)
|
|
||||||
} else if (tab2) {
|
|
||||||
await driver.switchTo().window(tab2)
|
|
||||||
await driver.close()
|
|
||||||
await driver.switchTo().window(tab1)
|
await driver.switchTo().window(tab1)
|
||||||
|
selectedUrl = await driver.getCurrentUrl()
|
||||||
|
await delay(tinyDelayMs)
|
||||||
|
if (selectedUrl.match(/popup.html/)) {
|
||||||
|
await closeAllWindowHandlesExcept(driver, tab1)
|
||||||
|
} else if (tab2) {
|
||||||
|
await driver.switchTo().window(tab2)
|
||||||
|
selectedUrl = await driver.getCurrentUrl()
|
||||||
|
selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('popup.html not found')
|
||||||
}
|
}
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
const [appTab] = await driver.getAllWindowHandles()
|
||||||
|
await driver.switchTo().window(appTab)
|
||||||
|
await delay(tinyDelayMs)
|
||||||
|
|
||||||
|
await loadExtension(driver, extensionId)
|
||||||
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
|
const continueBtn = await findElement(driver, By.css('.welcome-screen__button'))
|
||||||
await continueBtn.click()
|
await continueBtn.click()
|
||||||
@ -201,7 +195,16 @@ describe('MetaMask', function () {
|
|||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function retypeSeedPhrase (words, wasReloaded) {
|
async function clickWordAndWait (word) {
|
||||||
|
const xpathClass = 'backup-phrase__confirm-seed-option backup-phrase__confirm-seed-option--unselected'
|
||||||
|
const xpath = `//button[@class='${xpathClass}' and contains(text(), '${word}')]`
|
||||||
|
const word0 = await findElement(driver, By.xpath(xpath), 10000)
|
||||||
|
|
||||||
|
await word0.click()
|
||||||
|
await delay(tinyDelayMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retypeSeedPhrase (words, wasReloaded, count = 0) {
|
||||||
try {
|
try {
|
||||||
if (wasReloaded) {
|
if (wasReloaded) {
|
||||||
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button')
|
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button')
|
||||||
@ -215,67 +218,26 @@ describe('MetaMask', function () {
|
|||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
const word0 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[0]}')]`), 10000)
|
await clickWordAndWait(words[0])
|
||||||
|
await clickWordAndWait(words[1])
|
||||||
|
await clickWordAndWait(words[2])
|
||||||
|
await clickWordAndWait(words[3])
|
||||||
|
await clickWordAndWait(words[4])
|
||||||
|
await clickWordAndWait(words[5])
|
||||||
|
await clickWordAndWait(words[6])
|
||||||
|
await clickWordAndWait(words[7])
|
||||||
|
await clickWordAndWait(words[8])
|
||||||
|
await clickWordAndWait(words[9])
|
||||||
|
await clickWordAndWait(words[10])
|
||||||
|
await clickWordAndWait(words[11])
|
||||||
|
|
||||||
await word0.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word1 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[1]}')]`), 10000)
|
|
||||||
|
|
||||||
await word1.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word2 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[2]}')]`), 10000)
|
|
||||||
|
|
||||||
await word2.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word3 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[3]}')]`), 10000)
|
|
||||||
|
|
||||||
await word3.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word4 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[4]}')]`), 10000)
|
|
||||||
|
|
||||||
await word4.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word5 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[5]}')]`), 10000)
|
|
||||||
|
|
||||||
await word5.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word6 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[6]}')]`), 10000)
|
|
||||||
|
|
||||||
await word6.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word7 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[7]}')]`), 10000)
|
|
||||||
|
|
||||||
await word7.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word8 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[8]}')]`), 10000)
|
|
||||||
|
|
||||||
await word8.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word9 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[9]}')]`), 10000)
|
|
||||||
|
|
||||||
await word9.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word10 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[10]}')]`), 10000)
|
|
||||||
|
|
||||||
await word10.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
|
|
||||||
const word11 = await findElement(driver, By.xpath(`//button[contains(text(), '${words[11]}')]`), 10000)
|
|
||||||
await word11.click()
|
|
||||||
await delay(tinyDelayMs)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await loadExtension(driver, extensionId)
|
if (count > 2) {
|
||||||
await retypeSeedPhrase(words, true)
|
throw e
|
||||||
|
} else {
|
||||||
|
await loadExtension(driver, extensionId)
|
||||||
|
await retypeSeedPhrase(words, true, count + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,6 +351,16 @@ describe('MetaMask', function () {
|
|||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('switches to localhost', async () => {
|
||||||
|
const networkDropdown = await findElement(driver, By.css('.network-name'))
|
||||||
|
await networkDropdown.click()
|
||||||
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
|
const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`))
|
||||||
|
await localhost.click()
|
||||||
|
await delay(largeDelayMs * 2)
|
||||||
|
})
|
||||||
|
|
||||||
it('balance renders', async () => {
|
it('balance renders', async () => {
|
||||||
const balance = await findElement(driver, By.css('.balance-display .token-amount'))
|
const balance = await findElement(driver, By.css('.balance-display .token-amount'))
|
||||||
await driver.wait(until.elementTextMatches(balance, /100.+ETH/))
|
await driver.wait(until.elementTextMatches(balance, /100.+ETH/))
|
||||||
@ -512,7 +484,7 @@ describe('MetaMask', function () {
|
|||||||
|
|
||||||
it('displays the contract creation data', async () => {
|
it('displays the contract creation data', async () => {
|
||||||
const dataTab = await findElement(driver, By.xpath(`//li[contains(text(), 'Data')]`))
|
const dataTab = await findElement(driver, By.xpath(`//li[contains(text(), 'Data')]`))
|
||||||
dataTab.click()
|
await dataTab.click()
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
await findElement(driver, By.xpath(`//div[contains(text(), '127.0.0.1')]`))
|
await findElement(driver, By.xpath(`//div[contains(text(), '127.0.0.1')]`))
|
||||||
@ -522,7 +494,7 @@ describe('MetaMask', function () {
|
|||||||
assert.equal(confirmDataText.match(/0x608060405234801561001057600080fd5b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff/))
|
assert.equal(confirmDataText.match(/0x608060405234801561001057600080fd5b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff/))
|
||||||
|
|
||||||
const detailsTab = await findElement(driver, By.xpath(`//li[contains(text(), 'Details')]`))
|
const detailsTab = await findElement(driver, By.xpath(`//li[contains(text(), 'Details')]`))
|
||||||
detailsTab.click()
|
await detailsTab.click()
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -543,9 +515,15 @@ describe('MetaMask', function () {
|
|||||||
await driver.switchTo().window(dapp)
|
await driver.switchTo().window(dapp)
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
|
let contractStatus = await driver.findElement(By.css('#contractStatus'))
|
||||||
|
await driver.wait(until.elementTextMatches(contractStatus, /Deployed/))
|
||||||
|
|
||||||
const depositButton = await findElement(driver, By.css('#depositButton'))
|
const depositButton = await findElement(driver, By.css('#depositButton'))
|
||||||
await depositButton.click()
|
await depositButton.click()
|
||||||
await delay(regularDelayMs)
|
await delay(largeDelayMs)
|
||||||
|
|
||||||
|
contractStatus = await driver.findElement(By.css('#contractStatus'))
|
||||||
|
await driver.wait(until.elementTextMatches(contractStatus, /Deposit\sinitiated/))
|
||||||
|
|
||||||
await driver.switchTo().window(extension)
|
await driver.switchTo().window(extension)
|
||||||
await delay(largeDelayMs)
|
await delay(largeDelayMs)
|
||||||
|
@ -6,5 +6,5 @@ set -o pipefail
|
|||||||
|
|
||||||
export PATH="$PATH:./node_modules/.bin"
|
export PATH="$PATH:./node_modules/.bin"
|
||||||
|
|
||||||
shell-parallel -s 'npm run ganache:start' -x 'sleep 5 && superstatic test/e2e/beta/contract-test/ --port 8080 --host 127.0.0.1' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
|
shell-parallel -s 'npm run ganache:start' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
|
||||||
shell-parallel -s 'npm run ganache:start -- -d' -x 'sleep 5 && superstatic test/e2e/beta/contract-test/ --port 8080 --host 127.0.0.1' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
|
shell-parallel -s 'npm run ganache:start -- -d' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
|
||||||
|
@ -59,6 +59,13 @@ describe('Metamask popup page', function () {
|
|||||||
await driver.switchTo().window(windowHandles[0])
|
await driver.switchTo().window(windowHandles[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('does not select the new UI option', async () => {
|
||||||
|
await delay(300)
|
||||||
|
const button = await driver.findElement(By.xpath("//button[contains(text(), 'No thanks, maybe later')]"))
|
||||||
|
await button.click()
|
||||||
|
await delay(1000)
|
||||||
|
})
|
||||||
|
|
||||||
it('sets provider type to localhost', async function () {
|
it('sets provider type to localhost', async function () {
|
||||||
await delay(300)
|
await delay(300)
|
||||||
await setProviderType('localhost')
|
await setProviderType('localhost')
|
||||||
@ -133,9 +140,9 @@ describe('Metamask popup page', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('adds a second account', async function () {
|
it('adds a second account', async function () {
|
||||||
await driver.findElement(By.css('#app-content > div > div.full-width > div > div:nth-child(2) > span > div')).click()
|
await driver.findElement(By.css('div.full-width > div > div:nth-child(2) > span > div')).click()
|
||||||
await delay(300)
|
await delay(300)
|
||||||
await driver.findElement(By.css('#app-content > div > div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(3) > span')).click()
|
await driver.findElement(By.css('div.full-width > div > div:nth-child(2) > span > div > div > span > div > li:nth-child(3) > span')).click()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('shows account address', async function () {
|
it('shows account address', async function () {
|
||||||
@ -146,7 +153,7 @@ describe('Metamask popup page', function () {
|
|||||||
it('logs out of the vault', async () => {
|
it('logs out of the vault', async () => {
|
||||||
await driver.findElement(By.css('.sandwich-expando')).click()
|
await driver.findElement(By.css('.sandwich-expando')).click()
|
||||||
await delay(500)
|
await delay(500)
|
||||||
const logoutButton = await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)'))
|
const logoutButton = await driver.findElement(By.css('.menu-droppo > li:nth-child(3)'))
|
||||||
assert.equal(await logoutButton.getText(), 'Log Out')
|
assert.equal(await logoutButton.getText(), 'Log Out')
|
||||||
await logoutButton.click()
|
await logoutButton.click()
|
||||||
})
|
})
|
||||||
@ -178,7 +185,7 @@ describe('Metamask popup page', function () {
|
|||||||
it('logs out', async function () {
|
it('logs out', async function () {
|
||||||
await driver.findElement(By.css('.sandwich-expando')).click()
|
await driver.findElement(By.css('.sandwich-expando')).click()
|
||||||
await delay(200)
|
await delay(200)
|
||||||
const logOut = await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)'))
|
const logOut = await driver.findElement(By.css('.menu-droppo > li:nth-child(3)'))
|
||||||
assert.equal(await logOut.getText(), 'Log Out')
|
assert.equal(await logOut.getText(), 'Log Out')
|
||||||
await logOut.click()
|
await logOut.click()
|
||||||
await delay(300)
|
await delay(300)
|
||||||
|
@ -27,6 +27,11 @@ async function runFirstTimeUsageTest(assert, done) {
|
|||||||
|
|
||||||
const app = $('#app-content')
|
const app = $('#app-content')
|
||||||
|
|
||||||
|
// Selects new ui
|
||||||
|
const tryNewUIButton = (await findAsync(app, 'button.negative'))[0]
|
||||||
|
tryNewUIButton.click()
|
||||||
|
await timeout()
|
||||||
|
|
||||||
// recurse notices
|
// recurse notices
|
||||||
while (true) {
|
while (true) {
|
||||||
const button = await findAsync(app, 'button')
|
const button = await findAsync(app, 'button')
|
||||||
|
@ -7,10 +7,11 @@ const PreferencesController = require('../../../../app/scripts/controllers/prefe
|
|||||||
|
|
||||||
describe('DetectTokensController', () => {
|
describe('DetectTokensController', () => {
|
||||||
const sandbox = sinon.createSandbox()
|
const sandbox = sinon.createSandbox()
|
||||||
let clock
|
let clock, keyringMemStore, network, preferences
|
||||||
let keyringMemStore
|
beforeEach(async () => {
|
||||||
before(async () => {
|
|
||||||
keyringMemStore = new ObservableStore({ isUnlocked: false})
|
keyringMemStore = new ObservableStore({ isUnlocked: false})
|
||||||
|
network = new NetworkController({ provider: { type: 'mainnet' }})
|
||||||
|
preferences = new PreferencesController({ network })
|
||||||
})
|
})
|
||||||
after(() => {
|
after(() => {
|
||||||
sandbox.restore()
|
sandbox.restore()
|
||||||
@ -25,9 +26,7 @@ describe('DetectTokensController', () => {
|
|||||||
|
|
||||||
it('should be called on every polling period', async () => {
|
it('should be called on every polling period', async () => {
|
||||||
clock = sandbox.useFakeTimers()
|
clock = sandbox.useFakeTimers()
|
||||||
const network = new NetworkController()
|
|
||||||
network.setProviderType('mainnet')
|
network.setProviderType('mainnet')
|
||||||
const preferences = new PreferencesController()
|
|
||||||
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
||||||
controller.isOpen = true
|
controller.isOpen = true
|
||||||
controller.isUnlocked = true
|
controller.isUnlocked = true
|
||||||
@ -45,9 +44,7 @@ describe('DetectTokensController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not check tokens while in test network', async () => {
|
it('should not check tokens while in test network', async () => {
|
||||||
const network = new NetworkController()
|
|
||||||
network.setProviderType('rinkeby')
|
network.setProviderType('rinkeby')
|
||||||
const preferences = new PreferencesController()
|
|
||||||
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
||||||
controller.isOpen = true
|
controller.isOpen = true
|
||||||
controller.isUnlocked = true
|
controller.isUnlocked = true
|
||||||
@ -61,9 +58,7 @@ describe('DetectTokensController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should only check and add tokens while in main network', async () => {
|
it('should only check and add tokens while in main network', async () => {
|
||||||
const network = new NetworkController()
|
|
||||||
network.setProviderType('mainnet')
|
network.setProviderType('mainnet')
|
||||||
const preferences = new PreferencesController()
|
|
||||||
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
||||||
controller.isOpen = true
|
controller.isOpen = true
|
||||||
controller.isUnlocked = true
|
controller.isUnlocked = true
|
||||||
@ -80,9 +75,7 @@ describe('DetectTokensController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not detect same token while in main network', async () => {
|
it('should not detect same token while in main network', async () => {
|
||||||
const network = new NetworkController()
|
|
||||||
network.setProviderType('mainnet')
|
network.setProviderType('mainnet')
|
||||||
const preferences = new PreferencesController()
|
|
||||||
preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8)
|
preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8)
|
||||||
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
||||||
controller.isOpen = true
|
controller.isOpen = true
|
||||||
@ -100,9 +93,7 @@ describe('DetectTokensController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should trigger detect new tokens when change address', async () => {
|
it('should trigger detect new tokens when change address', async () => {
|
||||||
const network = new NetworkController()
|
|
||||||
network.setProviderType('mainnet')
|
network.setProviderType('mainnet')
|
||||||
const preferences = new PreferencesController()
|
|
||||||
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
||||||
controller.isOpen = true
|
controller.isOpen = true
|
||||||
controller.isUnlocked = true
|
controller.isUnlocked = true
|
||||||
@ -112,9 +103,7 @@ describe('DetectTokensController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should trigger detect new tokens when submit password', async () => {
|
it('should trigger detect new tokens when submit password', async () => {
|
||||||
const network = new NetworkController()
|
|
||||||
network.setProviderType('mainnet')
|
network.setProviderType('mainnet')
|
||||||
const preferences = new PreferencesController()
|
|
||||||
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
||||||
controller.isOpen = true
|
controller.isOpen = true
|
||||||
controller.selectedAddress = '0x0'
|
controller.selectedAddress = '0x0'
|
||||||
@ -124,9 +113,7 @@ describe('DetectTokensController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not trigger detect new tokens when not open or not unlocked', async () => {
|
it('should not trigger detect new tokens when not open or not unlocked', async () => {
|
||||||
const network = new NetworkController()
|
|
||||||
network.setProviderType('mainnet')
|
network.setProviderType('mainnet')
|
||||||
const preferences = new PreferencesController()
|
|
||||||
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
|
||||||
controller.isOpen = true
|
controller.isOpen = true
|
||||||
controller.isUnlocked = false
|
controller.isUnlocked = false
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
|
const ObservableStore = require('obs-store')
|
||||||
const PreferencesController = require('../../../../app/scripts/controllers/preferences')
|
const PreferencesController = require('../../../../app/scripts/controllers/preferences')
|
||||||
|
|
||||||
describe('preferences controller', function () {
|
describe('preferences controller', function () {
|
||||||
let preferencesController
|
let preferencesController
|
||||||
|
let network
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
preferencesController = new PreferencesController()
|
network = {providerStore: new ObservableStore({ type: 'mainnet' })}
|
||||||
|
preferencesController = new PreferencesController({ network })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('setAddresses', function () {
|
describe('setAddresses', function () {
|
||||||
@ -28,6 +31,20 @@ describe('preferences controller', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should create account tokens for each account in the store', function () {
|
||||||
|
preferencesController.setAddresses([
|
||||||
|
'0xda22le',
|
||||||
|
'0x7e57e2',
|
||||||
|
])
|
||||||
|
|
||||||
|
const accountTokens = preferencesController.store.getState().accountTokens
|
||||||
|
|
||||||
|
assert.deepEqual(accountTokens, {
|
||||||
|
'0xda22le': {},
|
||||||
|
'0x7e57e2': {},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('should replace its list of addresses', function () {
|
it('should replace its list of addresses', function () {
|
||||||
preferencesController.setAddresses([
|
preferencesController.setAddresses([
|
||||||
'0xda22le',
|
'0xda22le',
|
||||||
@ -64,6 +81,17 @@ describe('preferences controller', function () {
|
|||||||
assert.equal(preferencesController.store.getState().identities['0xda22le'], undefined)
|
assert.equal(preferencesController.store.getState().identities['0xda22le'], undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should remove an address from state and respective tokens', function () {
|
||||||
|
preferencesController.setAddresses([
|
||||||
|
'0xda22le',
|
||||||
|
'0x7e57e2',
|
||||||
|
])
|
||||||
|
|
||||||
|
preferencesController.removeAddress('0xda22le')
|
||||||
|
|
||||||
|
assert.equal(preferencesController.store.getState().accountTokens['0xda22le'], undefined)
|
||||||
|
})
|
||||||
|
|
||||||
it('should switch accounts if the selected address is removed', function () {
|
it('should switch accounts if the selected address is removed', function () {
|
||||||
preferencesController.setAddresses([
|
preferencesController.setAddresses([
|
||||||
'0xda22le',
|
'0xda22le',
|
||||||
@ -158,6 +186,42 @@ describe('preferences controller', function () {
|
|||||||
await preferencesController.addToken(address, symbol, decimals)
|
await preferencesController.addToken(address, symbol, decimals)
|
||||||
assert.equal(preferencesController.getTokens().length, 1, 'one token added for 2nd address')
|
assert.equal(preferencesController.getTokens().length, 1, 'one token added for 2nd address')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should add token per account', async function () {
|
||||||
|
const addressFirst = '0xabcdef1234567'
|
||||||
|
const addressSecond = '0xabcdef1234568'
|
||||||
|
const symbolFirst = 'ABBR'
|
||||||
|
const symbolSecond = 'ABBB'
|
||||||
|
const decimals = 5
|
||||||
|
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e2')
|
||||||
|
await preferencesController.addToken(addressFirst, symbolFirst, decimals)
|
||||||
|
const tokensFirstAddress = preferencesController.getTokens()
|
||||||
|
|
||||||
|
await preferencesController.setSelectedAddress('0xda22le')
|
||||||
|
await preferencesController.addToken(addressSecond, symbolSecond, decimals)
|
||||||
|
const tokensSeconAddress = preferencesController.getTokens()
|
||||||
|
|
||||||
|
assert.notEqual(tokensFirstAddress, tokensSeconAddress, 'add different tokens for two account and tokens are equal')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should add token per network', async function () {
|
||||||
|
const addressFirst = '0xabcdef1234567'
|
||||||
|
const addressSecond = '0xabcdef1234568'
|
||||||
|
const symbolFirst = 'ABBR'
|
||||||
|
const symbolSecond = 'ABBB'
|
||||||
|
const decimals = 5
|
||||||
|
|
||||||
|
network.providerStore.updateState({ type: 'mainnet' })
|
||||||
|
await preferencesController.addToken(addressFirst, symbolFirst, decimals)
|
||||||
|
const tokensFirstAddress = preferencesController.getTokens()
|
||||||
|
|
||||||
|
network.providerStore.updateState({ type: 'rinkeby' })
|
||||||
|
await preferencesController.addToken(addressSecond, symbolSecond, decimals)
|
||||||
|
const tokensSeconAddress = preferencesController.getTokens()
|
||||||
|
|
||||||
|
assert.notEqual(tokensFirstAddress, tokensSeconAddress, 'add different tokens for two networks and tokens are equal')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('removeToken', function () {
|
describe('removeToken', function () {
|
||||||
@ -182,6 +246,98 @@ describe('preferences controller', function () {
|
|||||||
const [token1] = tokens
|
const [token1] = tokens
|
||||||
assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5})
|
assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should remove a token from its state on corresponding address', async function () {
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e2')
|
||||||
|
await preferencesController.addToken('0xa', 'A', 4)
|
||||||
|
await preferencesController.addToken('0xb', 'B', 5)
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e3')
|
||||||
|
await preferencesController.addToken('0xa', 'A', 4)
|
||||||
|
await preferencesController.addToken('0xb', 'B', 5)
|
||||||
|
const initialTokensSecond = preferencesController.getTokens()
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e2')
|
||||||
|
await preferencesController.removeToken('0xa')
|
||||||
|
|
||||||
|
const tokensFirst = preferencesController.getTokens()
|
||||||
|
assert.equal(tokensFirst.length, 1, 'one token removed in account')
|
||||||
|
|
||||||
|
const [token1] = tokensFirst
|
||||||
|
assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5})
|
||||||
|
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e3')
|
||||||
|
const tokensSecond = preferencesController.getTokens()
|
||||||
|
assert.deepEqual(tokensSecond, initialTokensSecond, 'token deleted for account')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should remove a token from its state on corresponding network', async function () {
|
||||||
|
network.providerStore.updateState({ type: 'mainnet' })
|
||||||
|
await preferencesController.addToken('0xa', 'A', 4)
|
||||||
|
await preferencesController.addToken('0xb', 'B', 5)
|
||||||
|
network.providerStore.updateState({ type: 'rinkeby' })
|
||||||
|
await preferencesController.addToken('0xa', 'A', 4)
|
||||||
|
await preferencesController.addToken('0xb', 'B', 5)
|
||||||
|
const initialTokensSecond = preferencesController.getTokens()
|
||||||
|
network.providerStore.updateState({ type: 'mainnet' })
|
||||||
|
await preferencesController.removeToken('0xa')
|
||||||
|
|
||||||
|
const tokensFirst = preferencesController.getTokens()
|
||||||
|
assert.equal(tokensFirst.length, 1, 'one token removed in network')
|
||||||
|
|
||||||
|
const [token1] = tokensFirst
|
||||||
|
assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5})
|
||||||
|
|
||||||
|
network.providerStore.updateState({ type: 'rinkeby' })
|
||||||
|
const tokensSecond = preferencesController.getTokens()
|
||||||
|
assert.deepEqual(tokensSecond, initialTokensSecond, 'token deleted for network')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('on setSelectedAddress', function () {
|
||||||
|
it('should update tokens from its state on corresponding address', async function () {
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e2')
|
||||||
|
await preferencesController.addToken('0xa', 'A', 4)
|
||||||
|
await preferencesController.addToken('0xb', 'B', 5)
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e3')
|
||||||
|
await preferencesController.addToken('0xa', 'C', 4)
|
||||||
|
await preferencesController.addToken('0xb', 'D', 5)
|
||||||
|
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e2')
|
||||||
|
const initialTokensFirst = preferencesController.getTokens()
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e3')
|
||||||
|
const initialTokensSecond = preferencesController.getTokens()
|
||||||
|
|
||||||
|
assert.notDeepEqual(initialTokensFirst, initialTokensSecond, 'tokens not equal for different accounts and tokens')
|
||||||
|
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e2')
|
||||||
|
const tokensFirst = preferencesController.getTokens()
|
||||||
|
await preferencesController.setSelectedAddress('0x7e57e3')
|
||||||
|
const tokensSecond = preferencesController.getTokens()
|
||||||
|
|
||||||
|
assert.deepEqual(tokensFirst, initialTokensFirst, 'tokens equal for same account')
|
||||||
|
assert.deepEqual(tokensSecond, initialTokensSecond, 'tokens equal for same account')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('on updateStateNetworkType', function () {
|
||||||
|
it('should remove a token from its state on corresponding network', async function () {
|
||||||
|
network.providerStore.updateState({ type: 'mainnet' })
|
||||||
|
await preferencesController.addToken('0xa', 'A', 4)
|
||||||
|
await preferencesController.addToken('0xb', 'B', 5)
|
||||||
|
const initialTokensFirst = preferencesController.getTokens()
|
||||||
|
network.providerStore.updateState({ type: 'rinkeby' })
|
||||||
|
await preferencesController.addToken('0xa', 'C', 4)
|
||||||
|
await preferencesController.addToken('0xb', 'D', 5)
|
||||||
|
const initialTokensSecond = preferencesController.getTokens()
|
||||||
|
|
||||||
|
assert.notDeepEqual(initialTokensFirst, initialTokensSecond, 'tokens not equal for different networks and tokens')
|
||||||
|
|
||||||
|
network.providerStore.updateState({ type: 'mainnet' })
|
||||||
|
const tokensFirst = preferencesController.getTokens()
|
||||||
|
network.providerStore.updateState({ type: 'rinkeby' })
|
||||||
|
const tokensSecond = preferencesController.getTokens()
|
||||||
|
assert.deepEqual(tokensFirst, initialTokensFirst, 'tokens equal for same network')
|
||||||
|
assert.deepEqual(tokensSecond, initialTokensSecond, 'tokens equal for same network')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1486,11 +1486,12 @@ function showAccountDetail (address) {
|
|||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(actions.showLoadingIndication())
|
dispatch(actions.showLoadingIndication())
|
||||||
log.debug(`background.setSelectedAddress`)
|
log.debug(`background.setSelectedAddress`)
|
||||||
background.setSelectedAddress(address, (err) => {
|
background.setSelectedAddress(address, (err, tokens) => {
|
||||||
dispatch(actions.hideLoadingIndication())
|
dispatch(actions.hideLoadingIndication())
|
||||||
if (err) {
|
if (err) {
|
||||||
return dispatch(actions.displayWarning(err.message))
|
return dispatch(actions.displayWarning(err.message))
|
||||||
}
|
}
|
||||||
|
dispatch(updateTokens(tokens))
|
||||||
dispatch({
|
dispatch({
|
||||||
type: actions.SHOW_ACCOUNT_DETAIL,
|
type: actions.SHOW_ACCOUNT_DETAIL,
|
||||||
value: address,
|
value: address,
|
||||||
|
@ -43,7 +43,7 @@ export default class ConfirmPageContainer extends Component {
|
|||||||
// Footer
|
// Footer
|
||||||
onCancel: PropTypes.func,
|
onCancel: PropTypes.func,
|
||||||
onSubmit: PropTypes.func,
|
onSubmit: PropTypes.func,
|
||||||
valid: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -54,7 +54,7 @@ export default class ConfirmPageContainer extends Component {
|
|||||||
fromAddress,
|
fromAddress,
|
||||||
toName,
|
toName,
|
||||||
toAddress,
|
toAddress,
|
||||||
valid,
|
disabled,
|
||||||
errorKey,
|
errorKey,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
contentComponent,
|
contentComponent,
|
||||||
@ -110,7 +110,7 @@ export default class ConfirmPageContainer extends Component {
|
|||||||
onSubmit={() => onSubmit()}
|
onSubmit={() => onSubmit()}
|
||||||
submitText={this.context.t('confirm')}
|
submitText={this.context.t('confirm')}
|
||||||
submitButtonType="confirm"
|
submitButtonType="confirm"
|
||||||
disabled={!valid}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -87,7 +87,6 @@ class DropdownMenuItem extends Component {
|
|||||||
padding: '8px 0px',
|
padding: '8px 0px',
|
||||||
fontSize: '18px',
|
fontSize: '18px',
|
||||||
fontStyle: 'normal',
|
fontStyle: 'normal',
|
||||||
fontFamily: 'Montserrat Regular',
|
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
|
@ -71,7 +71,6 @@ NetworkDropdown.prototype.render = function () {
|
|||||||
const rpcList = props.frequentRpcList
|
const rpcList = props.frequentRpcList
|
||||||
const isOpen = this.props.networkDropdownOpen
|
const isOpen = this.props.networkDropdownOpen
|
||||||
const dropdownMenuItemStyle = {
|
const dropdownMenuItemStyle = {
|
||||||
fontFamily: 'DIN OT',
|
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
lineHeight: '20px',
|
lineHeight: '20px',
|
||||||
padding: '12px 0',
|
padding: '12px 0',
|
||||||
@ -286,7 +285,6 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) {
|
|||||||
closeMenu: () => this.props.hideNetworkDropdown(),
|
closeMenu: () => this.props.hideNetworkDropdown(),
|
||||||
onClick: () => props.setRpcTarget(rpc),
|
onClick: () => props.setRpcTarget(rpc),
|
||||||
style: {
|
style: {
|
||||||
fontFamily: 'DIN OT',
|
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
lineHeight: '20px',
|
lineHeight: '20px',
|
||||||
padding: '12px 0',
|
padding: '12px 0',
|
||||||
@ -325,7 +323,6 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) {
|
|||||||
onClick: () => props.setRpcTarget(rpcTarget),
|
onClick: () => props.setRpcTarget(rpcTarget),
|
||||||
closeMenu: () => this.props.hideNetworkDropdown(),
|
closeMenu: () => this.props.hideNetworkDropdown(),
|
||||||
style: {
|
style: {
|
||||||
fontFamily: 'DIN OT',
|
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
lineHeight: '20px',
|
lineHeight: '20px',
|
||||||
padding: '12px 0',
|
padding: '12px 0',
|
||||||
|
@ -71,6 +71,10 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
warning: PropTypes.string,
|
warning: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
submitting: false,
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate () {
|
||||||
const {
|
const {
|
||||||
transactionStatus,
|
transactionStatus,
|
||||||
@ -258,15 +262,25 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
|
|
||||||
handleSubmit () {
|
handleSubmit () {
|
||||||
const { sendTransaction, clearConfirmTransaction, txData, history, onSubmit } = this.props
|
const { sendTransaction, clearConfirmTransaction, txData, history, onSubmit } = this.props
|
||||||
|
const { submitting } = this.state
|
||||||
|
|
||||||
|
if (submitting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ submitting: true })
|
||||||
|
|
||||||
if (onSubmit) {
|
if (onSubmit) {
|
||||||
onSubmit(txData)
|
Promise.resolve(onSubmit(txData))
|
||||||
|
.then(this.setState({ submitting: false }))
|
||||||
} else {
|
} else {
|
||||||
sendTransaction(txData)
|
sendTransaction(txData)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
clearConfirmTransaction()
|
clearConfirmTransaction()
|
||||||
|
this.setState({ submitting: false })
|
||||||
history.push(DEFAULT_ROUTE)
|
history.push(DEFAULT_ROUTE)
|
||||||
})
|
})
|
||||||
|
.catch(() => this.setState({ submitting: false }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +294,7 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
methodData,
|
methodData,
|
||||||
ethTransactionAmount,
|
ethTransactionAmount,
|
||||||
fiatTransactionAmount,
|
fiatTransactionAmount,
|
||||||
valid: propsValid,
|
valid: propsValid = true,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
errorKey: propsErrorKey,
|
errorKey: propsErrorKey,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
@ -295,6 +309,7 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
nonce,
|
nonce,
|
||||||
warning,
|
warning,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
const { submitting } = this.state
|
||||||
|
|
||||||
const { name } = methodData
|
const { name } = methodData
|
||||||
const fiatConvertedAmount = formatCurrency(fiatTransactionAmount, currentCurrency)
|
const fiatConvertedAmount = formatCurrency(fiatTransactionAmount, currentCurrency)
|
||||||
@ -320,7 +335,7 @@ export default class ConfirmTransactionBase extends Component {
|
|||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
errorKey={propsErrorKey || errorKey}
|
errorKey={propsErrorKey || errorKey}
|
||||||
warning={warning}
|
warning={warning}
|
||||||
valid={propsValid || valid}
|
disabled={!propsValid || !valid || submitting}
|
||||||
onEdit={() => this.handleEdit()}
|
onEdit={() => this.handleEdit()}
|
||||||
onCancel={() => this.handleCancel()}
|
onCancel={() => this.handleCancel()}
|
||||||
onSubmit={() => this.handleSubmit()}
|
onSubmit={() => this.handleSubmit()}
|
||||||
|
@ -213,7 +213,7 @@ TxListItem.prototype.showRetryButton = function () {
|
|||||||
if (!txParams) {
|
if (!txParams) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let currentTxIsLatest = false
|
let currentTxSharesEarliestNonce = false
|
||||||
const currentNonce = txParams.nonce
|
const currentNonce = txParams.nonce
|
||||||
const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
|
const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
|
||||||
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
||||||
@ -222,14 +222,14 @@ TxListItem.prototype.showRetryButton = function () {
|
|||||||
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
||||||
lastSubmittedTxWithCurrentNonce.id === transactionId
|
lastSubmittedTxWithCurrentNonce.id === transactionId
|
||||||
if (currentSubmittedTxs.length > 0) {
|
if (currentSubmittedTxs.length > 0) {
|
||||||
const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
|
const earliestSubmitted = currentSubmittedTxs.reduce((tx1, tx2) => {
|
||||||
if (tx1.submittedTime < tx2.submittedTime) return tx1
|
if (tx1.submittedTime < tx2.submittedTime) return tx1
|
||||||
return tx2
|
return tx2
|
||||||
})
|
})
|
||||||
currentTxIsLatest = lastTx.id === transactionId
|
currentTxSharesEarliestNonce = currentNonce === earliestSubmitted.txParams.nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000 && currentTxIsLatest
|
return currentTxSharesEarliestNonce && currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
|
||||||
}
|
}
|
||||||
|
|
||||||
TxListItem.prototype.setSelectedToken = function (tokenAddress) {
|
TxListItem.prototype.setSelectedToken = function (tokenAddress) {
|
||||||
|
@ -76,7 +76,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.network-name-item {
|
.network-name-item {
|
||||||
font-weight: 100;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
color: $dusty-gray;
|
color: $dusty-gray;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
Loading…
Reference in New Issue
Block a user