aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkumavis <aaron@kumavis.me>2018-06-07 02:08:29 +0800
committerkumavis <aaron@kumavis.me>2018-06-07 02:08:29 +0800
commit9d77b0a19648fa5c4de594bbecaab2137d5d5658 (patch)
treefe87a5798021ac1b3332a3a13a70f5c4a930e126
parent32293a959c367ce5dd585111d4ee0d873072c830 (diff)
parentc2e3194dbf4e2a3fd2bdffb3300ec0cd822dc78e (diff)
downloadtangerine-wallet-browser-9d77b0a19648fa5c4de594bbecaab2137d5d5658.tar
tangerine-wallet-browser-9d77b0a19648fa5c4de594bbecaab2137d5d5658.tar.gz
tangerine-wallet-browser-9d77b0a19648fa5c4de594bbecaab2137d5d5658.tar.bz2
tangerine-wallet-browser-9d77b0a19648fa5c4de594bbecaab2137d5d5658.tar.lz
tangerine-wallet-browser-9d77b0a19648fa5c4de594bbecaab2137d5d5658.tar.xz
tangerine-wallet-browser-9d77b0a19648fa5c4de594bbecaab2137d5d5658.tar.zst
tangerine-wallet-browser-9d77b0a19648fa5c4de594bbecaab2137d5d5658.zip
Merge branch 'develop' of github.com:MetaMask/metamask-extension into network-remove-provider-engine
-rw-r--r--CHANGELOG.md35
-rw-r--r--MISSION.md14
-rw-r--r--README.md12
-rw-r--r--app/_locales/en/messages.json23
-rw-r--r--app/images/check-icon.svg17
-rw-r--r--app/manifest.json5
-rw-r--r--app/scripts/background.js14
-rw-r--r--app/scripts/controllers/preferences.js80
-rw-r--r--app/scripts/controllers/transactions/index.js8
-rw-r--r--app/scripts/controllers/transactions/tx-state-manager.js21
-rw-r--r--app/scripts/controllers/user-actions.js17
-rw-r--r--app/scripts/lib/cleanErrorStack.js24
-rw-r--r--app/scripts/lib/createErrorMiddleware.js1
-rw-r--r--app/scripts/lib/diagnostics-reporter.js71
-rw-r--r--app/scripts/lib/get-first-preferred-lang-code.js11
-rw-r--r--app/scripts/metamask-controller.js39
-rw-r--r--app/scripts/migrations/026.js2
-rw-r--r--docs/publishing.md28
-rw-r--r--mascara/src/app/first-time/create-password-screen.js2
-rw-r--r--mascara/src/app/first-time/import-seed-phrase-screen.js2
-rw-r--r--mascara/src/app/first-time/index.css17
-rw-r--r--package-lock.json111
-rw-r--r--package.json5
-rw-r--r--test/e2e/metamask.spec.js7
-rw-r--r--test/integration/lib/add-token.js10
-rw-r--r--test/integration/lib/confirm-sig-requests.js6
-rw-r--r--test/integration/lib/send-new-ui.js6
-rw-r--r--test/screens/new-ui.js12
-rw-r--r--test/unit/app/controllers/metamask-controller-test.js27
-rw-r--r--ui/app/actions.js50
-rw-r--r--ui/app/app.js3
-rw-r--r--ui/app/components/button/button.component.js23
-rw-r--r--ui/app/components/button/button.stories.js19
-rw-r--r--ui/app/components/customize-gas-modal/index.js2
-rw-r--r--ui/app/components/index.scss4
-rw-r--r--ui/app/components/loading-screen/loading-screen.component.js6
-rw-r--r--ui/app/components/modals/confirm-reset-account/confirm-reset-account.component.js54
-rw-r--r--ui/app/components/modals/confirm-reset-account/confirm-reset-account.container.js13
-rw-r--r--ui/app/components/modals/confirm-reset-account/index.js2
-rw-r--r--ui/app/components/modals/deposit-ether-modal.js2
-rw-r--r--ui/app/components/modals/edit-account-name-modal.js2
-rw-r--r--ui/app/components/modals/export-private-key-modal.js6
-rw-r--r--ui/app/components/modals/hide-token-confirmation-modal.js2
-rw-r--r--ui/app/components/modals/index.scss52
-rw-r--r--ui/app/components/modals/modal.js71
-rw-r--r--ui/app/components/modals/notification-modals/confirm-reset-account.js46
-rw-r--r--ui/app/components/modals/notification/index.js2
-rw-r--r--ui/app/components/modals/notification/notification.component.js30
-rw-r--r--ui/app/components/modals/notification/notification.container.js38
-rw-r--r--ui/app/components/modals/shapeshift-deposit-tx-modal.js2
-rw-r--r--ui/app/components/modals/transaction-confirmed/index.js2
-rw-r--r--ui/app/components/modals/transaction-confirmed/transaction-confirmed.component.js24
-rw-r--r--ui/app/components/modals/welcome-beta/index.js2
-rw-r--r--ui/app/components/modals/welcome-beta/welcome-beta.component.js23
-rw-r--r--ui/app/components/pages/add-token/add-token.component.js2
-rw-r--r--ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss4
-rw-r--r--ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js2
-rw-r--r--ui/app/components/pages/confirm-add-token/confirm-add-token.component.js2
-rw-r--r--ui/app/components/pages/create-account/import-account/index.js2
-rw-r--r--ui/app/components/pages/create-account/import-account/json.js4
-rw-r--r--ui/app/components/pages/create-account/import-account/private-key.js6
-rw-r--r--ui/app/components/pages/create-account/new-account.js4
-rw-r--r--ui/app/components/pages/keychains/reveal-seed.js6
-rw-r--r--ui/app/components/pages/settings/settings.js8
-rw-r--r--ui/app/components/pages/unlock-page/unlock-page.component.js20
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js60
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js57
-rw-r--r--ui/app/components/pending-tx/index.js5
-rw-r--r--ui/app/components/selected-account/index.js2
-rw-r--r--ui/app/components/selected-account/index.scss38
-rw-r--r--ui/app/components/selected-account/selected-account.component.js60
-rw-r--r--ui/app/components/selected-account/selected-account.container.js13
-rw-r--r--ui/app/components/shapeshift-form.js2
-rw-r--r--ui/app/components/signature-request.js4
-rw-r--r--ui/app/components/text-field/text-field.component.js95
-rw-r--r--ui/app/components/text-field/text-field.stories.js29
-rw-r--r--ui/app/components/token-cell.js4
-rw-r--r--ui/app/components/tx-list-item.js10
-rw-r--r--ui/app/components/tx-view.js28
-rw-r--r--ui/app/components/wallet-view.js12
-rw-r--r--ui/app/conf-tx.js63
-rw-r--r--ui/app/css/itcss/components/account-menu.scss4
-rw-r--r--ui/app/css/itcss/components/buttons.scss49
-rw-r--r--ui/app/css/itcss/components/hero-balance.scss1
-rw-r--r--ui/app/css/itcss/components/loading-overlay.scss4
-rw-r--r--ui/app/css/itcss/components/token-list.scss13
-rw-r--r--ui/app/reducers/app.js19
-rw-r--r--ui/app/send-v2.js2
88 files changed, 1325 insertions, 416 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2cec4fd6b..f4c2abd4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,41 @@
## Current Master
+- Fixes issue where old nicknames were kept around causing errors.
+
+## 4.7.2 Sun Jun 03 2018
+
+- Fix bug preventing users from logging in. Internally accounts and identities were out of sync.
+- Fix support links to point to new support system (Zendesk)
+- Fix bug in migration #26 ( moving account nicknames to preferences )
+- Clears account nicknames on restore from seedPhrase
+
+## 4.7.1 Fri Jun 01 2018
+
+- Fix bug where errors were not returned to Dapps.
+
+## 4.7.0 Wed May 30 2018
+
+- Fix Brave support
+- Adds error messages when passwords don't match in onboarding flow.
+- Adds modal notification if a retry in the process of being confirmed is dropped.
+- New unlock screen design.
+- Design improvements to the add token screen.
+- Fix inconsistencies in confirm screen between extension and browser window modes.
+- Fix scrolling in deposit ether modal.
+- Fix styling of app spinner.
+- Font weight changed from 300 to 400.
+- New reveal screen design.
+- Styling improvements to labels in first time flow and signature request headers.
+
+## 4.6.1 Mon Apr 30 2018
+
+- Fix bug where sending a transaction resulted in an infinite spinner
+- Allow transactions with a 0 gwei gas price
+- Handle encoding errors in ERC20 symbol + digits
+- Fix ShapeShift forms (new + old ui)
+- Fix sourcemaps
+
## 4.6.0 Thu Apr 26 2018
- Correctly format currency conversion for locally selected preferred currency.
diff --git a/MISSION.md b/MISSION.md
new file mode 100644
index 000000000..9045828b1
--- /dev/null
+++ b/MISSION.md
@@ -0,0 +1,14 @@
+# MetaMask Philosophy
+
+## Mission
+
+Making it safe and easy for the most people to use the decentralized web to the greatest degree that is empowering to them.
+
+## Vision
+
+To realize the highest goals achievable for the human race with the twin powers of peer to peer networks and cryptography. To empower users to hold and use their own keys on these new networks as securely and intelligibly as possible, enabling a new world of peer to peer agreements and economies, in hopes that we may collectively overcome the many great problems that we face together, through the power of strong cooperation.
+
+## Strategy
+
+We provide software for users to manage accounts, for sites to easily propose actions to users, and for users to coherently review actions before approving them. We build on this rapidly evolving set of protocols with the goal of empowering the most people to the greatest degree, and aspire to continuously evolve our offering to pursue that goal.
+
diff --git a/README.md b/README.md
index ca25fc0b9..70faa8856 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,16 @@
# MetaMask Browser Extension
[![Build Status](https://circleci.com/gh/MetaMask/metamask-extension.svg?style=shield&circle-token=a1ddcf3cd38e29267f254c9c59d556d513e3a1fd)](https://circleci.com/gh/MetaMask/metamask-extension) [![Coverage Status](https://coveralls.io/repos/github/MetaMask/metamask-extension/badge.svg?branch=master)](https://coveralls.io/github/MetaMask/metamask-extension?branch=master) [![Greenkeeper badge](https://badges.greenkeeper.io/MetaMask/metamask-extension.svg)](https://greenkeeper.io/) [![Stories in Ready](https://badge.waffle.io/MetaMask/metamask-extension.png?label=in%20progress&title=waffle.io)](https://waffle.io/MetaMask/metamask-extension)
-[Internal documentation](./docs/jsdocs)
-
## Support
If you're a user seeking support, [here is our support site](https://metamask.helpscoutdocs.com/).
+## Introduction
+
+[Mission Statement](./MISSION.md)
+
+[Internal documentation](./docs/jsdocs)
+
## Developing Compatible Dapps
If you're a web dapp developer, we've got two types of guides for you:
@@ -23,7 +27,9 @@ If you're a web dapp developer, we've got two types of guides for you:
## Building locally
- Install [Node.js](https://nodejs.org/en/) version 6.3.1 or later.
- - Install local dependencies with `npm install`.
+ - Install dependencies:
+ - For node versions up to and including 9, install local dependencies with `npm install`.
+ - For node versions 10 and later, install [Yarn](https://yarnpkg.com/lang/en/docs/install/) and use `yarn install`.
- Install gulp globally with `npm install -g gulp-cli`.
- Build the project to the `./dist/` folder with `gulp build`.
- Optionally, to rebuild on file changes, run `gulp dev`.
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index fa01fea24..40d362f51 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -402,6 +402,9 @@
"infoHelp": {
"message": "Info & Help"
},
+ "initialTransactionConfirmed": {
+ "message": "Your initial transaction was confirmed by the network. Click OK to go back."
+ },
"insufficientFunds": {
"message": "Insufficient funds."
},
@@ -520,6 +523,9 @@
"networks": {
"message": "Networks"
},
+ "nevermind": {
+ "message": "Nevermind"
+ },
"newAccount": {
"message": "New Account"
},
@@ -634,9 +640,15 @@
"rejected": {
"message": "Rejected"
},
+ "reset": {
+ "message": "Reset"
+ },
"resetAccount": {
"message": "Reset Account"
},
+ "resetAccountDescription": {
+ "message": "Resetting your account will clear your transaction history."
+ },
"restoreFromSeed": {
"message": "Restore account?"
},
@@ -676,6 +688,9 @@
"ropsten": {
"message": "Ropsten Test Network"
},
+ "rpc": {
+ "message": "Custom RPC"
+ },
"currentRpc": {
"message": "Current RPC"
},
@@ -701,10 +716,10 @@
"save": {
"message": "Save"
},
- "reprice_title": {
- "message": "Reprice Transaction"
+ "speedUpTitle": {
+ "message": "Speed Up Transaction"
},
- "reprice_subtitle": {
+ "speedUpSubtitle": {
"message": "Increase your gas price to attempt to overwrite and speed up your transaction"
},
"saveAsCsvFile": {
@@ -891,7 +906,7 @@
"message": "Welcome to the New UI (Beta)"
},
"uiWelcomeMessage": {
- "message": "You are now using the new Metamask UI. Take a look around, try out new features like sending tokens, and let us know if you have any issues."
+ "message": "You are now using the new Metamask UI."
},
"unapproved": {
"message": "Unapproved"
diff --git a/app/images/check-icon.svg b/app/images/check-icon.svg
new file mode 100644
index 000000000..cafa864e5
--- /dev/null
+++ b/app/images/check-icon.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: sketchtool 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
+ <title>76BCDB09-52B0-41CB-908F-12F9087A2F1B</title>
+ <desc>Created with sketchtool.</desc>
+ <defs></defs>
+ <g id="Confirm-TX-screen" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="confirmed-alert" transform="translate(-144.000000, -53.000000)" stroke="#61BA00" stroke-width="4">
+ <g id="Group-17-Copy" transform="translate(22.000000, 20.000000)">
+ <g id="check-icon" transform="translate(124.000000, 35.000000)">
+ <circle id="Oval-5" cx="48" cy="48" r="48"></circle>
+ <polyline id="Path-3" stroke-linecap="round" points="29.76 52.8 41.0023819 64.32 71.04 34.56"></polyline>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/app/manifest.json b/app/manifest.json
index 3e5eed205..c1f26d2ea 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "4.6.0",
+ "version": "4.7.2",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
@@ -67,6 +67,9 @@
"externally_connectable": {
"matches": [
"https://metamask.io/*"
+ ],
+ "ids": [
+ "*"
]
}
} \ No newline at end of file
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 686296329..56e190f97 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -309,6 +309,7 @@ function setupController (initState, initLangCode) {
// connect to other contexts
//
extension.runtime.onConnect.addListener(connectRemote)
+ extension.runtime.onConnectExternal.addListener(connectExternal)
const metamaskInternalProcessHash = {
[ENVIRONMENT_TYPE_POPUP]: true,
@@ -335,9 +336,9 @@ function setupController (initState, initLangCode) {
function connectRemote (remotePort) {
const processName = remotePort.name
const isMetaMaskInternalProcess = metamaskInternalProcessHash[processName]
- const portStream = new PortStream(remotePort)
if (isMetaMaskInternalProcess) {
+ const portStream = new PortStream(remotePort)
// communication with popup
controller.isClientOpen = true
controller.setupTrustedCommunication(portStream, 'MetaMask')
@@ -370,12 +371,17 @@ function setupController (initState, initLangCode) {
})
}
} else {
- // communication with page
- const originDomain = urlUtil.parse(remotePort.sender.url).hostname
- controller.setupUntrustedCommunication(portStream, originDomain)
+ connectExternal(remotePort)
}
}
+ // communication with page or other extension
+ function connectExternal(remotePort) {
+ const originDomain = urlUtil.parse(remotePort.sender.url).hostname
+ const portStream = new PortStream(remotePort)
+ controller.setupUntrustedCommunication(portStream, originDomain)
+ }
+
//
// User Interface setup
//
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index a4ff1207e..a5d8cc27b 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -2,6 +2,7 @@ const ObservableStore = require('obs-store')
const normalizeAddress = require('eth-sig-util').normalize
const extend = require('xtend')
+
class PreferencesController {
/**
@@ -28,7 +29,11 @@ class PreferencesController {
featureFlags: {},
currentLocale: opts.initLangCode,
identities: {},
+ lostIdentities: {},
}, opts.initState)
+
+ this.diagnostics = opts.diagnostics
+
this.store = new ObservableStore(initState)
}
// PUBLIC METHODS
@@ -63,6 +68,13 @@ class PreferencesController {
this.store.updateState({ currentLocale: key })
}
+ /**
+ * Updates identities to only include specified addresses. Removes identities
+ * not included in addresses array
+ *
+ * @param {string[]} addresses An array of hex addresses
+ *
+ */
setAddresses (addresses) {
const oldIdentities = this.store.getState().identities
const identities = addresses.reduce((ids, address, index) => {
@@ -74,6 +86,68 @@ class PreferencesController {
}
/**
+ * Adds addresses to the identities object without removing identities
+ *
+ * @param {string[]} addresses An array of hex addresses
+ *
+ */
+ addAddresses (addresses) {
+ const identities = this.store.getState().identities
+ addresses.forEach((address) => {
+ // skip if already exists
+ if (identities[address]) return
+ // add missing identity
+ const identityCount = Object.keys(identities).length
+ identities[address] = { name: `Account ${identityCount + 1}`, address }
+ })
+ this.store.updateState({ identities })
+ }
+
+ /*
+ * Synchronizes identity entries with known accounts.
+ * Removes any unknown identities, and returns the resulting selected address.
+ *
+ * @param {Array<string>} addresses known to the vault.
+ * @returns {Promise<string>} selectedAddress the selected address.
+ */
+ syncAddresses (addresses) {
+ let { identities, lostIdentities } = this.store.getState()
+
+ let newlyLost = {}
+ Object.keys(identities).forEach((identity) => {
+ if (!addresses.includes(identity)) {
+ newlyLost[identity] = identities[identity]
+ delete identities[identity]
+ }
+ })
+
+ // Identities are no longer present.
+ if (Object.keys(newlyLost).length > 0) {
+
+ // Notify our servers:
+ if (this.diagnostics) this.diagnostics.reportOrphans(newlyLost)
+
+ // store lost accounts
+ for (let key in newlyLost) {
+ lostIdentities[key] = newlyLost[key]
+ }
+ }
+
+ this.store.updateState({ identities, lostIdentities })
+ this.addAddresses(addresses)
+
+ // If the selected account is no longer valid,
+ // select an arbitrary other account:
+ let selected = this.getSelectedAddress()
+ if (!addresses.includes(selected)) {
+ selected = addresses[0]
+ this.setSelectedAddress(selected)
+ }
+
+ return selected
+ }
+
+ /**
* Setter for the `selectedAddress` property
*
* @param {string} _address A new hex address for an account
@@ -111,7 +185,7 @@ class PreferencesController {
/**
* Adds a new token to the token array, or updates the token if passed an address that already exists.
* Modifies the existing tokens array from the store. All objects in the tokens array array AddedToken objects.
- * @see AddedToken {@link AddedToken}
+ * @see AddedToken {@link AddedToken}
*
* @param {string} rawAddress Hex address of the token contract. May or may not be a checksum address.
* @param {string} symbol The symbol of the token
@@ -197,7 +271,7 @@ class PreferencesController {
}
/**
- * Setter for the `currentAccountTab` property
+ * Setter for the `currentAccountTab` property
*
* @param {string} currentAccountTab Specifies the new tab to be marked as current
* @returns {Promise<void>} Promise resolves with undefined
@@ -215,7 +289,7 @@ class PreferencesController {
* The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the
* end of the list. The current list is modified and returned as a promise.
*
- * @param {string} _url The rpc url to add to the frequentRpcList.
+ * @param {string} _url The rpc url to add to the frequentRpcList.
* @returns {Promise<array>} The updated frequentRpcList.
*
*/
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index 71e7ea920..79241ed1a 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -8,6 +8,7 @@ const TxGasUtil = require('./tx-gas-utils')
const PendingTransactionTracker = require('./pending-tx-tracker')
const NonceTracker = require('./nonce-tracker')
const txUtils = require('./lib/util')
+const cleanErrorStack = require('../../lib/cleanErrorStack')
const log = require('loglevel')
/**
@@ -123,6 +124,7 @@ class TransactionController extends EventEmitter {
@param txParams {object} - txParams for the transaction
@param opts {object} - with the key origin to put the origin on the txMeta
*/
+
async newUnapprovedTransaction (txParams, opts = {}) {
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
const initialTxMeta = await this.addUnapprovedTransaction(txParams)
@@ -135,11 +137,11 @@ class TransactionController extends EventEmitter {
case 'submitted':
return resolve(finishedTxMeta.hash)
case 'rejected':
- return reject(new Error('MetaMask Tx Signature: User denied transaction signature.'))
+ return reject(cleanErrorStack(new Error('MetaMask Tx Signature: User denied transaction signature.')))
case 'failed':
- return reject(new Error(finishedTxMeta.err.message))
+ return reject(cleanErrorStack(new Error(finishedTxMeta.err.message)))
default:
- return reject(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`))
+ return reject(cleanErrorStack(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`)))
}
})
})
diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js
index 00e837571..0aae4774b 100644
--- a/app/scripts/controllers/transactions/tx-state-manager.js
+++ b/app/scripts/controllers/transactions/tx-state-manager.js
@@ -2,6 +2,7 @@ const extend = require('xtend')
const EventEmitter = require('events')
const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util')
+const log = require('loglevel')
const txStateHistoryHelper = require('./lib/tx-state-history-helper')
const createId = require('../../lib/random-id')
const { getFinalStates } = require('./lib/util')
@@ -398,13 +399,19 @@ class TransactionStateManager extends EventEmitter {
_setTxStatus (txId, status) {
const txMeta = this.getTx(txId)
txMeta.status = status
- this.emit(`${txMeta.id}:${status}`, txId)
- this.emit(`tx:status-update`, txId, status)
- if (['submitted', 'rejected', 'failed'].includes(status)) {
- this.emit(`${txMeta.id}:finished`, txMeta)
- }
- this.updateTx(txMeta, `txStateManager: setting status to ${status}`)
- this.emit('update:badge')
+ setTimeout(() => {
+ try {
+ this.updateTx(txMeta, `txStateManager: setting status to ${status}`)
+ this.emit(`${txMeta.id}:${status}`, txId)
+ this.emit(`tx:status-update`, txId, status)
+ if (['submitted', 'rejected', 'failed'].includes(status)) {
+ this.emit(`${txMeta.id}:finished`, txMeta)
+ }
+ this.emit('update:badge')
+ } catch (error) {
+ log.error(error)
+ }
+ })
}
/**
diff --git a/app/scripts/controllers/user-actions.js b/app/scripts/controllers/user-actions.js
new file mode 100644
index 000000000..f777054b8
--- /dev/null
+++ b/app/scripts/controllers/user-actions.js
@@ -0,0 +1,17 @@
+const MessageManager = require('./lib/message-manager')
+const PersonalMessageManager = require('./lib/personal-message-manager')
+const TypedMessageManager = require('./lib/typed-message-manager')
+
+class UserActionController {
+
+ constructor (opts = {}) {
+
+ this.messageManager = new MessageManager()
+ this.personalMessageManager = new PersonalMessageManager()
+ this.typedMessageManager = new TypedMessageManager()
+
+ }
+
+}
+
+module.exports = UserActionController
diff --git a/app/scripts/lib/cleanErrorStack.js b/app/scripts/lib/cleanErrorStack.js
new file mode 100644
index 000000000..fe1bfb0ce
--- /dev/null
+++ b/app/scripts/lib/cleanErrorStack.js
@@ -0,0 +1,24 @@
+/**
+ * Returns error without stack trace for better UI display
+ * @param {Error} err - error
+ * @returns {Error} Error with clean stack trace.
+ */
+function cleanErrorStack(err){
+ var name = err.name
+ name = (name === undefined) ? 'Error' : String(name)
+
+ var msg = err.message
+ msg = (msg === undefined) ? '' : String(msg)
+
+ if (name === '') {
+ err.stack = err.message
+ } else if (msg === '') {
+ err.stack = err.name
+ } else {
+ err.stack = err.name + ': ' + err.message
+ }
+
+ return err
+}
+
+module.exports = cleanErrorStack
diff --git a/app/scripts/lib/createErrorMiddleware.js b/app/scripts/lib/createErrorMiddleware.js
index baed99e45..c70beddfd 100644
--- a/app/scripts/lib/createErrorMiddleware.js
+++ b/app/scripts/lib/createErrorMiddleware.js
@@ -59,6 +59,7 @@ function createErrorMiddleware ({ override = true } = {}) {
if (!error) { return done() }
sanitizeRPCError(error)
log.error(`MetaMask - RPC Error: ${error.message}`, error)
+ done()
})
}
}
diff --git a/app/scripts/lib/diagnostics-reporter.js b/app/scripts/lib/diagnostics-reporter.js
new file mode 100644
index 000000000..aa4ca6e26
--- /dev/null
+++ b/app/scripts/lib/diagnostics-reporter.js
@@ -0,0 +1,71 @@
+class DiagnosticsReporter {
+
+ constructor ({ firstTimeInfo, version }) {
+ this.firstTimeInfo = firstTimeInfo
+ this.version = version
+ }
+
+ async reportOrphans(orphans) {
+ try {
+ return await this.submit({
+ accounts: Object.keys(orphans),
+ metadata: {
+ type: 'orphans',
+ },
+ })
+ } catch (err) {
+ console.error('DiagnosticsReporter - "reportOrphans" encountered an error:')
+ console.error(err)
+ }
+ }
+
+ async reportMultipleKeyrings(rawKeyrings) {
+ try {
+ const keyrings = await Promise.all(rawKeyrings.map(async (keyring, index) => {
+ return {
+ index,
+ type: keyring.type,
+ accounts: await keyring.getAccounts(),
+ }
+ }))
+ return await this.submit({
+ accounts: [],
+ metadata: {
+ type: 'keyrings',
+ keyrings,
+ },
+ })
+ } catch (err) {
+ console.error('DiagnosticsReporter - "reportMultipleKeyrings" encountered an error:')
+ console.error(err)
+ }
+ }
+
+ async submit (message) {
+ try {
+ // add metadata
+ message.metadata.version = this.version
+ message.metadata.firstTimeInfo = this.firstTimeInfo
+ return await postData(message)
+ } catch (err) {
+ console.error('DiagnosticsReporter - "submit" encountered an error:')
+ throw err
+ }
+ }
+
+}
+
+function postData(data) {
+ const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts'
+ return fetch(uri, {
+ body: JSON.stringify(data), // must match 'Content-Type' header
+ credentials: 'same-origin', // include, same-origin, *omit
+ headers: {
+ 'content-type': 'application/json',
+ },
+ method: 'POST', // *GET, POST, PUT, DELETE, etc.
+ mode: 'cors', // no-cors, cors, *same-origin
+ })
+}
+
+module.exports = DiagnosticsReporter
diff --git a/app/scripts/lib/get-first-preferred-lang-code.js b/app/scripts/lib/get-first-preferred-lang-code.js
index 5473fccf0..1e6a83ba6 100644
--- a/app/scripts/lib/get-first-preferred-lang-code.js
+++ b/app/scripts/lib/get-first-preferred-lang-code.js
@@ -2,6 +2,12 @@ const extension = require('extensionizer')
const promisify = require('pify')
const allLocales = require('../../_locales/index.json')
+const isSupported = extension.i18n && extension.i18n.getAcceptLanguages
+const getPreferredLocales = isSupported ? promisify(
+ extension.i18n.getAcceptLanguages,
+ { errorFirst: false }
+) : async () => []
+
const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().replace('_', '-'))
/**
@@ -12,10 +18,7 @@ const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().r
*
*/
async function getFirstPreferredLangCode () {
- const userPreferredLocaleCodes = await promisify(
- extension.i18n.getAcceptLanguages,
- { errorFirst: false }
- )()
+ const userPreferredLocaleCodes = await getPreferredLocales()
const firstPreferredLangCode = userPreferredLocaleCodes
.map(code => code.toLowerCase())
.find(code => existingLocaleCodes.includes(code))
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 2e74b3a20..b206c6b67 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -45,6 +45,8 @@ const BN = require('ethereumjs-util').BN
const GWEI_BN = new BN('1000000000')
const percentile = require('percentile')
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
+const cleanErrorStack = require('./lib/cleanErrorStack')
+const DiagnosticsReporter = require('./lib/diagnostics-reporter')
const log = require('loglevel')
module.exports = class MetamaskController extends EventEmitter {
@@ -63,6 +65,12 @@ module.exports = class MetamaskController extends EventEmitter {
const initState = opts.initState || {}
this.recordFirstTimeInfo(initState)
+ // metamask diagnostics reporter
+ this.diagnostics = opts.diagnostics || new DiagnosticsReporter({
+ firstTimeInfo: initState.firstTimeInfo,
+ version,
+ })
+
// platform-specific api
this.platform = opts.platform
@@ -84,6 +92,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.preferencesController = new PreferencesController({
initState: initState.PreferencesController,
initLangCode: opts.initLangCode,
+ diagnostics: this.diagnostics,
})
// currency controller
@@ -139,6 +148,8 @@ module.exports = class MetamaskController extends EventEmitter {
const address = addresses[0]
this.preferencesController.setSelectedAddress(address)
}
+ // ensure preferences + identities controller know about all addresses
+ this.preferencesController.addAddresses(addresses)
this.accountTracker.syncWithAddresses(addresses)
})
@@ -348,7 +359,7 @@ module.exports = class MetamaskController extends EventEmitter {
importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
// vault management
- submitPassword: nodeify(keyringController.submitPassword, keyringController),
+ submitPassword: nodeify(this.submitPassword, this),
// network management
setProviderType: nodeify(networkController.setProviderType, networkController),
@@ -450,7 +461,11 @@ module.exports = class MetamaskController extends EventEmitter {
async createNewVaultAndRestore (password, seed) {
const release = await this.createVaultMutex.acquire()
try {
+ // clear known identities
+ this.preferencesController.setAddresses([])
+ // create new vault
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
+ // set new identities
const accounts = await this.keyringController.getAccounts()
this.preferencesController.setAddresses(accounts)
this.selectFirstIdentity()
@@ -462,6 +477,28 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ /*
+ * Submits the user's password and attempts to unlock the vault.
+ * Also synchronizes the preferencesController, to ensure its schema
+ * is up to date with known accounts once the vault is decrypted.
+ *
+ * @param {string} password - The user's password
+ * @returns {Promise<object>} - The keyringController update.
+ */
+ async submitPassword (password) {
+ await this.keyringController.submitPassword(password)
+ const accounts = await this.keyringController.getAccounts()
+
+ // verify keyrings
+ const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair')
+ if (nonSimpleKeyrings.length > 1 && this.diagnostics) {
+ await this.diagnostics.reportMultipleKeyrings(nonSimpleKeyrings)
+ }
+
+ await this.preferencesController.syncAddresses(accounts)
+ return this.keyringController.fullUpdate()
+ }
+
/**
* @type Identity
* @property {string} name - The account nickname.
diff --git a/app/scripts/migrations/026.js b/app/scripts/migrations/026.js
index 1b8a91a45..4e907e09c 100644
--- a/app/scripts/migrations/026.js
+++ b/app/scripts/migrations/026.js
@@ -27,7 +27,7 @@ module.exports = {
function transformState (state) {
if (!state.KeyringController || !state.PreferencesController) {
- return
+ return state
}
if (!state.KeyringController.walletNicknames) {
diff --git a/docs/publishing.md b/docs/publishing.md
index 3022b7eda..45662900d 100644
--- a/docs/publishing.md
+++ b/docs/publishing.md
@@ -2,18 +2,32 @@
When publishing a new version of MetaMask, we follow this procedure:
+## Preparation
+
+We try to ensure certain criteria are met before deploying:
+
+- Deploy early in the week, to give time for emergency responses to unforeseen bugs.
+- Deploy early in the day, for the same reason.
+- Make sure at least one member of the support team is "on duty" to watch for new user issues coming through the support system.
+- Roll out incrementally when possible, to a small number of users first, and gradually to more users.
+
## Incrementing Version & Changelog
Version can be automatically incremented [using our bump script](./bumping-version.md).
npm run version:bump $BUMP_TYPE` where `$BUMP_TYPE` is one of `major`, `minor`, or `patch`.
-## Publishing
+## Building
-1. `npm run dist` to generate the latest build.
-2. Publish to chrome store.
- - Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=2).
-3. Publish to firefox addon marketplace.
-4. Post on Github releases page.
-5. `npm run announce`, post that announcement in our public places.
+While we develop on the main `develop` branch, our production version is maintained on the `master` branch.
+
+With each pull request, the @MetaMaskBot will comment with a build of that new pull request, so after bumping the version on `develop`, open a pull request against `master`, and once the pull request is reviewed and merged, you can download those builds for publication.
+
+## Publishing
+1. Publish to chrome store.
+2. Visit [the chrome developer dashboard](https://chrome.google.com/webstore/developer/dashboard?authuser=2).
+3. Publish to [firefox addon marketplace](http://addons.mozilla.org/en-us/firefox/addon/ether-metamask).
+4. Publish to [Opera store](https://addons.opera.com/en/extensions/details/metamask/).
+5. Post on [Github releases](https://github.com/MetaMask/metamask-extension/releases) page.
+6. Run the `npm run announce` script, and post that announcement in our public places.
diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js
index 99d210ed1..6b284f7c5 100644
--- a/mascara/src/app/first-time/create-password-screen.js
+++ b/mascara/src/app/first-time/create-password-screen.js
@@ -143,6 +143,7 @@ class CreatePasswordScreen extends Component {
autoComplete="new-password"
margin="normal"
fullWidth
+ largeLabel
/>
<TextField
id="confirm-password"
@@ -155,6 +156,7 @@ class CreatePasswordScreen extends Component {
autoComplete="confirm-password"
margin="normal"
fullWidth
+ largeLabel
/>
<button
className="first-time-flow__button"
diff --git a/mascara/src/app/first-time/import-seed-phrase-screen.js b/mascara/src/app/first-time/import-seed-phrase-screen.js
index 4fda2bbc1..fd2516ad4 100644
--- a/mascara/src/app/first-time/import-seed-phrase-screen.js
+++ b/mascara/src/app/first-time/import-seed-phrase-screen.js
@@ -146,6 +146,7 @@ class ImportSeedPhraseScreen extends Component {
error={passwordError}
autoComplete="new-password"
margin="normal"
+ largeLabel
/>
<TextField
id="confirm-password"
@@ -157,6 +158,7 @@ class ImportSeedPhraseScreen extends Component {
error={confirmPasswordError}
autoComplete="confirm-password"
margin="normal"
+ largeLabel
/>
<button
className="first-time-flow__button"
diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css
index 25e60b84a..09e7d378d 100644
--- a/mascara/src/app/first-time/index.css
+++ b/mascara/src/app/first-time/index.css
@@ -123,10 +123,6 @@
width: calc(100vw - 80px);
}
- .unique-image {
- width: auto;
- }
-
.create-password__title,
.unique-image__title,
.tou__title,
@@ -148,7 +144,7 @@
height: 100%;
flex-direction: column;
align-items: center;
- justify-content: space-evenly;
+ justify-content: flex-start;
margin-top: 12px;
}
@@ -181,7 +177,6 @@
margin: 0 !important;
padding: 16px 20px !important;
height: 30vh !important;
- width: calc(100% - 48px) !important;
}
.backup-phrase__content-wrapper {
@@ -280,6 +275,12 @@
width: 335px;
}
+@media only screen and (max-width: 575px) {
+ .unique-image__body-text {
+ width: initial;
+ }
+}
+
.unique-image__body-text +
.unique-image__body-text,
.backup-phrase__body-text +
@@ -294,7 +295,7 @@
border-radius: 8px;
background-color: #FFFFFF;
margin: 0 142px 0 0;
- height: 334px;
+ height: 200px;
overflow-y: auto;
color: #757575;
font-family: Roboto;
@@ -679,7 +680,7 @@ button.backup-phrase__confirm-seed-option:hover {
}
.first-time-flow__input {
- width: 350px;
+ max-width: 350px;
}
.first-time-flow__button {
diff --git a/package-lock.json b/package-lock.json
index 2e0bb8184..d8e6f0845 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1500,7 +1500,8 @@
},
"dependencies": {
"bignumber.js": {
- "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
+ "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2",
+ "from": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
},
"chai": {
"version": "3.5.0",
@@ -7149,6 +7150,12 @@
"domelementtype": "1.3.0"
}
},
+ "dot-only-hunter": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/dot-only-hunter/-/dot-only-hunter-1.0.3.tgz",
+ "integrity": "sha1-9k0h7b5v8xFJlfEGGmGpNcMAIEs=",
+ "dev": true
+ },
"dotenv": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
@@ -8248,20 +8255,20 @@
}
},
"eth-keyring-controller": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/eth-keyring-controller/-/eth-keyring-controller-3.1.1.tgz",
- "integrity": "sha512-Z9HTzrop/V4Ld8Wq7uwetKecfWIyx25/uL8aFoZxV3kegZGoXaWoRmNy+4oW0WNLp4BcJ1lk6QfsGEdlymGjmA==",
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/eth-keyring-controller/-/eth-keyring-controller-3.1.4.tgz",
+ "integrity": "sha512-NNlVB/TBc8p9CblwECjPlUR+7MNQKiBa7tEFxIzZ9MjjNCEYPWDXTm0vJZzuDtVmFxYwIA53UD0QEn0QNxWNEQ==",
"requires": {
- "bip39": "2.4.0",
- "bluebird": "3.5.1",
- "browser-passworder": "2.0.3",
- "eth-hd-keyring": "1.2.2",
- "eth-sig-util": "1.4.2",
- "eth-simple-keyring": "1.2.1",
- "ethereumjs-util": "5.2.0",
- "loglevel": "1.6.0",
- "obs-store": "2.4.1",
- "promise-filter": "1.1.0"
+ "bip39": "^2.4.0",
+ "bluebird": "^3.5.0",
+ "browser-passworder": "^2.0.3",
+ "eth-hd-keyring": "^1.2.2",
+ "eth-sig-util": "^1.4.0",
+ "eth-simple-keyring": "^1.2.2",
+ "ethereumjs-util": "^5.1.2",
+ "loglevel": "^1.5.0",
+ "obs-store": "^2.4.1",
+ "promise-filter": "^1.1.0"
},
"dependencies": {
"babelify": {
@@ -8269,8 +8276,8 @@
"resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
"integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
"requires": {
- "babel-core": "6.26.0",
- "object-assign": "4.1.1"
+ "babel-core": "^6.0.14",
+ "object-assign": "^4.0.0"
}
},
"ethereumjs-util": {
@@ -8278,13 +8285,13 @@
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
"integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==",
"requires": {
- "bn.js": "4.11.8",
- "create-hash": "1.1.3",
- "ethjs-util": "0.1.4",
- "keccak": "1.4.0",
- "rlp": "2.0.0",
- "safe-buffer": "5.1.1",
- "secp256k1": "3.4.0"
+ "bn.js": "^4.11.0",
+ "create-hash": "^1.1.2",
+ "ethjs-util": "^0.1.3",
+ "keccak": "^1.0.2",
+ "rlp": "^2.0.0",
+ "safe-buffer": "^5.1.1",
+ "secp256k1": "^3.0.1"
}
},
"obs-store": {
@@ -8292,11 +8299,11 @@
"resolved": "https://registry.npmjs.org/obs-store/-/obs-store-2.4.1.tgz",
"integrity": "sha512-wpA8G4uSn8cnCKZ0pFTvqsamvy0Sm1hR2ot0Qonbfj5yBMwdAp/eD4vDI+U/ZCbV1hb2V5GapL8YKUdGCvahgg==",
"requires": {
- "babel-preset-es2015": "6.24.1",
- "babelify": "7.3.0",
- "readable-stream": "2.3.3",
- "through2": "2.0.3",
- "xtend": "4.0.1"
+ "babel-preset-es2015": "^6.22.0",
+ "babelify": "^7.3.0",
+ "readable-stream": "^2.2.2",
+ "through2": "^2.0.3",
+ "xtend": "^4.0.1"
}
}
}
@@ -8343,6 +8350,7 @@
"dependencies": {
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#4ea2fdfed09e8f99117d9362d17c6b01b64a2bcf",
+ "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"requires": {
"bn.js": "4.11.8",
"ethereumjs-util": "5.1.3"
@@ -8365,15 +8373,15 @@
}
},
"eth-simple-keyring": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/eth-simple-keyring/-/eth-simple-keyring-1.2.1.tgz",
- "integrity": "sha1-bXs1LcWppQINYfafryHvsvY2P0U=",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/eth-simple-keyring/-/eth-simple-keyring-1.2.2.tgz",
+ "integrity": "sha512-uQVBYshHUOaXVoat1BpLA/QNMCr4hgdFBgwIB7rRmQ+m3vQQAseUsOM+biPDYzq6end+6LjcccElLpQaIZe6dg==",
"requires": {
- "eth-sig-util": "1.4.2",
- "ethereumjs-util": "5.2.0",
- "ethereumjs-wallet": "0.6.0",
- "events": "1.1.1",
- "xtend": "4.0.1"
+ "eth-sig-util": "^1.4.2",
+ "ethereumjs-util": "^5.1.1",
+ "ethereumjs-wallet": "^0.6.0",
+ "events": "^1.1.1",
+ "xtend": "^4.0.1"
},
"dependencies": {
"ethereumjs-util": {
@@ -8381,13 +8389,13 @@
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
"integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==",
"requires": {
- "bn.js": "4.11.8",
- "create-hash": "1.1.3",
- "ethjs-util": "0.1.4",
- "keccak": "1.4.0",
- "rlp": "2.0.0",
- "safe-buffer": "5.1.1",
- "secp256k1": "3.4.0"
+ "bn.js": "^4.11.0",
+ "create-hash": "^1.1.2",
+ "ethjs-util": "^0.1.3",
+ "keccak": "^1.0.2",
+ "rlp": "^2.0.0",
+ "safe-buffer": "^5.1.1",
+ "secp256k1": "^3.0.1"
}
}
}
@@ -8574,7 +8582,7 @@
"eth-query": "2.1.2",
"ethereumjs-block": "1.7.0",
"ethereumjs-tx": "1.3.3",
- "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
+ "ethereumjs-util": "^5.0.1",
"ethereumjs-vm": "2.3.5",
"through2": "2.0.3",
"treeify": "1.1.0",
@@ -8681,7 +8689,7 @@
"async": "2.6.0",
"ethereum-common": "0.2.0",
"ethereumjs-tx": "1.3.3",
- "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
+ "ethereumjs-util": "^5.0.0",
"merkle-patricia-tree": "2.3.0"
}
},
@@ -8691,7 +8699,7 @@
"integrity": "sha1-7OBR0+/b53GtKlGNYWMsoqt17Ls=",
"requires": {
"ethereum-common": "0.0.18",
- "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9"
+ "ethereumjs-util": "^5.0.0"
},
"dependencies": {
"ethereum-common": {
@@ -8703,6 +8711,7 @@
},
"ethereumjs-util": {
"version": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
+ "from": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
"requires": {
"bn.js": "4.11.8",
"create-hash": "1.1.3",
@@ -11192,6 +11201,7 @@
"dependencies": {
"async-eventemitter": {
"version": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
+ "from": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
"requires": {
"async": "2.6.0"
}
@@ -11884,6 +11894,7 @@
},
"gulp": {
"version": "github:gulpjs/gulp#71c094a51c7972d26f557899ddecab0210ef3776",
+ "from": "github:gulpjs/gulp#4.0",
"requires": {
"glob-watcher": "4.0.0",
"gulp-cli": "2.0.1",
@@ -15533,6 +15544,7 @@
"dependencies": {
"async-eventemitter": {
"version": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
+ "from": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
"requires": {
"async": "2.6.0"
}
@@ -18307,7 +18319,7 @@
"integrity": "sha512-LKd2OoIT9Re/OG38zXbd5pyHIk2IfcOUczCwkYXl5iJIbufg9nqpweh66VfPwMkUlrEvc7YVvtQdmSrB9V9TkQ==",
"requires": {
"async": "1.5.2",
- "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
+ "ethereumjs-util": "^5.0.0",
"level-ws": "0.0.0",
"levelup": "1.3.9",
"memdown": "1.4.1",
@@ -31329,7 +31341,8 @@
},
"dependencies": {
"bignumber.js": {
- "version": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934"
+ "version": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934",
+ "from": "git+https://github.com/frozeman/bignumber.js-nolookahead.git"
}
}
},
@@ -31687,6 +31700,7 @@
"dependencies": {
"async-eventemitter": {
"version": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
+ "from": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c",
"requires": {
"async": "2.6.0"
}
@@ -31777,6 +31791,7 @@
},
"websocket": {
"version": "git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c",
+ "from": "websocket@git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c",
"requires": {
"debug": "2.6.9",
"nan": "2.8.0",
diff --git a/package.json b/package.json
index 91bf60014..981c5dc55 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"dist": "gulp dist",
"doc": "jsdoc -c development/tools/.jsdoc.json",
"test": "npm run test:unit && npm run test:integration && npm run lint",
- "test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\"",
+ "test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\" && dot-only-hunter",
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
"test:integration:build": "gulp build:scss",
@@ -100,7 +100,7 @@
"eth-json-rpc-filters": "^2.1.1",
"eth-json-rpc-infura": "^3.0.0",
"eth-json-rpc-middleware": "^2.2.2",
- "eth-keyring-controller": "^3.1.1",
+ "eth-keyring-controller": "^3.1.4",
"eth-phishing-detect": "^1.1.4",
"eth-query": "^2.1.2",
"eth-sig-util": "^1.4.2",
@@ -223,6 +223,7 @@
"css-loader": "^0.28.11",
"deep-freeze-strict": "^1.1.1",
"del": "^3.0.0",
+ "dot-only-hunter": "^1.0.3",
"envify": "^4.0.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-15": "^1.0.5",
diff --git a/test/e2e/metamask.spec.js b/test/e2e/metamask.spec.js
index 8ec7de16c..a08a34d96 100644
--- a/test/e2e/metamask.spec.js
+++ b/test/e2e/metamask.spec.js
@@ -121,7 +121,14 @@ describe('Metamask popup page', function () {
await delay(300)
})
+ 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 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()
+ })
+
it('shows account address', async function () {
+ await delay(300)
accountAddress = await driver.findElement(By.css('#app-content > div > div.app-primary.from-left > div > div > div:nth-child(1) > flex-column > div.flex-row > div')).getText()
})
diff --git a/test/integration/lib/add-token.js b/test/integration/lib/add-token.js
index e51c854d2..5a08c90cd 100644
--- a/test/integration/lib/add-token.js
+++ b/test/integration/lib/add-token.js
@@ -43,7 +43,7 @@ async function runAddTokenFlowTest (assert, done) {
assert.equal(addTokenTitle[0].textContent, 'Add Tokens', 'add token title is correct')
// Cancel Add Token
- const cancelAddTokenButton = await queryAsync($, 'button.btn-secondary--lg.page-container__footer-button')
+ const cancelAddTokenButton = await queryAsync($, 'button.btn-default.btn--large.page-container__footer-button')
assert.ok(cancelAddTokenButton[0], 'cancel add token button present')
cancelAddTokenButton.click()
@@ -75,15 +75,15 @@ async function runAddTokenFlowTest (assert, done) {
tokenWrapper[0].click()
// Click Next button
- let nextButton = await queryAsync($, 'button.btn-primary--lg')
+ let nextButton = await queryAsync($, 'button.btn-primary.btn--large')
assert.equal(nextButton[0].textContent, 'Next', 'next button rendered')
nextButton[0].click()
// Confirm Add token
const confirmAddToken = await queryAsync($, '.confirm-add-token')
assert.ok(confirmAddToken[0], 'confirm add token rendered')
- assert.ok($('button.btn-primary--lg')[0], 'confirm add token button found')
- $('button.btn-primary--lg')[0].click()
+ assert.ok($('button.btn-primary.btn--large')[0], 'confirm add token button found')
+ $('button.btn-primary.btn--large')[0].click()
// Verify added token image
let heroBalance = await queryAsync($, '.hero-balance')
@@ -120,7 +120,7 @@ async function runAddTokenFlowTest (assert, done) {
const errorMessage = await queryAsync($, '#custom-symbol-helper-text')
assert.ok(errorMessage[0], 'error rendered')
- $('button.btn-secondary--lg')[0].click()
+ $('button.btn-default.btn--large')[0].click()
// await timeout(100000)
diff --git a/test/integration/lib/confirm-sig-requests.js b/test/integration/lib/confirm-sig-requests.js
index 3936ac5fa..d5ed7c77c 100644
--- a/test/integration/lib/confirm-sig-requests.js
+++ b/test/integration/lib/confirm-sig-requests.js
@@ -38,7 +38,7 @@ async function runConfirmSigRequestsTest(assert, done) {
let confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
assert.equal(confirmSigRowValue[0].textContent, '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0')
- let confirmSigSignButton = await queryAsync($, 'button.btn-primary--lg')
+ let confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large')
confirmSigSignButton[0].click()
confirmSigHeadline = await queryAsync($, '.request-signature__headline')
@@ -47,7 +47,7 @@ async function runConfirmSigRequestsTest(assert, done) {
confirmSigRowValue = await queryAsync($, '.request-signature__row-value')
assert.ok(confirmSigRowValue[0].textContent.match(/^\#\sTerms\sof\sUse/))
- confirmSigSignButton = await queryAsync($, 'button.btn-primary--lg')
+ confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large')
confirmSigSignButton[0].click()
confirmSigHeadline = await queryAsync($, '.request-signature__headline')
@@ -57,7 +57,7 @@ async function runConfirmSigRequestsTest(assert, done) {
assert.equal(confirmSigRowValue[0].textContent, 'Hi, Alice!')
assert.equal(confirmSigRowValue[1].textContent, '1337')
- confirmSigSignButton = await queryAsync($, 'button.btn-primary--lg')
+ confirmSigSignButton = await queryAsync($, 'button.btn-primary.btn--large')
confirmSigSignButton[0].click()
const txView = await queryAsync($, '.tx-view')
diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js
index 3da3f4f95..176907926 100644
--- a/test/integration/lib/send-new-ui.js
+++ b/test/integration/lib/send-new-ui.js
@@ -129,7 +129,7 @@ async function runSendFlowTest(assert, done) {
await customizeGas(assert, 0, 21000, '0', '$0.00 USD')
await customizeGas(assert, 500, 60000, '0.003', '$3.60 USD')
- const sendButton = await queryAsync($, 'button.btn-primary--lg.page-container__footer-button')
+ const sendButton = await queryAsync($, 'button.btn-primary.btn--large.page-container__footer-button')
assert.equal(sendButton[0].textContent, 'Next', 'next button rendered')
sendButton[0].click()
await timeout()
@@ -169,13 +169,13 @@ async function runSendFlowTest(assert, done) {
sendAmountFieldInputInEdit.val('1.0')
reactTriggerChange(sendAmountFieldInputInEdit[0])
- const sendButtonInEdit = await queryAsync($, '.btn-primary--lg.page-container__footer-button')
+ const sendButtonInEdit = await queryAsync($, '.btn-primary.btn--large.page-container__footer-button')
assert.equal(sendButtonInEdit[0].textContent, 'Next', 'next button in edit rendered')
selectState.val('send new ui')
reactTriggerChange(selectState[0])
- const cancelButtonInEdit = await queryAsync($, '.btn-secondary--lg.page-container__footer-button')
+ const cancelButtonInEdit = await queryAsync($, '.btn-default.btn--large.page-container__footer-button')
cancelButtonInEdit[0].click()
// sendButtonInEdit[0].click()
diff --git a/test/screens/new-ui.js b/test/screens/new-ui.js
index 6b873ac85..e3ba7f6ab 100644
--- a/test/screens/new-ui.js
+++ b/test/screens/new-ui.js
@@ -11,9 +11,8 @@ const GIFEncoder = require('gifencoder')
const pngFileStream = require('png-file-stream')
const sizeOfPng = require('image-size/lib/types/png')
const By = webdriver.By
-const { delay, buildWebDriver } = require('./func')
const localesIndex = require('../../app/_locales/index.json')
-// const localesIndex = []
+const { delay, buildChromeWebDriver, buildFirefoxWebdriver, installWebExt, getExtensionIdChrome, getExtensionIdFirefox } = require('../e2e/func')
const eth = new Ethjs(new Ethjs.HttpProvider('http://localhost:8545'))
@@ -50,11 +49,10 @@ async function captureAllScreens() {
await cleanScreenShotDir()
- // setup selenium and install extension
const extPath = path.resolve('dist/chrome')
- driver = buildWebDriver(extPath)
- await driver.get('chrome://extensions-frame')
- const extensionId = await driver.executeScript('return document.querySelector("extensions-manager").shadowRoot.querySelector("extensions-view-manager extensions-item-list").shadowRoot.querySelector("#container > div.items-container > extensions-item:nth-child(2)").getAttribute("id")')
+ driver = buildChromeWebDriver(extPath)
+ const extensionId = await getExtensionIdChrome(driver)
+
await driver.get(`chrome-extension://${extensionId}/home.html`)
await delay(500)
tabs = await driver.getAllWindowHandles()
@@ -165,7 +163,7 @@ async function captureAllScreens() {
await delay(300)
await captureLanguageScreenShots('metamask account detail export private key screen - password entered')
- await driver.findElement(By.css('.btn-primary--lg.export-private-key__button')).click()
+ await driver.findElement(By.css('.btn-primary.btn--large.export-private-key__button')).click()
await delay(300)
await captureLanguageScreenShots('metamask account detail export private key screen - reveal key')
diff --git a/test/unit/app/controllers/metamask-controller-test.js b/test/unit/app/controllers/metamask-controller-test.js
index 1b2c95f48..13d74bce1 100644
--- a/test/unit/app/controllers/metamask-controller-test.js
+++ b/test/unit/app/controllers/metamask-controller-test.js
@@ -47,7 +47,7 @@ describe('MetaMaskController', function () {
encryptor: {
encrypt: function (password, object) {
this.object = object
- return Promise.resolve()
+ return Promise.resolve('mock-encrypted')
},
decrypt: function () {
return Promise.resolve(this.object)
@@ -64,6 +64,31 @@ describe('MetaMaskController', function () {
sandbox.restore()
})
+ describe('submitPassword', function () {
+ const password = 'password'
+
+ beforeEach(async function () {
+ await metamaskController.createNewVaultAndKeychain(password)
+ })
+
+ it('removes any identities that do not correspond to known accounts.', async function () {
+ const fakeAddress = '0xbad0'
+ metamaskController.preferencesController.addAddresses([fakeAddress])
+ await metamaskController.submitPassword(password)
+
+ const identities = Object.keys(metamaskController.preferencesController.store.getState().identities)
+ const addresses = await metamaskController.keyringController.getAccounts()
+
+ identities.forEach((identity) => {
+ assert.ok(addresses.includes(identity), `addresses should include all IDs: ${identity}`)
+ })
+
+ addresses.forEach((address) => {
+ assert.ok(identities.includes(address), `identities should include all Addresses: ${address}`)
+ })
+ })
+ })
+
describe('#getGasPrice', function () {
it('gives the 50th percentile lowest accepted gas price from recentBlocksController', async function () {
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 894e31fde..a9372d6f3 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -503,17 +503,23 @@ function requestRevealSeedWords (password) {
}
function resetAccount () {
- return (dispatch) => {
- background.resetAccount((err, account) => {
- dispatch(actions.hideLoadingIndication())
- if (err) {
- dispatch(actions.displayWarning(err.message))
- }
+ return dispatch => {
+ dispatch(actions.showLoadingIndication())
- log.info('Transaction history reset for ' + account)
- dispatch(actions.showAccountsPage())
- })
- }
+ return new Promise((resolve, reject) => {
+ background.resetAccount((err, account) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
+
+ log.info('Transaction history reset for ' + account)
+ dispatch(actions.showAccountsPage())
+ resolve(account)
+ })
+ })
+ }
}
function addNewKeyring (type, opts) {
@@ -1397,16 +1403,24 @@ function markAccountsFound () {
function retryTransaction (txId) {
log.debug(`background.retryTransaction`)
+ let newTxId
+
return (dispatch) => {
- background.retryTransaction(txId, (err, newState) => {
- if (err) {
- return dispatch(actions.displayWarning(err.message))
- }
- const { selectedAddressTxList } = newState
- const { id: newTxId } = selectedAddressTxList[selectedAddressTxList.length - 1]
- dispatch(actions.updateMetamaskState(newState))
- dispatch(actions.viewPendingTx(newTxId))
+ return new Promise((resolve, reject) => {
+ background.retryTransaction(txId, (err, newState) => {
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ reject(err)
+ }
+
+ const { selectedAddressTxList } = newState
+ const { id } = selectedAddressTxList[selectedAddressTxList.length - 1]
+ newTxId = id
+ resolve(newState)
+ })
})
+ .then(newState => dispatch(actions.updateMetamaskState(newState)))
+ .then(() => newTxId)
}
}
diff --git a/ui/app/app.js b/ui/app/app.js
index 3d2961340..0e8b907df 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -76,7 +76,7 @@ class App extends Component {
h(Authenticated, { path: REVEAL_SEED_ROUTE, exact, component: RevealSeedConfirmation }),
h(Authenticated, { path: SETTINGS_ROUTE, component: Settings }),
h(Authenticated, { path: NOTICE_ROUTE, exact, component: NoticeScreen }),
- h(Authenticated, { path: CONFIRM_TRANSACTION_ROUTE, component: ConfirmTxScreen }),
+ h(Authenticated, { path: `${CONFIRM_TRANSACTION_ROUTE}/:id?`, component: ConfirmTxScreen }),
h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen2 }),
h(Authenticated, { path: ADD_TOKEN_ROUTE, exact, component: AddTokenPage }),
h(Authenticated, { path: CONFIRM_ADD_TOKEN_ROUTE, exact, component: ConfirmAddTokenPage }),
@@ -137,7 +137,6 @@ class App extends Component {
(isLoading || isLoadingNetwork) && h(Loading, {
loadingMessage: loadMessage,
- fullScreen: true,
}),
// content
diff --git a/ui/app/components/button/button.component.js b/ui/app/components/button/button.component.js
index fe3bf363c..e8e798445 100644
--- a/ui/app/components/button/button.component.js
+++ b/ui/app/components/button/button.component.js
@@ -2,20 +2,15 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
-const SECONDARY = 'secondary'
+const CLASSNAME_DEFAULT = 'btn-default'
const CLASSNAME_PRIMARY = 'btn-primary'
-const CLASSNAME_PRIMARY_LARGE = 'btn-primary--lg'
const CLASSNAME_SECONDARY = 'btn-secondary'
-const CLASSNAME_SECONDARY_LARGE = 'btn-secondary--lg'
+const CLASSNAME_LARGE = 'btn--large'
-const getClassName = (type, large = false) => {
- let output = type === SECONDARY ? CLASSNAME_SECONDARY : CLASSNAME_PRIMARY
-
- if (large) {
- output += ` ${type === SECONDARY ? CLASSNAME_SECONDARY_LARGE : CLASSNAME_PRIMARY_LARGE}`
- }
-
- return output
+const typeHash = {
+ default: CLASSNAME_DEFAULT,
+ primary: CLASSNAME_PRIMARY,
+ secondary: CLASSNAME_SECONDARY,
}
class Button extends Component {
@@ -24,7 +19,11 @@ class Button extends Component {
return (
<button
- className={classnames(getClassName(type, large), className)}
+ className={classnames(
+ typeHash[type],
+ large && CLASSNAME_LARGE,
+ className
+ )}
{ ...buttonProps }
>
{ this.props.children }
diff --git a/ui/app/components/button/button.stories.js b/ui/app/components/button/button.stories.js
index d1e14e869..dec084a25 100644
--- a/ui/app/components/button/button.stories.js
+++ b/ui/app/components/button/button.stories.js
@@ -13,13 +13,21 @@ storiesOf('Button', module)
{text('text', 'Click me')}
</Button>
)
- .add('secondary', () => (
+ .add('secondary', () =>
<Button
onClick={action('clicked')}
type="secondary"
>
{text('text', 'Click me')}
</Button>
+ )
+ .add('default', () => (
+ <Button
+ onClick={action('clicked')}
+ type="default"
+ >
+ {text('text', 'Click me')}
+ </Button>
))
.add('large primary', () => (
<Button
@@ -39,3 +47,12 @@ storiesOf('Button', module)
{text('text', 'Click me')}
</Button>
))
+ .add('large default', () => (
+ <Button
+ onClick={action('clicked')}
+ type="default"
+ large
+ >
+ {text('text', 'Click me')}
+ </Button>
+ ))
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index 1ff8eea87..e3529041b 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -308,7 +308,7 @@ CustomizeGasModal.prototype.render = function () {
}, [this.context.t('revert')]),
h('div.send-v2__customize-gas__buttons', [
- h('button.btn-secondary.send-v2__customize-gas__cancel', {
+ h('button.btn-default.send-v2__customize-gas__cancel', {
onClick: this.props.hideModal,
style: {
marginRight: '10px',
diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss
index f3fe823f8..351640f6e 100644
--- a/ui/app/components/index.scss
+++ b/ui/app/components/index.scss
@@ -1,5 +1,9 @@
@import './export-text-container/index';
+@import './selected-account/index';
+
@import './info-box/index';
@import './pages/index';
+
+@import './modals/index';
diff --git a/ui/app/components/loading-screen/loading-screen.component.js b/ui/app/components/loading-screen/loading-screen.component.js
index bce2a4aac..6b843cfee 100644
--- a/ui/app/components/loading-screen/loading-screen.component.js
+++ b/ui/app/components/loading-screen/loading-screen.component.js
@@ -1,7 +1,6 @@
const { Component } = require('react')
const h = require('react-hyperscript')
const PropTypes = require('prop-types')
-const classnames = require('classnames')
const Spinner = require('../spinner')
class LoadingScreen extends Component {
@@ -12,9 +11,7 @@ class LoadingScreen extends Component {
render () {
return (
- h('.loading-overlay', {
- className: classnames({ 'loading-overlay--full-screen': this.props.fullScreen }),
- }, [
+ h('.loading-overlay', [
h('.loading-overlay__container', [
h(Spinner, {
color: '#F7C06C',
@@ -29,7 +26,6 @@ class LoadingScreen extends Component {
LoadingScreen.propTypes = {
loadingMessage: PropTypes.string,
- fullScreen: PropTypes.bool,
}
module.exports = LoadingScreen
diff --git a/ui/app/components/modals/confirm-reset-account/confirm-reset-account.component.js b/ui/app/components/modals/confirm-reset-account/confirm-reset-account.component.js
new file mode 100644
index 000000000..14a4da62a
--- /dev/null
+++ b/ui/app/components/modals/confirm-reset-account/confirm-reset-account.component.js
@@ -0,0 +1,54 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import Button from '../../button'
+
+class ConfirmResetAccount extends Component {
+ static propTypes = {
+ hideModal: PropTypes.func.isRequired,
+ resetAccount: PropTypes.func.isRequired,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ handleReset () {
+ this.props.resetAccount()
+ .then(() => this.props.hideModal())
+ }
+
+ render () {
+ const { t } = this.context
+
+ return (
+ <div className="modal-container">
+ <div className="modal-container__content">
+ <div className="modal-container__title">
+ { `${t('resetAccount')}?` }
+ </div>
+ <div className="modal-container__description">
+ { t('resetAccountDescription') }
+ </div>
+ </div>
+ <div className="modal-container__footer">
+ <Button
+ type="default"
+ className="modal-container__footer-button"
+ onClick={() => this.props.hideModal()}
+ >
+ { t('nevermind') }
+ </Button>
+ <Button
+ type="secondary"
+ className="modal-container__footer-button"
+ onClick={() => this.handleReset()}
+ >
+ { t('reset') }
+ </Button>
+ </div>
+ </div>
+ )
+ }
+}
+
+export default ConfirmResetAccount
diff --git a/ui/app/components/modals/confirm-reset-account/confirm-reset-account.container.js b/ui/app/components/modals/confirm-reset-account/confirm-reset-account.container.js
new file mode 100644
index 000000000..9630a5593
--- /dev/null
+++ b/ui/app/components/modals/confirm-reset-account/confirm-reset-account.container.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import ConfirmResetAccount from './confirm-reset-account.component'
+
+const { hideModal, resetAccount } = require('../../../actions')
+
+const mapDispatchToProps = dispatch => {
+ return {
+ hideModal: () => dispatch(hideModal()),
+ resetAccount: () => dispatch(resetAccount()),
+ }
+}
+
+export default connect(null, mapDispatchToProps)(ConfirmResetAccount)
diff --git a/ui/app/components/modals/confirm-reset-account/index.js b/ui/app/components/modals/confirm-reset-account/index.js
new file mode 100644
index 000000000..c812ffc55
--- /dev/null
+++ b/ui/app/components/modals/confirm-reset-account/index.js
@@ -0,0 +1,2 @@
+import ConfirmResetAccount from './confirm-reset-account.container'
+module.exports = ConfirmResetAccount
diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js
index ad5f9b695..2daa7fa1d 100644
--- a/ui/app/components/modals/deposit-ether-modal.js
+++ b/ui/app/components/modals/deposit-ether-modal.js
@@ -109,7 +109,7 @@ DepositEtherModal.prototype.renderRow = function ({
]),
!hideButton && h('div.deposit-ether-modal__buy-row__button', [
- h('button.btn-primary--lg.deposit-ether-modal__deposit-button', {
+ h('button.btn-primary.btn--large.deposit-ether-modal__deposit-button', {
onClick: onButtonClick,
}, [buttonLabel]),
]),
diff --git a/ui/app/components/modals/edit-account-name-modal.js b/ui/app/components/modals/edit-account-name-modal.js
index 5681a3cad..edced8725 100644
--- a/ui/app/components/modals/edit-account-name-modal.js
+++ b/ui/app/components/modals/edit-account-name-modal.js
@@ -9,7 +9,7 @@ const { getSelectedAccount } = require('../../selectors')
function mapStateToProps (state) {
return {
selectedAccount: getSelectedAccount(state),
- identity: state.appState.modal.modalState.identity,
+ identity: state.appState.modal.modalState.props.identity,
}
}
diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js
index 447e43b7a..80ece425f 100644
--- a/ui/app/components/modals/export-private-key-modal.js
+++ b/ui/app/components/modals/export-private-key-modal.js
@@ -87,14 +87,14 @@ ExportPrivateKeyModal.prototype.renderButton = function (className, onClick, lab
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) {
return h('div.export-private-key-buttons', {}, [
!privateKey && this.renderButton(
- 'btn-secondary--lg export-private-key__button export-private-key__button--cancel',
+ 'btn-default btn--large export-private-key__button export-private-key__button--cancel',
() => hideModal(),
'Cancel'
),
(privateKey
- ? this.renderButton('btn-primary--lg export-private-key__button', () => hideModal(), this.context.t('done'))
- : this.renderButton('btn-primary--lg export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), this.context.t('confirm'))
+ ? this.renderButton('btn-primary btn--large export-private-key__button', () => hideModal(), this.context.t('done'))
+ : this.renderButton('btn-primary btn--large export-private-key__button', () => this.exportAccountAndGetPrivateKey(this.state.password, address), this.context.t('confirm'))
),
])
diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js
index 72e9c84eb..1518fa9a0 100644
--- a/ui/app/components/modals/hide-token-confirmation-modal.js
+++ b/ui/app/components/modals/hide-token-confirmation-modal.js
@@ -9,7 +9,7 @@ const Identicon = require('../identicon')
function mapStateToProps (state) {
return {
network: state.metamask.network,
- token: state.appState.modal.modalState.token,
+ token: state.appState.modal.modalState.props.token,
}
}
diff --git a/ui/app/components/modals/index.scss b/ui/app/components/modals/index.scss
new file mode 100644
index 000000000..ad6fe16d3
--- /dev/null
+++ b/ui/app/components/modals/index.scss
@@ -0,0 +1,52 @@
+.modal-container {
+ width: 100%;
+ height: 100%;
+ background-color: #fff;
+ display: flex;
+ flex-flow: column;
+ border-radius: 8px;
+
+ &__title {
+ font-size: 1.5rem;
+ font-weight: 500;
+ padding: 16px 0;
+ text-align: center;
+ }
+
+ &__description {
+ text-align: center;
+ font-size: .875rem;
+ }
+
+ &__content {
+ overflow-y: auto;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 32px;
+
+ @media screen and (max-width: 575px) {
+ justify-content: center;
+ padding: 28px 20px;
+ }
+ }
+
+ &__footer {
+ display: flex;
+ flex-flow: row;
+ justify-content: center;
+ border-top: 1px solid #d2d8dd;
+ padding: 16px;
+ flex: 0 0 auto;
+
+ &-button {
+ min-width: 0;
+ margin-right: 16px;
+
+ &:last-of-type {
+ margin-right: 0;
+ }
+ }
+ }
+}
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index 43dcd20ae..85e85597a 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -19,7 +19,30 @@ const ShapeshiftDepositTxModal = require('./shapeshift-deposit-tx-modal.js')
const HideTokenConfirmationModal = require('./hide-token-confirmation-modal')
const CustomizeGasModal = require('../customize-gas-modal')
const NotifcationModal = require('./notification-modal')
-const ConfirmResetAccount = require('./notification-modals/confirm-reset-account')
+const ConfirmResetAccount = require('./confirm-reset-account')
+const TransactionConfirmed = require('./transaction-confirmed')
+const WelcomeBeta = require('./welcome-beta')
+const Notification = require('./notification')
+
+const modalContainerBaseStyle = {
+ transform: 'translate3d(-50%, 0, 0px)',
+ border: '1px solid #CCCFD1',
+ borderRadius: '8px',
+ backgroundColor: '#FFFFFF',
+ boxShadow: '0 2px 22px 0 rgba(0,0,0,0.2)',
+}
+
+const modalContainerLaptopStyle = {
+ ...modalContainerBaseStyle,
+ width: '344px',
+ top: '15%',
+}
+
+const modalContainerMobileStyle = {
+ ...modalContainerBaseStyle,
+ width: '309px',
+ top: '12.5%',
+}
const accountModalStyle = {
mobileModalStyle: {
@@ -173,18 +196,18 @@ const MODALS = {
BETA_UI_NOTIFICATION_MODAL: {
contents: [
- h(NotifcationModal, {
- header: 'uiWelcome',
- message: 'uiWelcomeMessage',
- }),
+ h(Notification, [
+ h(WelcomeBeta),
+ ]),
],
mobileModalStyle: {
- width: '95%',
- top: getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP ? '52vh' : '36.5vh',
+ ...modalContainerMobileStyle,
},
laptopModalStyle: {
- width: '449px',
- top: 'calc(33% + 45px)',
+ ...modalContainerLaptopStyle,
+ },
+ contentStyle: {
+ borderRadius: '8px',
},
},
@@ -208,12 +231,13 @@ const MODALS = {
CONFIRM_RESET_ACCOUNT: {
contents: h(ConfirmResetAccount),
mobileModalStyle: {
- width: '95%',
- top: getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP ? '52vh' : '36.5vh',
+ ...modalContainerMobileStyle,
},
laptopModalStyle: {
- width: '473px',
- top: 'calc(33% + 45px)',
+ ...modalContainerLaptopStyle,
+ },
+ contentStyle: {
+ borderRadius: '8px',
},
},
@@ -265,6 +289,24 @@ const MODALS = {
},
},
+ TRANSACTION_CONFIRMED: {
+ disableBackdropClick: true,
+ contents: [
+ h(Notification, [
+ h(TransactionConfirmed),
+ ]),
+ ],
+ mobileModalStyle: {
+ ...modalContainerMobileStyle,
+ },
+ laptopModalStyle: {
+ ...modalContainerLaptopStyle,
+ },
+ contentStyle: {
+ borderRadius: '8px',
+ },
+ },
+
DEFAULT: {
contents: [],
mobileModalStyle: {},
@@ -306,7 +348,7 @@ module.exports = connect(mapStateToProps, mapDispatchToProps)(Modal)
Modal.prototype.render = function () {
const modal = MODALS[this.props.modalState.name || 'DEFAULT']
- const children = modal.contents
+ const { contents: children, disableBackdropClick = false } = modal
const modalStyle = modal[isMobileView() ? 'mobileModalStyle' : 'laptopModalStyle']
const contentStyle = modal.contentStyle || {}
@@ -326,6 +368,7 @@ Modal.prototype.render = function () {
modalStyle,
contentStyle,
backdropStyle: BACKDROPSTYLE,
+ closeOnClick: !disableBackdropClick,
},
children,
)
diff --git a/ui/app/components/modals/notification-modals/confirm-reset-account.js b/ui/app/components/modals/notification-modals/confirm-reset-account.js
deleted file mode 100644
index 89fa9bef1..000000000
--- a/ui/app/components/modals/notification-modals/confirm-reset-account.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const { Component } = require('react')
-const PropTypes = require('prop-types')
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const actions = require('../../../actions')
-const NotifcationModal = require('../notification-modal')
-
-class ConfirmResetAccount extends Component {
- render () {
- const { resetAccount } = this.props
-
- return h(NotifcationModal, {
- header: 'Are you sure you want to reset account?',
- message: h('div', [
-
- h('span', `Resetting is for developer use only. This button wipes the current account's transaction history,
- which is used to calculate the current account nonce. `),
-
- h('a.notification-modal__link', {
- href: 'http://metamask.helpscoutdocs.com/article/36-resetting-an-account',
- target: '_blank',
- onClick (event) { global.platform.openWindow({ url: event.target.href }) },
- }, 'Read more.'),
-
- ]),
- showCancelButton: true,
- showConfirmButton: true,
- onConfirm: resetAccount,
-
- })
- }
-}
-
-ConfirmResetAccount.propTypes = {
- resetAccount: PropTypes.func,
-}
-
-const mapDispatchToProps = dispatch => {
- return {
- resetAccount: () => {
- dispatch(actions.resetAccount())
- },
- }
-}
-
-module.exports = connect(null, mapDispatchToProps)(ConfirmResetAccount)
diff --git a/ui/app/components/modals/notification/index.js b/ui/app/components/modals/notification/index.js
new file mode 100644
index 000000000..d60a3129b
--- /dev/null
+++ b/ui/app/components/modals/notification/index.js
@@ -0,0 +1,2 @@
+import Notification from './notification.container'
+module.exports = Notification
diff --git a/ui/app/components/modals/notification/notification.component.js b/ui/app/components/modals/notification/notification.component.js
new file mode 100644
index 000000000..1af2f3ca8
--- /dev/null
+++ b/ui/app/components/modals/notification/notification.component.js
@@ -0,0 +1,30 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import Button from '../../button'
+
+const Notification = (props, context) => {
+ return (
+ <div className="modal-container">
+ { props.children }
+ <div className="modal-container__footer">
+ <Button
+ type="primary"
+ onClick={() => props.onHide()}
+ >
+ { context.t('ok') }
+ </Button>
+ </div>
+ </div>
+ )
+}
+
+Notification.propTypes = {
+ onHide: PropTypes.func.isRequired,
+ children: PropTypes.element,
+}
+
+Notification.contextTypes = {
+ t: PropTypes.func,
+}
+
+export default Notification
diff --git a/ui/app/components/modals/notification/notification.container.js b/ui/app/components/modals/notification/notification.container.js
new file mode 100644
index 000000000..5b98714da
--- /dev/null
+++ b/ui/app/components/modals/notification/notification.container.js
@@ -0,0 +1,38 @@
+import { connect } from 'react-redux'
+import Notification from './notification.component'
+
+const { hideModal } = require('../../../actions')
+
+const mapStateToProps = state => {
+ const { appState: { modal: { modalState: { props } } } } = state
+ const { onHide } = props
+ return {
+ onHide,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ hideModal: () => dispatch(hideModal()),
+ }
+}
+
+const mergeProps = (stateProps, dispatchProps, ownProps) => {
+ const { onHide, ...otherStateProps } = stateProps
+ const { hideModal, ...otherDispatchProps } = dispatchProps
+
+ return {
+ ...otherStateProps,
+ ...otherDispatchProps,
+ ...ownProps,
+ onHide: () => {
+ hideModal()
+
+ if (onHide && typeof onHide === 'function') {
+ onHide()
+ }
+ },
+ }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(Notification)
diff --git a/ui/app/components/modals/shapeshift-deposit-tx-modal.js b/ui/app/components/modals/shapeshift-deposit-tx-modal.js
index 24af5a0de..242c7b89d 100644
--- a/ui/app/components/modals/shapeshift-deposit-tx-modal.js
+++ b/ui/app/components/modals/shapeshift-deposit-tx-modal.js
@@ -8,7 +8,7 @@ const AccountModalContainer = require('./account-modal-container')
function mapStateToProps (state) {
return {
- Qr: state.appState.modal.modalState.Qr,
+ Qr: state.appState.modal.modalState.props.Qr,
}
}
diff --git a/ui/app/components/modals/transaction-confirmed/index.js b/ui/app/components/modals/transaction-confirmed/index.js
new file mode 100644
index 000000000..cee8da7f8
--- /dev/null
+++ b/ui/app/components/modals/transaction-confirmed/index.js
@@ -0,0 +1,2 @@
+import TransactionConfirmed from './transaction-confirmed.component'
+module.exports = TransactionConfirmed
diff --git a/ui/app/components/modals/transaction-confirmed/transaction-confirmed.component.js b/ui/app/components/modals/transaction-confirmed/transaction-confirmed.component.js
new file mode 100644
index 000000000..c1c8a2976
--- /dev/null
+++ b/ui/app/components/modals/transaction-confirmed/transaction-confirmed.component.js
@@ -0,0 +1,24 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+const TransactionConfirmed = (props, context) => {
+ const { t } = context
+
+ return (
+ <div className="modal-container__content">
+ <img src="images/check-icon.svg" />
+ <div className="modal-container__title">
+ { `${t('confirmed')}!` }
+ </div>
+ <div className="modal-container__description">
+ { t('initialTransactionConfirmed') }
+ </div>
+ </div>
+ )
+}
+
+TransactionConfirmed.contextTypes = {
+ t: PropTypes.func,
+}
+
+export default TransactionConfirmed
diff --git a/ui/app/components/modals/welcome-beta/index.js b/ui/app/components/modals/welcome-beta/index.js
new file mode 100644
index 000000000..515c9cdaf
--- /dev/null
+++ b/ui/app/components/modals/welcome-beta/index.js
@@ -0,0 +1,2 @@
+import WelcomeBeta from './welcome-beta.component'
+module.exports = WelcomeBeta
diff --git a/ui/app/components/modals/welcome-beta/welcome-beta.component.js b/ui/app/components/modals/welcome-beta/welcome-beta.component.js
new file mode 100644
index 000000000..61571723a
--- /dev/null
+++ b/ui/app/components/modals/welcome-beta/welcome-beta.component.js
@@ -0,0 +1,23 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+const TransactionConfirmed = (props, context) => {
+ const { t } = context
+
+ return (
+ <div className="modal-container__content">
+ <div className="modal-container__title">
+ { `${t('uiWelcome')}` }
+ </div>
+ <div className="modal-container__description">
+ { t('uiWelcomeMessage') }
+ </div>
+ </div>
+ )
+}
+
+TransactionConfirmed.contextTypes = {
+ t: PropTypes.func,
+}
+
+export default TransactionConfirmed
diff --git a/ui/app/components/pages/add-token/add-token.component.js b/ui/app/components/pages/add-token/add-token.component.js
index 0677b4317..1f4b37b53 100644
--- a/ui/app/components/pages/add-token/add-token.component.js
+++ b/ui/app/components/pages/add-token/add-token.component.js
@@ -323,7 +323,7 @@ class AddToken extends Component {
</div>
<div className="page-container__footer">
<Button
- type="secondary"
+ type="default"
large
className="page-container__footer-button"
onClick={() => {
diff --git a/ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss b/ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss
index 9d0f4be32..cc495dfb0 100644
--- a/ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss
+++ b/ui/app/components/pages/add-token/token-list/token-list-placeholder/index.scss
@@ -11,6 +11,10 @@
width: 50%;
text-align: center;
margin-top: 8px;
+
+ @media screen and (max-width: 575px) {
+ width: 60%;
+ }
}
&__link {
diff --git a/ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js b/ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js
index abd599b26..1611f817b 100644
--- a/ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js
+++ b/ui/app/components/pages/add-token/token-list/token-list-placeholder/token-list-placeholder.component.js
@@ -15,7 +15,7 @@ export default class TokenListPlaceholder extends Component {
</div>
<a
className="token-list-placeholder__link"
- href="http://metamask.helpscoutdocs.com/article/16-managing-erc20-tokens"
+ href="https://consensys.zendesk.com/hc/en-us/articles/360004135092"
target="_blank"
rel="noopener noreferrer"
>
diff --git a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js
index 9db9efc37..65d654b92 100644
--- a/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js
+++ b/ui/app/components/pages/confirm-add-token/confirm-add-token.component.js
@@ -87,7 +87,7 @@ export default class ConfirmAddToken extends Component {
</div>
<div className="page-container__footer">
<Button
- type="secondary"
+ type="default"
large
className="page-container__footer-button"
onClick={() => history.push(ADD_TOKEN_ROUTE)}
diff --git a/ui/app/components/pages/create-account/import-account/index.js b/ui/app/components/pages/create-account/import-account/index.js
index 52d3dcde9..e2e973af9 100644
--- a/ui/app/components/pages/create-account/import-account/index.js
+++ b/ui/app/components/pages/create-account/import-account/index.js
@@ -46,7 +46,7 @@ AccountImportSubview.prototype.render = function () {
},
onClick: () => {
global.platform.openWindow({
- url: 'https://metamask.helpscoutdocs.com/article/17-what-are-loose-accounts',
+ url: 'https://consensys.zendesk.com/hc/en-us/articles/360004180111-What-are-imported-accounts-New-UI',
})
},
}, this.context.t('here')),
diff --git a/ui/app/components/pages/create-account/import-account/json.js b/ui/app/components/pages/create-account/import-account/json.js
index 0a3314b2a..0164417ec 100644
--- a/ui/app/components/pages/create-account/import-account/json.js
+++ b/ui/app/components/pages/create-account/import-account/json.js
@@ -51,7 +51,7 @@ class JsonImportSubview extends Component {
h('div.new-account-create-form__buttons', {}, [
- h('button.btn-secondary.new-account-create-form__button', {
+ h('button.btn-default.new-account-create-form__button', {
onClick: () => this.props.history.push(DEFAULT_ROUTE),
}, [
this.context.t('cancel'),
@@ -105,7 +105,7 @@ class JsonImportSubview extends Component {
}
this.props.importNewJsonAccount([ fileContents, password ])
- // JS runtime requires caught rejections but failures are handled by Redux
+ // JS runtime requires caught rejections but failures are handled by Redux
.catch()
}
}
diff --git a/ui/app/components/pages/create-account/import-account/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js
index df7ac910a..e55a47a3c 100644
--- a/ui/app/components/pages/create-account/import-account/private-key.js
+++ b/ui/app/components/pages/create-account/import-account/private-key.js
@@ -59,13 +59,13 @@ PrivateKeyImportView.prototype.render = function () {
h('div.new-account-import-form__buttons', {}, [
- h('button.btn-secondary--lg.new-account-create-form__button', {
+ h('button.btn-default.btn--large.new-account-create-form__button', {
onClick: () => this.props.history.push(DEFAULT_ROUTE),
}, [
this.context.t('cancel'),
]),
- h('button.btn-primary--lg.new-account-create-form__button', {
+ h('button.btn-primary.btn--large.new-account-create-form__button', {
onClick: () => this.createNewKeychain(),
}, [
this.context.t('import'),
@@ -91,7 +91,7 @@ PrivateKeyImportView.prototype.createNewKeychain = function () {
const { importNewAccount, history } = this.props
importNewAccount('Private Key', [ privateKey ])
- // JS runtime requires caught rejections but failures are handled by Redux
+ // JS runtime requires caught rejections but failures are handled by Redux
.catch()
.then(() => history.push(DEFAULT_ROUTE))
}
diff --git a/ui/app/components/pages/create-account/new-account.js b/ui/app/components/pages/create-account/new-account.js
index 03a5ee72d..9c94990e0 100644
--- a/ui/app/components/pages/create-account/new-account.js
+++ b/ui/app/components/pages/create-account/new-account.js
@@ -38,13 +38,13 @@ class NewAccountCreateForm extends Component {
h('div.new-account-create-form__buttons', {}, [
- h('button.btn-secondary--lg.new-account-create-form__button', {
+ h('button.btn-default.btn--large.new-account-create-form__button', {
onClick: () => history.push(DEFAULT_ROUTE),
}, [
this.context.t('cancel'),
]),
- h('button.btn-primary--lg.new-account-create-form__button', {
+ h('button.btn-primary.btn--large.new-account-create-form__button', {
onClick: () => {
createAccount(newAccountName || defaultAccountName)
.then(() => history.push(DEFAULT_ROUTE))
diff --git a/ui/app/components/pages/keychains/reveal-seed.js b/ui/app/components/pages/keychains/reveal-seed.js
index 685c81074..7d7d3f462 100644
--- a/ui/app/components/pages/keychains/reveal-seed.js
+++ b/ui/app/components/pages/keychains/reveal-seed.js
@@ -106,10 +106,10 @@ class RevealSeedPage extends Component {
renderPasswordPromptFooter () {
return (
h('.page-container__footer', [
- h('button.btn-secondary--lg.page-container__footer-button', {
+ h('button.btn-default.btn--large.page-container__footer-button', {
onClick: () => this.props.history.push(DEFAULT_ROUTE),
}, this.context.t('cancel')),
- h('button.btn-primary--lg.page-container__footer-button', {
+ h('button.btn-primary.btn--large.page-container__footer-button', {
onClick: event => this.handleSubmit(event),
disabled: this.state.password === '',
}, this.context.t('next')),
@@ -120,7 +120,7 @@ class RevealSeedPage extends Component {
renderRevealSeedFooter () {
return (
h('.page-container__footer', [
- h('button.btn-secondary--lg.page-container__footer-button', {
+ h('button.btn-default.btn--large.page-container__footer-button', {
onClick: () => this.props.history.push(DEFAULT_ROUTE),
}, this.context.t('close')),
])
diff --git a/ui/app/components/pages/settings/settings.js b/ui/app/components/pages/settings/settings.js
index f58ac7ddf..ff42a13de 100644
--- a/ui/app/components/pages/settings/settings.js
+++ b/ui/app/components/pages/settings/settings.js
@@ -217,7 +217,7 @@ class Settings extends Component {
]),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.btn-primary--lg.settings__button', {
+ h('button.btn-primary.btn--large.settings__button', {
onClick (event) {
window.logStateString((err, result) => {
if (err) {
@@ -242,7 +242,7 @@ class Settings extends Component {
h('div.settings__content-item', this.context.t('revealSeedWords')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.btn-primary--lg.settings__button--red', {
+ h('button.btn-primary.btn--large.settings__button--red', {
onClick: event => {
event.preventDefault()
history.push(REVEAL_SEED_ROUTE)
@@ -262,7 +262,7 @@ class Settings extends Component {
h('div.settings__content-item', this.context.t('useOldUI')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.btn-primary--lg.settings__button--orange', {
+ h('button.btn-primary.btn--large.settings__button--orange', {
onClick (event) {
event.preventDefault()
setFeatureFlagToBeta()
@@ -281,7 +281,7 @@ class Settings extends Component {
h('div.settings__content-item', this.context.t('resetAccount')),
h('div.settings__content-item', [
h('div.settings__content-item-col', [
- h('button.btn-primary--lg.settings__button--orange', {
+ h('button.btn-primary.btn--large.settings__button--orange', {
onClick (event) {
event.preventDefault()
showResetAccountConfirmationModal()
diff --git a/ui/app/components/pages/unlock-page/unlock-page.component.js b/ui/app/components/pages/unlock-page/unlock-page.component.js
index b6384b32d..8bc3897da 100644
--- a/ui/app/components/pages/unlock-page/unlock-page.component.js
+++ b/ui/app/components/pages/unlock-page/unlock-page.component.js
@@ -34,14 +34,7 @@ class UnlockPage extends Component {
}
}
- tryUnlockMetamask (password) {
- const { tryUnlockMetamask, history } = this.props
- tryUnlockMetamask(password)
- .catch(({ message }) => this.setState({ error: message }))
- .then(() => history.push(DEFAULT_ROUTE))
- }
-
- handleSubmit (event) {
+ async handleSubmit (event) {
event.preventDefault()
event.stopPropagation()
@@ -54,9 +47,14 @@ class UnlockPage extends Component {
this.setState({ error: null })
- tryUnlockMetamask(password)
- .catch(({ message }) => this.setState({ error: message }))
- .then(() => history.push(DEFAULT_ROUTE))
+ try {
+ await tryUnlockMetamask(password)
+ } catch ({ message }) {
+ this.setState({ error: message })
+ return
+ }
+
+ history.push(DEFAULT_ROUTE)
}
handleInputChange ({ target }) {
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index c07c96ccc..5ad35c269 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -291,18 +291,48 @@ ConfirmSendEther.prototype.convertToRenderableCurrency = function (value, curren
: value
}
-ConfirmSendEther.prototype.editTransaction = function (txMeta) {
+ConfirmSendEther.prototype.editTransaction = function () {
const { editTransaction, history } = this.props
+ const txMeta = this.gatherTxMeta()
editTransaction(txMeta)
history.push(SEND_ROUTE)
}
-ConfirmSendEther.prototype.renderNetworkDisplay = function () {
+ConfirmSendEther.prototype.renderHeaderRow = function (isTxReprice) {
const windowType = window.METAMASK_UI_TYPE
+ const isFullScreen = windowType !== ENVIRONMENT_TYPE_NOTIFICATION &&
+ windowType !== ENVIRONMENT_TYPE_POPUP
- return (windowType === ENVIRONMENT_TYPE_NOTIFICATION || windowType === ENVIRONMENT_TYPE_POPUP)
- ? h(NetworkDisplay)
- : null
+ if (isTxReprice && isFullScreen) {
+ return null
+ }
+
+ return (
+ h('.page-container__header-row', [
+ h('span.page-container__back-button', {
+ onClick: () => this.editTransaction(),
+ style: {
+ visibility: isTxReprice ? 'hidden' : 'initial',
+ },
+ }, 'Edit'),
+ !isFullScreen && h(NetworkDisplay),
+ ])
+ )
+}
+
+ConfirmSendEther.prototype.renderHeader = function (isTxReprice) {
+ const title = isTxReprice ? this.context.t('speedUpTitle') : this.context.t('confirm')
+ const subtitle = isTxReprice
+ ? this.context.t('speedUpSubtitle')
+ : this.context.t('pleaseReviewTransaction')
+
+ return (
+ h('.page-container__header', [
+ this.renderHeaderRow(isTxReprice),
+ h('.page-container__title', title),
+ h('.page-container__subtitle', subtitle),
+ ])
+ )
}
ConfirmSendEther.prototype.render = function () {
@@ -320,6 +350,7 @@ ConfirmSendEther.prototype.render = function () {
},
} = this.props
const txMeta = this.gatherTxMeta()
+ const isTxReprice = Boolean(txMeta.lastGasPrice)
const txParams = txMeta.txParams || {}
const {
@@ -338,11 +369,6 @@ ConfirmSendEther.prototype.render = function () {
totalInETH,
} = this.getData()
- const title = txMeta.lastGasPrice ? 'Reprice Transaction' : 'Confirm'
- const subtitle = txMeta.lastGasPrice
- ? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
- : 'Please review your transaction.'
-
const convertedAmountInFiat = this.convertToRenderableCurrency(amountInFIAT, currentCurrency)
const convertedTotalInFiat = this.convertToRenderableCurrency(totalInFIAT, currentCurrency)
@@ -362,19 +388,7 @@ ConfirmSendEther.prototype.render = function () {
return (
// Main Send token Card
h('.page-container', [
- h('.page-container__header', [
- h('.page-container__header-row', [
- h('span.page-container__back-button', {
- onClick: () => this.editTransaction(txMeta),
- style: {
- visibility: !txMeta.lastGasPrice ? 'initial' : 'hidden',
- },
- }, 'Edit'),
- this.renderNetworkDisplay(),
- ]),
- h('.page-container__title', title),
- h('.page-container__subtitle', subtitle),
- ]),
+ this.renderHeader(isTxReprice),
h('.page-container__content', [
h(SenderToRecipient, {
senderName: fromName,
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index 656093b3d..ddaa13d22 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -12,6 +12,7 @@ const actions = require('../../actions')
const clone = require('clone')
const Identicon = require('../identicon')
const GasFeeDisplay = require('../send/gas-fee-display-v2.js')
+const NetworkDisplay = require('../network-display')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const {
@@ -39,6 +40,11 @@ const {
} = require('../../selectors')
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
+const {
+ ENVIRONMENT_TYPE_POPUP,
+ ENVIRONMENT_TYPE_NOTIFICATION,
+} = require('../../../../app/scripts/lib/enums')
+
ConfirmSendToken.contextTypes = {
t: PropTypes.func,
}
@@ -430,6 +436,43 @@ ConfirmSendToken.prototype.convertToRenderableCurrency = function (value, curren
: value
}
+ConfirmSendToken.prototype.renderHeaderRow = function (isTxReprice) {
+ const windowType = window.METAMASK_UI_TYPE
+ const isFullScreen = windowType !== ENVIRONMENT_TYPE_NOTIFICATION &&
+ windowType !== ENVIRONMENT_TYPE_POPUP
+
+ if (isTxReprice && isFullScreen) {
+ return null
+ }
+
+ return (
+ h('.page-container__header-row', [
+ h('span.page-container__back-button', {
+ onClick: () => this.editTransaction(),
+ style: {
+ visibility: isTxReprice ? 'hidden' : 'initial',
+ },
+ }, 'Edit'),
+ !isFullScreen && h(NetworkDisplay),
+ ])
+ )
+}
+
+ConfirmSendToken.prototype.renderHeader = function (isTxReprice) {
+ const title = isTxReprice ? this.context.t('speedUpTitle') : this.context.t('confirm')
+ const subtitle = isTxReprice
+ ? this.context.t('speedUpSubtitle')
+ : this.context.t('pleaseReviewTransaction')
+
+ return (
+ h('.page-container__header', [
+ this.renderHeaderRow(isTxReprice),
+ h('.page-container__title', title),
+ h('.page-container__subtitle', subtitle),
+ ])
+ )
+}
+
ConfirmSendToken.prototype.render = function () {
const txMeta = this.gatherTxMeta()
const {
@@ -443,25 +486,13 @@ ConfirmSendToken.prototype.render = function () {
},
} = this.getData()
- this.inputs = []
-
const isTxReprice = Boolean(txMeta.lastGasPrice)
- const title = isTxReprice ? this.context.t('reprice_title') : this.context.t('confirm')
- const subtitle = isTxReprice
- ? this.context.t('reprice_subtitle')
- : this.context.t('pleaseReviewTransaction')
return (
h('div.confirm-screen-container.confirm-send-token', [
// Main Send token Card
h('div.page-container', [
- h('div.page-container__header', [
- !txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
- onClick: () => this.editTransaction(txMeta),
- }, this.context.t('edit')),
- h('div.page-container__title', title),
- h('div.page-container__subtitle', subtitle),
- ]),
+ this.renderHeader(isTxReprice),
h('.page-container__content', [
h('div.flex-row.flex-center.confirm-screen-identicons', [
h('div.confirm-screen-account-wrapper', [
diff --git a/ui/app/components/pending-tx/index.js b/ui/app/components/pending-tx/index.js
index 893538bcf..3f8cd8823 100644
--- a/ui/app/components/pending-tx/index.js
+++ b/ui/app/components/pending-tx/index.js
@@ -130,7 +130,6 @@ PendingTx.prototype.render = function () {
if (isFetching) {
return h(Loading, {
- fullScreen: true,
loadingMessage: this.context.t('generatingTransaction'),
})
}
@@ -157,9 +156,7 @@ PendingTx.prototype.render = function () {
sendTransaction,
})
default:
- return h(Loading, {
- fullScreen: true,
- })
+ return h(Loading)
}
}
diff --git a/ui/app/components/selected-account/index.js b/ui/app/components/selected-account/index.js
new file mode 100644
index 000000000..eb342181f
--- /dev/null
+++ b/ui/app/components/selected-account/index.js
@@ -0,0 +1,2 @@
+import SelectedAccount from './selected-account.container'
+module.exports = SelectedAccount
diff --git a/ui/app/components/selected-account/index.scss b/ui/app/components/selected-account/index.scss
new file mode 100644
index 000000000..5339a228b
--- /dev/null
+++ b/ui/app/components/selected-account/index.scss
@@ -0,0 +1,38 @@
+.selected-account {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ flex: 1;
+
+ &__name {
+ max-width: 200px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ text-align: center;
+ }
+
+ &__address {
+ font-size: .75rem;
+ color: $silver-chalice;
+ }
+
+ &__clickable {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 5px 15px;
+ border-radius: 10px;
+ cursor: pointer;
+
+ &:hover {
+ background-color: #e8e6e8;
+ }
+
+ &:active {
+ background-color: #d9d7da;
+ }
+ }
+}
diff --git a/ui/app/components/selected-account/selected-account.component.js b/ui/app/components/selected-account/selected-account.component.js
new file mode 100644
index 000000000..3386a4196
--- /dev/null
+++ b/ui/app/components/selected-account/selected-account.component.js
@@ -0,0 +1,60 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import copyToClipboard from 'copy-to-clipboard'
+
+const Tooltip = require('../tooltip-v2.js')
+
+const addressStripper = (address = '') => {
+ if (address.length < 4) {
+ return address
+ }
+
+ return `${address.slice(0, 4)}...${address.slice(-4)}`
+}
+
+class SelectedAccount extends Component {
+ state = {
+ copied: false,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ static propTypes = {
+ selectedAddress: PropTypes.string,
+ selectedIdentity: PropTypes.object,
+ }
+
+ render () {
+ const { t } = this.context
+ const { selectedAddress, selectedIdentity } = this.props
+
+ return (
+ <div className="selected-account">
+ <Tooltip
+ position="bottom"
+ title={this.state.copied ? t('copiedExclamation') : t('copyToClipboard')}
+ >
+ <div
+ className="selected-account__clickable"
+ onClick={() => {
+ this.setState({ copied: true })
+ setTimeout(() => this.setState({ copied: false }), 3000)
+ copyToClipboard(selectedAddress)
+ }}
+ >
+ <div className="selected-account__name">
+ { selectedIdentity.name }
+ </div>
+ <div className="selected-account__address">
+ { addressStripper(selectedAddress) }
+ </div>
+ </div>
+ </Tooltip>
+ </div>
+ )
+ }
+}
+
+export default SelectedAccount
diff --git a/ui/app/components/selected-account/selected-account.container.js b/ui/app/components/selected-account/selected-account.container.js
new file mode 100644
index 000000000..f9e061d15
--- /dev/null
+++ b/ui/app/components/selected-account/selected-account.container.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import SelectedAccount from './selected-account.component'
+
+const selectors = require('../../selectors')
+
+const mapStateToProps = state => {
+ return {
+ selectedAddress: selectors.getSelectedAddress(state),
+ selectedIdentity: selectors.getSelectedIdentity(state),
+ }
+}
+
+export default connect(mapStateToProps)(SelectedAccount)
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index 22ab64426..93d2023b5 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -242,7 +242,7 @@ ShapeshiftForm.prototype.render = function () {
]),
- !depositAddress && h('button.btn-primary--lg.shapeshift-form__shapeshift-buy-btn', {
+ !depositAddress && h('button.btn-primary.btn--large.shapeshift-form__shapeshift-buy-btn', {
className: btnClass,
disabled: !token,
onClick: () => this.onBuyWithShapeShift(),
diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js
index 474fcf439..ab780dcf4 100644
--- a/ui/app/components/signature-request.js
+++ b/ui/app/components/signature-request.js
@@ -235,12 +235,12 @@ SignatureRequest.prototype.renderFooter = function () {
}
return h('div.request-signature__footer', [
- h('button.btn-secondary--lg.request-signature__footer__cancel-button', {
+ h('button.btn-default.btn--large.request-signature__footer__cancel-button', {
onClick: event => {
cancel(event).then(() => this.props.history.push(DEFAULT_ROUTE))
},
}, this.context.t('cancel')),
- h('button.btn-primary--lg', {
+ h('button.btn-primary.btn--large', {
onClick: event => {
sign(event).then(() => this.props.history.push(DEFAULT_ROUTE))
},
diff --git a/ui/app/components/text-field/text-field.component.js b/ui/app/components/text-field/text-field.component.js
index b695a449a..2c72d8124 100644
--- a/ui/app/components/text-field/text-field.component.js
+++ b/ui/app/components/text-field/text-field.component.js
@@ -1,8 +1,15 @@
-import React, { Component } from 'react'
+import React from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import { default as MaterialTextField } from '@material-ui/core/TextField'
+const inputLabelBase = {
+ transform: 'none',
+ transition: 'none',
+ position: 'initial',
+ color: '#5b5b5b',
+}
+
const styles = {
materialLabel: {
'&$materialFocused': {
@@ -46,57 +53,57 @@ const styles = {
border: '1px solid #2f9ae0',
},
},
+ largeInputLabel: {
+ ...inputLabelBase,
+ fontSize: '1rem',
+ },
inputLabel: {
+ ...inputLabelBase,
fontSize: '.75rem',
- transform: 'none',
- transition: 'none',
- position: 'initial',
- color: '#5b5b5b',
},
}
-class TextField extends Component {
- static defaultProps = {
- error: null,
- }
+const TextField = props => {
+ const { error, classes, material, startAdornment, largeLabel, ...textFieldProps } = props
- static propTypes = {
- error: PropTypes.string,
- classes: PropTypes.object,
- material: PropTypes.bool,
- startAdornment: PropTypes.element,
- }
+ return (
+ <MaterialTextField
+ error={Boolean(error)}
+ helperText={error}
+ InputLabelProps={{
+ shrink: material ? undefined : true,
+ className: material ? '' : (largeLabel ? classes.largeInputLabel : classes.inputLabel),
+ FormLabelClasses: {
+ root: material ? classes.materialLabel : classes.formLabel,
+ focused: material ? classes.materialFocused : classes.formLabelFocused,
+ error: classes.materialError,
+ },
+ }}
+ InputProps={{
+ startAdornment: startAdornment || undefined,
+ disableUnderline: !material,
+ classes: {
+ root: material ? '' : classes.inputRoot,
+ input: material ? '' : classes.input,
+ underline: material ? classes.materialUnderline : '',
+ focused: material ? '' : classes.inputFocused,
+ },
+ }}
+ {...textFieldProps}
+ />
+ )
+}
- render () {
- const { error, classes, material, startAdornment, ...textFieldProps } = this.props
+TextField.defaultProps = {
+ error: null,
+}
- return (
- <MaterialTextField
- error={Boolean(error)}
- helperText={error}
- InputLabelProps={{
- shrink: material ? undefined : true,
- className: material ? '' : classes.inputLabel,
- FormLabelClasses: {
- root: material ? classes.materialLabel : classes.formLabel,
- focused: material ? classes.materialFocused : classes.formLabelFocused,
- error: classes.materialError,
- },
- }}
- InputProps={{
- startAdornment: startAdornment || undefined,
- disableUnderline: !material,
- classes: {
- root: material ? '' : classes.inputRoot,
- input: material ? '' : classes.input,
- underline: material ? classes.materialUnderline : '',
- focused: material ? '' : classes.inputFocused,
- },
- }}
- {...textFieldProps}
- />
- )
- }
+TextField.propTypes = {
+ error: PropTypes.string,
+ classes: PropTypes.object,
+ material: PropTypes.bool,
+ startAdornment: PropTypes.element,
+ largeLabel: PropTypes.bool,
}
export default withStyles(styles)(TextField)
diff --git a/ui/app/components/text-field/text-field.stories.js b/ui/app/components/text-field/text-field.stories.js
index ee3e5faaf..c00873b8a 100644
--- a/ui/app/components/text-field/text-field.stories.js
+++ b/ui/app/components/text-field/text-field.stories.js
@@ -22,3 +22,32 @@ storiesOf('TextField', module)
error="Invalid value"
/>
)
+ .add('Mascara text', () =>
+ <TextField
+ label="Text"
+ type="text"
+ largeLabel
+ />
+ )
+ .add('Material text', () =>
+ <TextField
+ label="Text"
+ type="text"
+ material
+ />
+ )
+ .add('Material password', () =>
+ <TextField
+ label="Password"
+ type="password"
+ material
+ />
+ )
+ .add('Material error', () =>
+ <TextField
+ type="text"
+ label="Name"
+ error="Invalid value"
+ material
+ />
+ )
diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js
index c84117d84..4100d76a5 100644
--- a/ui/app/components/token-cell.js
+++ b/ui/app/components/token-cell.js
@@ -101,8 +101,8 @@ TokenCell.prototype.render = function () {
h('div.token-list-item__balance-ellipsis', null, [
h('div.token-list-item__balance-wrapper', null, [
- h('h3.token-list-item__token-balance', `${string || 0} ${symbol}`),
-
+ h('div.token-list-item__token-balance', `${string || 0}`),
+ h('div.token-list-item__token-symbol', symbol),
showFiat && h('div.token-list-item__fiat-amount', {
style: {},
}, formattedFiat),
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index bd4ea80a6..ef441ff73 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -1,5 +1,7 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
+const { compose } = require('recompose')
+const { withRouter } = require('react-router-dom')
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const inherits = require('util').inherits
@@ -16,13 +18,16 @@ const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const { calcTokenAmount } = require('../token-util')
const { getCurrentCurrency } = require('../selectors')
+const { CONFIRM_TRANSACTION_ROUTE } = require('../routes')
TxListItem.contextTypes = {
t: PropTypes.func,
}
-module.exports = connect(mapStateToProps, mapDispatchToProps)(TxListItem)
-
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(TxListItem)
function mapStateToProps (state) {
return {
@@ -216,6 +221,7 @@ TxListItem.prototype.setSelectedToken = function (tokenAddress) {
TxListItem.prototype.resubmit = function () {
const { transactionId } = this.props
this.props.retryTransaction(transactionId)
+ .then(id => this.props.history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`))
}
TxListItem.prototype.render = function () {
diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js
index 263f992c0..014497fcd 100644
--- a/ui/app/components/tx-view.js
+++ b/ui/app/components/tx-view.js
@@ -12,7 +12,7 @@ const { checksumAddress: toChecksumAddress } = require('../util')
const BalanceComponent = require('./balance-component')
const TxList = require('./tx-list')
-const Identicon = require('./identicon')
+const SelectedAccount = require('./selected-account')
module.exports = compose(
withRouter,
@@ -103,7 +103,7 @@ TxView.prototype.renderButtons = function () {
}
TxView.prototype.render = function () {
- const { selectedAddress, identity, network, isMascara } = this.props
+ const { isMascara } = this.props
return h('div.tx-view.flex-column', {
style: {},
@@ -111,10 +111,12 @@ TxView.prototype.render = function () {
h('div.flex-row.phone-visible', {
style: {
- justifyContent: 'space-between',
+ justifyContent: 'center',
alignItems: 'center',
flex: '0 0 auto',
- margin: '10px',
+ marginBottom: '16px',
+ padding: '5px',
+ borderBottom: '1px solid #e5e5e5',
},
}, [
@@ -127,23 +129,7 @@ TxView.prototype.render = function () {
onClick: () => this.props.sidebarOpen ? this.props.hideSidebar() : this.props.showSidebar(),
}),
- h('.identicon-wrapper.select-none', {
- style: {
- marginLeft: '0.9em',
- },
- }, [
- h(Identicon, {
- diameter: 24,
- address: selectedAddress,
- network,
- }),
- ]),
-
- h('span.account-name', {
- style: {},
- }, [
- identity.name,
- ]),
+ h(SelectedAccount),
!isMascara && h('div.open-in-browser', {
onClick: () => global.platform.openExtensionInBrowser(),
diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js
index 3b29dacac..da142fad8 100644
--- a/ui/app/components/wallet-view.js
+++ b/ui/app/components/wallet-view.js
@@ -36,7 +36,6 @@ function mapStateToProps (state) {
tokens: state.metamask.tokens,
keyrings: state.metamask.keyrings,
selectedAddress: selectors.getSelectedAddress(state),
- selectedIdentity: selectors.getSelectedIdentity(state),
selectedAccount: selectors.getSelectedAccount(state),
selectedTokenAddress: state.metamask.selectedTokenAddress,
}
@@ -99,21 +98,24 @@ WalletView.prototype.render = function () {
const {
responsiveDisplayClassname,
selectedAddress,
- selectedIdentity,
keyrings,
showAccountDetailModal,
sidebarOpen,
hideSidebar,
history,
+ identities,
} = this.props
// temporary logs + fake extra wallets
// console.log('walletview, selectedAccount:', selectedAccount)
const checksummedAddress = checksumAddress(selectedAddress)
+ if (!selectedAddress) {
+ throw new Error('selectedAddress should not be ' + String(selectedAddress))
+ }
+
const keyring = keyrings.find((kr) => {
- return kr.accounts.includes(selectedAddress) ||
- kr.accounts.includes(selectedIdentity.address)
+ return kr.accounts.includes(selectedAddress)
})
const type = keyring.type
@@ -145,7 +147,7 @@ WalletView.prototype.render = function () {
h('span.account-name', {
style: {},
}, [
- selectedIdentity.name,
+ identities[selectedAddress].name,
]),
h('button.btn-clear.wallet-view__details-button.allcaps', this.context.t('details')),
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index fb38aaa76..461587cb1 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -7,6 +7,7 @@ const { compose } = require('recompose')
const actions = require('./actions')
const txHelper = require('../lib/tx-helper')
const log = require('loglevel')
+const R = require('ramda')
const PendingTx = require('./components/pending-tx')
const SignatureRequest = require('./components/signature-request')
@@ -87,37 +88,74 @@ ConfirmTxScreen.prototype.componentDidUpdate = function (prevProps) {
network,
selectedAddressTxList,
send,
+ history,
+ match: { params: { id: transactionId } = {} },
} = this.props
- const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps
- const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network)
- const prevTxData = prevUnconfTxList[prevIndex] || {}
- const prevTx = selectedAddressTxList.find(({ id }) => id === prevTxData.id) || {}
+
+ let prevTx
+
+ if (transactionId) {
+ prevTx = R.find(({ id }) => id + '' === transactionId)(selectedAddressTxList)
+ } else {
+ const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps
+ const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network)
+ const prevTxData = prevUnconfTxList[prevIndex] || {}
+ prevTx = selectedAddressTxList.find(({ id }) => id === prevTxData.id) || {}
+ }
+
const unconfTxList = txHelper(unapprovedTxs, {}, {}, {}, network)
- if (unconfTxList.length === 0 &&
- (prevTx.status === 'dropped' || !send.to && this.getUnapprovedMessagesTotal() === 0)) {
+ if (prevTx.status === 'dropped') {
+ this.props.dispatch(actions.showModal({
+ name: 'TRANSACTION_CONFIRMED',
+ onHide: () => history.push(DEFAULT_ROUTE),
+ }))
+
+ return
+ }
+
+ if (unconfTxList.length === 0 && !send.to && this.getUnapprovedMessagesTotal() === 0) {
this.props.history.push(DEFAULT_ROUTE)
}
}
-ConfirmTxScreen.prototype.render = function () {
- const props = this.props
+ConfirmTxScreen.prototype.getTxData = function () {
const {
network,
+ index,
+ unapprovedTxs,
+ unapprovedMsgs,
+ unapprovedPersonalMsgs,
+ unapprovedTypedMessages,
+ match: { params: { id: transactionId } = {} },
+ } = this.props
+
+ const unconfTxList = txHelper(
unapprovedTxs,
- currentCurrency,
unapprovedMsgs,
unapprovedPersonalMsgs,
unapprovedTypedMessages,
+ network
+ )
+
+ log.info(`rendering a combined ${unconfTxList.length} unconf msgs & txs`)
+
+ return transactionId
+ ? R.find(({ id }) => id + '' === transactionId)(unconfTxList)
+ : unconfTxList[index]
+}
+
+ConfirmTxScreen.prototype.render = function () {
+ const props = this.props
+ const {
+ currentCurrency,
conversionRate,
blockGasLimit,
// provider,
// computedBalances,
} = props
- var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, network)
-
- var txData = unconfTxList[props.index] || {}
+ var txData = this.getTxData() || {}
var txParams = txData.params || {}
// var isNotification = isPopupOrNotification() === 'notification'
@@ -136,7 +174,6 @@ ConfirmTxScreen.prototype.render = function () {
]),
*/
- log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`)
return currentTxView({
// Properties
diff --git a/ui/app/css/itcss/components/account-menu.scss b/ui/app/css/itcss/components/account-menu.scss
index 657760ab5..96fba890c 100644
--- a/ui/app/css/itcss/components/account-menu.scss
+++ b/ui/app/css/itcss/components/account-menu.scss
@@ -116,6 +116,10 @@
&__name {
color: $white;
font-size: 18px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ max-width: 200px;
}
&__balance {
diff --git a/ui/app/css/itcss/components/buttons.scss b/ui/app/css/itcss/components/buttons.scss
index 4cbed6093..f93daec04 100644
--- a/ui/app/css/itcss/components/buttons.scss
+++ b/ui/app/css/itcss/components/buttons.scss
@@ -2,10 +2,10 @@
Buttons
*/
+.btn-default,
.btn-primary,
-.btn-primary--lg,
-.btn-secondary,
-.btn-secondary--lg {
+.btn-secondary {
+ height: 44px;
background: $white;
display: flex;
justify-content: center;
@@ -20,10 +20,16 @@
width: 100%;
text-transform: uppercase;
outline: none;
+
+ &--disabled,
+ &[disabled] {
+ cursor: auto;
+ opacity: .5;
+ pointer-events: none;
+ }
}
-.btn-primary,
-.btn-primary--lg {
+.btn-primary {
color: $curious-blue;
border: 2px solid $spindle;
@@ -35,17 +41,23 @@
&:hover {
border-color: $curious-blue;
}
+}
- &--disabled,
- &[disabled] {
- cursor: auto;
- opacity: .5;
- pointer-events: none;
+.btn-secondary {
+ color: $monzo;
+ border: 2px solid lighten($monzo, 40%);
+
+ &:active {
+ background: lighten($monzo, 55%);
+ border-color: $monzo;
+ }
+
+ &:hover {
+ border-color: $monzo;
}
}
-.btn-secondary,
-.btn-secondary--lg {
+.btn-default {
color: $scorpion;
border: 2px solid $dusty-gray;
@@ -57,20 +69,9 @@
&:hover {
border-color: $scorpion;
}
-
- &--disabled,
- &[disabled] {
- cursor: auto;
- opacity: .5;
- pointer-events: none;
- }
-}
-
-.btn-primary, .btn-secondary {
- height: 44px;
}
-.btn-primary--lg, .btn-secondary--lg {
+.btn--large {
height: 54px;
}
diff --git a/ui/app/css/itcss/components/hero-balance.scss b/ui/app/css/itcss/components/hero-balance.scss
index 69cde8a0f..09d66aedd 100644
--- a/ui/app/css/itcss/components/hero-balance.scss
+++ b/ui/app/css/itcss/components/hero-balance.scss
@@ -6,6 +6,7 @@
justify-content: flex-start;
align-items: center;
flex: 0 0 auto;
+ padding-top: 16px;
}
@media screen and (min-width: $break-large) {
diff --git a/ui/app/css/itcss/components/loading-overlay.scss b/ui/app/css/itcss/components/loading-overlay.scss
index c18b7fa59..b07d6af17 100644
--- a/ui/app/css/itcss/components/loading-overlay.scss
+++ b/ui/app/css/itcss/components/loading-overlay.scss
@@ -11,8 +11,8 @@
background: rgba(255, 255, 255, .8);
@media screen and (max-width: 575px) {
- margin-top: 56px;
- height: calc(100% - 56px);
+ margin-top: 66px;
+ height: calc(100% - 66px);
}
@media screen and (min-width: 576px) {
diff --git a/ui/app/css/itcss/components/token-list.scss b/ui/app/css/itcss/components/token-list.scss
index e8de317e3..72fda372f 100644
--- a/ui/app/css/itcss/components/token-list.scss
+++ b/ui/app/css/itcss/components/token-list.scss
@@ -14,10 +14,17 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
min-width: 0;
&__token-balance {
- font-size: 1.5rem;
+ margin-right: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ min-width: 0;
+ max-width: 100%;
+ }
+
+ &__token-balance, &__token-symbol {
+ font-size: 1.5rem;
+ flex: 0 0 auto;
@media #{$wallet-balance-breakpoint-range} {
font-size: 95%;
@@ -66,7 +73,9 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
}
&__balance-wrapper {
- flex: 1 1 auto;
+ flex: 1;
+ flex-flow: row wrap;
+ display: flex;
min-width: 0;
}
}
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index 2b39eb8db..4e9d0848c 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -42,6 +42,7 @@ function reduceApp (state, action) {
open: false,
modalState: {
name: null,
+ props: {},
},
previousModalState: {
name: null,
@@ -88,13 +89,17 @@ function reduceApp (state, action) {
// modal methods:
case actions.MODAL_OPEN:
+ const { name, ...modalProps } = action.payload
+
return extend(appState, {
- modal: Object.assign(
- state.appState.modal,
- { open: true },
- { modalState: action.payload },
- { previousModalState: appState.modal.modalState},
- ),
+ modal: {
+ open: true,
+ modalState: {
+ name: name,
+ props: { ...modalProps },
+ },
+ previousModalState: { ...appState.modal.modalState },
+ },
})
case actions.MODAL_CLOSE:
@@ -102,7 +107,7 @@ function reduceApp (state, action) {
modal: Object.assign(
state.appState.modal,
{ open: false },
- { modalState: { name: null } },
+ { modalState: { name: null, props: {} } },
{ previousModalState: appState.modal.modalState},
),
})
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index 6736b5571..4fbe8ff11 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -499,7 +499,7 @@ SendTransactionScreen.prototype.renderFooter = function () {
return h('div.page-container__footer', [
h(Button, {
- type: 'secondary',
+ type: 'default',
large: true,
className: 'page-container__footer-button',
onClick: () => {