mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
fix merge conflicts
This commit is contained in:
commit
69f4c8c336
4
.babelrc
4
.babelrc
@ -1,4 +1,4 @@
|
||||
{
|
||||
"presets": ["es2015", "stage-0", "react"],
|
||||
"plugins": ["transform-runtime", "transform-async-to-generator"]
|
||||
"presets": [["env", { "debug": true }], "react", "stage-0"],
|
||||
"plugins": ["transform-runtime", "transform-async-to-generator", "transform-class-properties"]
|
||||
}
|
||||
|
6
.github/CODEOWNERS
vendored
Normal file
6
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
ui/ @danjm @alextsg @whymarrh
|
||||
app/scripts/controllers/transactions @frankiebee
|
||||
|
@ -7,6 +7,7 @@
|
||||
- Add Trezor Support
|
||||
- Allow to remove accounts (Imported and Hardware Wallets)
|
||||
- [#4840](https://github.com/MetaMask/metamask-extension/pull/4840): Now shows notifications when transactions are completed.
|
||||
- [#4855](https://github.com/MetaMask/metamask-extension/pull/4855): network.js: convert rpc protocol to lower case.
|
||||
|
||||
## 4.8.0 Thur Jun 14 2018
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
# 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)
|
||||
|
||||
🚨 As of 7/25/18, the MetaMask extension has been removed from the Chrome Web Store. In the meantime, you can download the latest version of MetaMask on our [Releases](https://github.com/MetaMask/metamask-extension/releases) page and load it in Chrome by visiting `chrome://extensions`. For more detailed steps, see our [help center](https://consensys.zendesk.com/hc/en-us/articles/360004134152-How-to-Install-MetaMask-Manually). Follow [@metamask_io](https://twitter.com/metamask_io) on Twitter for updates. 🚨
|
||||
|
||||
## Support
|
||||
|
||||
If you're a user seeking support, [here is our support site](https://metamask.helpscoutdocs.com/).
|
||||
|
@ -17,6 +17,6 @@
|
||||
{ "code": "tml", "name": "Tamil" },
|
||||
{ "code": "tr", "name": "Turkish" },
|
||||
{ "code": "vi", "name": "Vietnamese" },
|
||||
{ "code": "zh_CN", "name": "Mandarin" },
|
||||
{ "code": "zh_TW", "name": "Taiwanese" }
|
||||
{ "code": "zh_CN", "name": "Chinese (Simplified)" },
|
||||
{ "code": "zh_TW", "name": "Chinese (Traditional)" }
|
||||
]
|
||||
|
@ -122,6 +122,9 @@
|
||||
"copy": {
|
||||
"message": "コピー"
|
||||
},
|
||||
"copyContractAddress": {
|
||||
"message": "コントラクトアドレスをコピー"
|
||||
},
|
||||
"copyToClipboard": {
|
||||
"message": "クリップボードへコピー"
|
||||
},
|
||||
@ -395,6 +398,9 @@
|
||||
"mainnet": {
|
||||
"message": "Ethereumメインネットワーク"
|
||||
},
|
||||
"menu": {
|
||||
"message": "メニュー"
|
||||
},
|
||||
"message": {
|
||||
"message": "メッセージ"
|
||||
},
|
||||
@ -464,6 +470,9 @@
|
||||
"oldUIMessage": {
|
||||
"message": "旧UIを表示しています。右上のドロップダウンメニューのオプションより、新UIへ切り替えが可能です。"
|
||||
},
|
||||
"openInTab": {
|
||||
"message": "タブを開く"
|
||||
},
|
||||
"or": {
|
||||
"message": "または",
|
||||
"description": "choice between creating or importing a new account"
|
||||
@ -573,6 +582,15 @@
|
||||
"searchResults": {
|
||||
"message": "検索結果"
|
||||
},
|
||||
"newPassword8Chars": {
|
||||
"message": "新しいパスワード (8桁以上)"
|
||||
},
|
||||
"select": {
|
||||
"message": "選択"
|
||||
},
|
||||
"selectCurrency": {
|
||||
"message": "通貨を選択"
|
||||
},
|
||||
"selectService": {
|
||||
"message": "サービスを選択"
|
||||
},
|
||||
@ -586,10 +604,14 @@
|
||||
"message": "ETHの送信"
|
||||
},
|
||||
"sendTokens": {
|
||||
"message": "トークンを送る"
|
||||
"message": "トークンを送信"
|
||||
},
|
||||
"onlySendToEtherAddress": {
|
||||
"message": "ETHはイーサリウムアカウントのみに送信できます。"
|
||||
"message": "ETH はイーサリウムアカウントのみに送信できます。"
|
||||
},
|
||||
"onlySendTokensToAccountAddress": {
|
||||
"message": "$1 はイーサリアムアカウントのみに送信できます。",
|
||||
"description": "displays token symbol"
|
||||
},
|
||||
"searchTokens": {
|
||||
"message": "トークンの検索"
|
||||
@ -690,10 +712,10 @@
|
||||
"message": "パスワードの入力"
|
||||
},
|
||||
"uiWelcome": {
|
||||
"message": "新UIへようこそ!(ベータ版)"
|
||||
"message": "新UIへようこそ! (ベータ版)"
|
||||
},
|
||||
"uiWelcomeMessage": {
|
||||
"message": "現在Metamaskの新しいUIをお使いになっています。トークン送信など、新たな機能を試してみましょう!何か問題があればご報告ください。"
|
||||
"message": "現在、MetaMask の新しいUIをお使いになっています。トークン送信など、新たな機能を試してみましょう! 何か問題があればご報告ください。"
|
||||
},
|
||||
"unavailable": {
|
||||
"message": "有効ではありません。"
|
||||
@ -720,6 +742,9 @@
|
||||
"viewAccount": {
|
||||
"message": "アカウントを見る"
|
||||
},
|
||||
"viewOnEtherscan": {
|
||||
"message": "Etherscan で見る"
|
||||
},
|
||||
"warning": {
|
||||
"message": "警告"
|
||||
},
|
||||
|
@ -178,6 +178,7 @@ function blacklistedDomainCheck () {
|
||||
'adyen.com',
|
||||
'gravityforms.com',
|
||||
'harbourair.com',
|
||||
'ani.gamer.com.tw',
|
||||
'blueskybooking.com',
|
||||
]
|
||||
var currentUrl = window.location.href
|
||||
|
@ -9,6 +9,7 @@ const extend = require('xtend')
|
||||
const EthQuery = require('eth-query')
|
||||
const createEventEmitterProxy = require('../../lib/events-proxy.js')
|
||||
const log = require('loglevel')
|
||||
const urlUtil = require('url')
|
||||
const {
|
||||
ROPSTEN,
|
||||
RINKEBY,
|
||||
@ -155,6 +156,8 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
}
|
||||
|
||||
_configureStandardProvider ({ rpcUrl }) {
|
||||
// urlUtil handles malformed urls
|
||||
rpcUrl = urlUtil.parse(rpcUrl).format()
|
||||
const providerParams = extend(this._baseProviderParams, {
|
||||
rpcUrl,
|
||||
engineParams: {
|
||||
|
@ -30,14 +30,10 @@ class TxGasUtil {
|
||||
try {
|
||||
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
||||
} catch (err) {
|
||||
const simulationFailed = (
|
||||
err.message.includes('Transaction execution error.') ||
|
||||
err.message.includes('gas required exceeds allowance or always failing transaction')
|
||||
)
|
||||
if (simulationFailed) {
|
||||
txMeta.simulationFails = true
|
||||
return txMeta
|
||||
txMeta.simulationFails = {
|
||||
reason: err.message,
|
||||
}
|
||||
return txMeta
|
||||
}
|
||||
this.setTxGas(txMeta, block.gasLimit, estimatedGasHex)
|
||||
return txMeta
|
||||
|
@ -37,4 +37,5 @@ module.exports = [
|
||||
require('./024'),
|
||||
require('./025'),
|
||||
require('./026'),
|
||||
require('./027'),
|
||||
]
|
||||
|
@ -13,6 +13,12 @@ class ExtensionPlatform {
|
||||
extension.tabs.create({ url })
|
||||
}
|
||||
|
||||
closeCurrentWindow () {
|
||||
return extension.windows.getCurrent((windowDetails) => {
|
||||
return extension.windows.remove(windowDetails.id)
|
||||
})
|
||||
}
|
||||
|
||||
getVersion () {
|
||||
return extension.runtime.getManifest().version
|
||||
}
|
||||
|
@ -64,7 +64,6 @@ async function start () {
|
||||
css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss()
|
||||
deleteInjectedCss = injectCss(css)
|
||||
}
|
||||
if (state.appState.shouldClose) notificationManager.closePopup()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -36,14 +36,23 @@ TransactionListItem.prototype.showRetryButton = function () {
|
||||
return false
|
||||
}
|
||||
|
||||
let currentTxIsLatest = false
|
||||
const currentNonce = txParams.nonce
|
||||
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
|
||||
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
||||
const currentSubmittedTxs = transactions.filter(tx => tx.status === 'submitted')
|
||||
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
|
||||
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
||||
lastSubmittedTxWithCurrentNonce.id === transaction.id
|
||||
if (currentSubmittedTxs.length > 0) {
|
||||
const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
|
||||
if (tx1.submittedTime < tx2.submittedTime) return tx1
|
||||
return tx2
|
||||
})
|
||||
currentTxIsLatest = lastTx.id === transaction.id
|
||||
}
|
||||
|
||||
return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
|
||||
return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000 && currentTxIsLatest
|
||||
}
|
||||
|
||||
TransactionListItem.prototype.render = function () {
|
||||
|
43
package-lock.json
generated
43
package-lock.json
generated
@ -3626,9 +3626,9 @@
|
||||
}
|
||||
},
|
||||
"babel-preset-env": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz",
|
||||
"integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz",
|
||||
"integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==",
|
||||
"requires": {
|
||||
"babel-plugin-check-es2015-constants": "^6.22.0",
|
||||
"babel-plugin-syntax-trailing-function-commas": "^6.22.0",
|
||||
@ -3657,7 +3657,7 @@
|
||||
"babel-plugin-transform-es2015-unicode-regex": "^6.22.0",
|
||||
"babel-plugin-transform-exponentiation-operator": "^6.22.0",
|
||||
"babel-plugin-transform-regenerator": "^6.22.0",
|
||||
"browserslist": "^2.1.2",
|
||||
"browserslist": "^3.2.6",
|
||||
"invariant": "^2.2.2",
|
||||
"semver": "^5.3.0"
|
||||
}
|
||||
@ -4672,12 +4672,19 @@
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.0.tgz",
|
||||
"integrity": "sha512-mNYp0RNeu1xueGuJFSXkU+K0nH+dBE/gcjtyhtNKfU8hwdrVIfoA7i5iFSjOmzkGdL2QaO7YX9ExiVPE7AY9JA==",
|
||||
"version": "3.2.8",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz",
|
||||
"integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==",
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30000784",
|
||||
"electron-to-chromium": "^1.3.30"
|
||||
"caniuse-lite": "^1.0.30000844",
|
||||
"electron-to-chromium": "^1.3.47"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.52",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.52.tgz",
|
||||
"integrity": "sha1-0tnxJwuko7lnuDHEDvcftNmrXOA="
|
||||
}
|
||||
}
|
||||
},
|
||||
"bs58": {
|
||||
@ -5004,9 +5011,9 @@
|
||||
"integrity": "sha1-MN/YMAnVcE8C3/s3clBo7RKjZrs="
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30000786",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000786.tgz",
|
||||
"integrity": "sha1-G0Jc2FaNgFvFY4veSQXNhjVod0Y="
|
||||
"version": "1.0.30000865",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000865.tgz",
|
||||
"integrity": "sha512-vs79o1mOSKRGv/1pSkp4EXgl4ZviWeYReXw60XfacPU64uQWZwJT6vZNmxRF9O+6zu71sJwMxLK5JXxbzuVrLw=="
|
||||
},
|
||||
"capture-stack-trace": {
|
||||
"version": "1.0.0",
|
||||
@ -13483,9 +13490,9 @@
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
||||
},
|
||||
"node-sass": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.1.tgz",
|
||||
"integrity": "sha512-m6H1I6cHXsHsJ7BIWdnJsz9S9gVMyh+/H2cOTXgl2/2WqyyWlBcl4PHJcqrXo5RZVCfCUFqOtjPN0+0XbVHR5Q==",
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz",
|
||||
"integrity": "sha512-LdxoJLZutx0aQXHtWIYwJKMj+9pTjneTcLWJgzf2XbGu0q5pRNqW5QvFCEdm3mc5rJOdru/mzln5d0EZLacf6g==",
|
||||
"requires": {
|
||||
"async-foreach": "^0.1.3",
|
||||
"chalk": "^1.1.1",
|
||||
@ -20666,9 +20673,9 @@
|
||||
}
|
||||
},
|
||||
"node-sass": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.1.tgz",
|
||||
"integrity": "sha512-m6H1I6cHXsHsJ7BIWdnJsz9S9gVMyh+/H2cOTXgl2/2WqyyWlBcl4PHJcqrXo5RZVCfCUFqOtjPN0+0XbVHR5Q==",
|
||||
"version": "4.9.2",
|
||||
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz",
|
||||
"integrity": "sha512-LdxoJLZutx0aQXHtWIYwJKMj+9pTjneTcLWJgzf2XbGu0q5pRNqW5QvFCEdm3mc5rJOdru/mzln5d0EZLacf6g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-foreach": "^0.1.3",
|
||||
|
15
package.json
15
package.json
@ -54,9 +54,19 @@
|
||||
"babelify",
|
||||
{
|
||||
"presets": [
|
||||
"es2015",
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"debug": true
|
||||
}
|
||||
],
|
||||
"stage-0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"plugins": [
|
||||
"transform-class-properties"
|
||||
]
|
||||
}
|
||||
],
|
||||
"reactify",
|
||||
@ -211,6 +221,7 @@
|
||||
"babel-plugin-transform-async-to-generator": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-register": "^6.7.2",
|
||||
@ -276,7 +287,7 @@
|
||||
"mocha-jsdom": "^1.1.0",
|
||||
"mocha-sinon": "^2.0.0",
|
||||
"nock": "^9.0.14",
|
||||
"node-sass": "^4.9.1",
|
||||
"node-sass": "^4.9.2",
|
||||
"nsp": "^3.2.1",
|
||||
"nyc": "^13.0.0",
|
||||
"open": "0.0.5",
|
||||
|
@ -472,7 +472,7 @@ describe('MetaMask', function () {
|
||||
await confirmButton.click()
|
||||
await delay(regularDelayMs)
|
||||
|
||||
await closeAllWindowHandlesExcept(driver, [extension, dapp])
|
||||
await waitUntilXWindowHandles(driver, 2)
|
||||
await driver.switchTo().window(extension)
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
@ -10,6 +10,8 @@ const {
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const { fetchLocale } = require('../i18n-helper')
|
||||
const log = require('loglevel')
|
||||
const { ENVIRONMENT_TYPE_NOTIFICATION } = require('../../app/scripts/lib/enums')
|
||||
const { hasUnconfirmedTransactions } = require('./helpers/confirm-transaction/util')
|
||||
|
||||
var actions = {
|
||||
_setBackgroundConnection: _setBackgroundConnection,
|
||||
@ -750,7 +752,7 @@ function setCurrentCurrency (currencyCode) {
|
||||
|
||||
function signMsg (msgData) {
|
||||
log.debug('action - signMsg')
|
||||
return (dispatch) => {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -767,6 +769,12 @@ function signMsg (msgData) {
|
||||
}
|
||||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
})
|
||||
@ -775,7 +783,7 @@ function signMsg (msgData) {
|
||||
|
||||
function signPersonalMsg (msgData) {
|
||||
log.debug('action - signPersonalMsg')
|
||||
return dispatch => {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -792,6 +800,12 @@ function signPersonalMsg (msgData) {
|
||||
}
|
||||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
})
|
||||
@ -800,7 +814,7 @@ function signPersonalMsg (msgData) {
|
||||
|
||||
function signTypedMsg (msgData) {
|
||||
log.debug('action - signTypedMsg')
|
||||
return (dispatch) => {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -817,6 +831,12 @@ function signTypedMsg (msgData) {
|
||||
}
|
||||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
})
|
||||
@ -1010,7 +1030,7 @@ function clearSend () {
|
||||
|
||||
function sendTx (txData) {
|
||||
log.info(`actions - sendTx: ${JSON.stringify(txData.txParams)}`)
|
||||
return (dispatch) => {
|
||||
return (dispatch, getState) => {
|
||||
log.debug(`actions calling background.approveTransaction`)
|
||||
background.approveTransaction(txData.id, (err) => {
|
||||
if (err) {
|
||||
@ -1018,6 +1038,11 @@ function sendTx (txData) {
|
||||
return log.error(err.message)
|
||||
}
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1066,7 +1091,7 @@ function updateTransaction (txData) {
|
||||
|
||||
function updateAndApproveTx (txData) {
|
||||
log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData))
|
||||
return dispatch => {
|
||||
return (dispatch, getState) => {
|
||||
log.debug(`actions calling background.updateAndApproveTx`)
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
@ -1091,6 +1116,12 @@ function updateAndApproveTx (txData) {
|
||||
dispatch(actions.clearSend())
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
|
||||
return txData
|
||||
})
|
||||
}
|
||||
@ -1119,7 +1150,7 @@ function txError (err) {
|
||||
}
|
||||
|
||||
function cancelMsg (msgData) {
|
||||
return dispatch => {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -1133,6 +1164,12 @@ function cancelMsg (msgData) {
|
||||
}
|
||||
|
||||
dispatch(actions.completedTx(msgData.id))
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
})
|
||||
@ -1140,7 +1177,7 @@ function cancelMsg (msgData) {
|
||||
}
|
||||
|
||||
function cancelPersonalMsg (msgData) {
|
||||
return dispatch => {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -1154,6 +1191,12 @@ function cancelPersonalMsg (msgData) {
|
||||
}
|
||||
|
||||
dispatch(actions.completedTx(id))
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
})
|
||||
@ -1161,7 +1204,7 @@ function cancelPersonalMsg (msgData) {
|
||||
}
|
||||
|
||||
function cancelTypedMsg (msgData) {
|
||||
return dispatch => {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -1175,6 +1218,12 @@ function cancelTypedMsg (msgData) {
|
||||
}
|
||||
|
||||
dispatch(actions.completedTx(id))
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
})
|
||||
@ -1182,7 +1231,7 @@ function cancelTypedMsg (msgData) {
|
||||
}
|
||||
|
||||
function cancelTx (txData) {
|
||||
return dispatch => {
|
||||
return (dispatch, getState) => {
|
||||
log.debug(`background.cancelTransaction`)
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
@ -1201,6 +1250,12 @@ function cancelTx (txData) {
|
||||
dispatch(actions.clearSend())
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
|
||||
return txData
|
||||
})
|
||||
}
|
||||
|
61
ui/app/components/button-group/button-group.component.js
Normal file
61
ui/app/components/button-group/button-group.component.js
Normal file
@ -0,0 +1,61 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
|
||||
export default class ButtonGroup extends PureComponent {
|
||||
static propTypes = {
|
||||
defaultActiveButtonIndex: PropTypes.number,
|
||||
disabled: PropTypes.bool,
|
||||
children: PropTypes.array,
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
className: 'button-group',
|
||||
}
|
||||
|
||||
state = {
|
||||
activeButtonIndex: this.props.defaultActiveButtonIndex || 0,
|
||||
}
|
||||
|
||||
handleButtonClick (activeButtonIndex) {
|
||||
this.setState({ activeButtonIndex })
|
||||
}
|
||||
|
||||
renderButtons () {
|
||||
const { children, disabled } = this.props
|
||||
|
||||
return React.Children.map(children, (child, index) => {
|
||||
return child && (
|
||||
<button
|
||||
className={classnames(
|
||||
'button-group__button',
|
||||
{ 'button-group__button--active': index === this.state.activeButtonIndex },
|
||||
)}
|
||||
onClick={() => {
|
||||
this.handleButtonClick(index)
|
||||
child.props.onClick && child.props.onClick()
|
||||
}}
|
||||
disabled={disabled || child.props.disabled}
|
||||
key={index}
|
||||
>
|
||||
{ child.props.children }
|
||||
</button>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, style } = this.props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
{ this.renderButtons() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
49
ui/app/components/button-group/button-group.stories.js
Normal file
49
ui/app/components/button-group/button-group.stories.js
Normal file
@ -0,0 +1,49 @@
|
||||
import React from 'react'
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import { action } from '@storybook/addon-actions'
|
||||
import ButtonGroup from './'
|
||||
import Button from '../button'
|
||||
import { text, boolean } from '@storybook/addon-knobs/react'
|
||||
|
||||
storiesOf('ButtonGroup', module)
|
||||
.add('with Buttons', () =>
|
||||
<ButtonGroup
|
||||
style={{ width: '300px' }}
|
||||
disabled={boolean('Disabled', false)}
|
||||
defaultActiveButtonIndex={1}
|
||||
>
|
||||
<Button
|
||||
onClick={action('cheap')}
|
||||
>
|
||||
{text('Button1', 'Cheap')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={action('average')}
|
||||
>
|
||||
{text('Button2', 'Average')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={action('fast')}
|
||||
>
|
||||
{text('Button3', 'Fast')}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)
|
||||
.add('with a disabled Button', () =>
|
||||
<ButtonGroup
|
||||
style={{ width: '300px' }}
|
||||
disabled={boolean('Disabled', false)}
|
||||
>
|
||||
<Button
|
||||
onClick={action('enabled')}
|
||||
>
|
||||
{text('Button1', 'Enabled')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={action('disabled')}
|
||||
disabled
|
||||
>
|
||||
{text('Button2', 'Disabled')}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)
|
1
ui/app/components/button-group/index.js
Normal file
1
ui/app/components/button-group/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './button-group.component'
|
38
ui/app/components/button-group/index.scss
Normal file
38
ui/app/components/button-group/index.scss
Normal file
@ -0,0 +1,38 @@
|
||||
.button-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&__button {
|
||||
font-family: Roboto;
|
||||
font-size: 1rem;
|
||||
color: $tundora;
|
||||
border-style: solid;
|
||||
border-color: $alto;
|
||||
border-width: 1px 1px 1px;
|
||||
border-left: 0;
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:first-child {
|
||||
border-left: 1px solid $alto;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
&--active {
|
||||
background-color: $dodger-blue;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
import ButtonGroup from '../button-group.component.js'
|
||||
|
||||
const childButtonSpies = {
|
||||
onClick: sinon.spy(),
|
||||
}
|
||||
|
||||
sinon.spy(ButtonGroup.prototype, 'handleButtonClick')
|
||||
sinon.spy(ButtonGroup.prototype, 'renderButtons')
|
||||
|
||||
const mockButtons = [
|
||||
<button onClick={childButtonSpies.onClick} key={'a'}><div className="mockClass" /></button>,
|
||||
<button onClick={childButtonSpies.onClick} key={'b'}></button>,
|
||||
<button onClick={childButtonSpies.onClick} key={'c'}></button>,
|
||||
]
|
||||
|
||||
describe('ButtonGroup Component', function () {
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<ButtonGroup
|
||||
defaultActiveButtonIndex={1}
|
||||
disabled={false}
|
||||
className="someClassName"
|
||||
style={ { color: 'red' } }
|
||||
>{mockButtons}</ButtonGroup>)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
childButtonSpies.onClick.resetHistory()
|
||||
ButtonGroup.prototype.handleButtonClick.resetHistory()
|
||||
ButtonGroup.prototype.renderButtons.resetHistory()
|
||||
})
|
||||
|
||||
describe('handleButtonClick', () => {
|
||||
it('should set the activeButtonIndex', () => {
|
||||
assert.equal(wrapper.state('activeButtonIndex'), 1)
|
||||
wrapper.instance().handleButtonClick(2)
|
||||
assert.equal(wrapper.state('activeButtonIndex'), 2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderButtons', () => {
|
||||
it('should render a button for each child', () => {
|
||||
const childButtons = wrapper.find('.button-group__button')
|
||||
assert.equal(childButtons.length, 3)
|
||||
})
|
||||
|
||||
it('should render the correct button with an active state', () => {
|
||||
const childButtons = wrapper.find('.button-group__button')
|
||||
const activeChildButton = wrapper.find('.button-group__button--active')
|
||||
assert.deepEqual(childButtons.get(1), activeChildButton.get(0))
|
||||
})
|
||||
|
||||
it('should call handleButtonClick and the respective button\'s onClick method when a button is clicked', () => {
|
||||
assert.equal(ButtonGroup.prototype.handleButtonClick.callCount, 0)
|
||||
assert.equal(childButtonSpies.onClick.callCount, 0)
|
||||
const childButtons = wrapper.find('.button-group__button')
|
||||
childButtons.at(0).props().onClick()
|
||||
childButtons.at(1).props().onClick()
|
||||
childButtons.at(2).props().onClick()
|
||||
assert.equal(ButtonGroup.prototype.handleButtonClick.callCount, 3)
|
||||
assert.equal(childButtonSpies.onClick.callCount, 3)
|
||||
})
|
||||
|
||||
it('should render all child buttons as disabled if props.disabled is true', () => {
|
||||
const childButtons = wrapper.find('.button-group__button')
|
||||
childButtons.forEach(button => {
|
||||
assert.equal(button.props().disabled, undefined)
|
||||
})
|
||||
wrapper.setProps({ disabled: true })
|
||||
const disabledChildButtons = wrapper.find('[disabled=true]')
|
||||
assert.equal(disabledChildButtons.length, 3)
|
||||
})
|
||||
|
||||
it('should render the children of the button', () => {
|
||||
const mockClass = wrapper.find('.mockClass')
|
||||
assert.equal(mockClass.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('render', () => {
|
||||
it('should render a div with the expected class and style', () => {
|
||||
assert.equal(wrapper.find('div').at(0).props().className, 'someClassName')
|
||||
assert.deepEqual(wrapper.find('div').at(0).props().style, { color: 'red' })
|
||||
})
|
||||
|
||||
it('should call renderButtons when rendering', () => {
|
||||
assert.equal(ButtonGroup.prototype.renderButtons.callCount, 1)
|
||||
wrapper.instance().render()
|
||||
assert.equal(ButtonGroup.prototype.renderButtons.callCount, 2)
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,64 @@
|
||||
import React from 'react'
|
||||
import assert from 'assert'
|
||||
import { shallow } from 'enzyme'
|
||||
import ConfirmDetailRow from '../confirm-detail-row.component.js'
|
||||
import sinon from 'sinon'
|
||||
|
||||
const propsMethodSpies = {
|
||||
onHeaderClick: sinon.spy(),
|
||||
}
|
||||
|
||||
describe('Confirm Detail Row Component', function () {
|
||||
let wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<ConfirmDetailRow
|
||||
errorType={'mockErrorType'}
|
||||
label={'mockLabel'}
|
||||
showError={false}
|
||||
fiatText = {'mockFiatText'}
|
||||
ethText = {'mockEthText'}
|
||||
fiatTextColor= {'mockColor'}
|
||||
onHeaderClick= {propsMethodSpies.onHeaderClick}
|
||||
headerText = {'mockHeaderText'}
|
||||
headerTextClassName = {'mockHeaderClass'}
|
||||
/>)
|
||||
})
|
||||
|
||||
describe('render', () => {
|
||||
it('should render a div with a confirm-detail-row class', () => {
|
||||
assert.equal(wrapper.find('div.confirm-detail-row').length, 1)
|
||||
})
|
||||
|
||||
it('should render the label as a child of the confirm-detail-row__label', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row > .confirm-detail-row__label').childAt(0).text(), 'mockLabel')
|
||||
})
|
||||
|
||||
it('should render the headerText as a child of the confirm-detail-row__header-text', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__header-text').childAt(0).text(), 'mockHeaderText')
|
||||
})
|
||||
|
||||
it('should render the fiatText as a child of the confirm-detail-row__fiat', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__fiat').childAt(0).text(), 'mockFiatText')
|
||||
})
|
||||
|
||||
it('should render the ethText as a child of the confirm-detail-row__eth', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__eth').childAt(0).text(), 'mockEthText')
|
||||
})
|
||||
|
||||
it('should set the fiatTextColor on confirm-detail-row__fiat', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__fiat').props().style.color, 'mockColor')
|
||||
})
|
||||
|
||||
it('should assure the confirm-detail-row__header-text classname is correct', () => {
|
||||
assert.equal(wrapper.find('.confirm-detail-row__header-text').props().className, 'confirm-detail-row__header-text mockHeaderClass')
|
||||
})
|
||||
|
||||
it('should call onHeaderClick when headerText div gets clicked', () => {
|
||||
wrapper.find('.confirm-detail-row__header-text').props().onClick()
|
||||
assert.equal(assert.equal(propsMethodSpies.onHeaderClick.callCount, 1))
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
})
|
@ -1,3 +1,5 @@
|
||||
@import './button-group/index';
|
||||
|
||||
@import './export-text-container/index';
|
||||
|
||||
@import './selected-account/index';
|
||||
|
@ -27,19 +27,17 @@ const {
|
||||
NOTICE_ROUTE,
|
||||
} = require('../../routes')
|
||||
|
||||
const { unconfirmedTransactionsCountSelector } = require('../../selectors/confirm-transaction')
|
||||
|
||||
class Home extends Component {
|
||||
componentDidMount () {
|
||||
const {
|
||||
history,
|
||||
unapprovedTxs = {},
|
||||
unapprovedMsgCount = 0,
|
||||
unapprovedPersonalMsgCount = 0,
|
||||
unapprovedTypedMessagesCount = 0,
|
||||
unconfirmedTransactionsCount = 0,
|
||||
} = this.props
|
||||
|
||||
// unapprovedTxs and unapproved messages
|
||||
if (Object.keys(unapprovedTxs).length ||
|
||||
unapprovedTypedMessagesCount + unapprovedMsgCount + unapprovedPersonalMsgCount > 0) {
|
||||
if (unconfirmedTransactionsCount > 0) {
|
||||
history.push(CONFIRM_TRANSACTION_ROUTE)
|
||||
}
|
||||
}
|
||||
@ -167,6 +165,7 @@ Home.propTypes = {
|
||||
isPopup: PropTypes.bool,
|
||||
isMouseUser: PropTypes.bool,
|
||||
t: PropTypes.func,
|
||||
unconfirmedTransactionsCount: PropTypes.number,
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
@ -230,6 +229,7 @@ function mapStateToProps (state) {
|
||||
|
||||
// state needed to get account dropdown temporarily rendering from app bar
|
||||
selected,
|
||||
unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,14 +213,23 @@ TxListItem.prototype.showRetryButton = function () {
|
||||
if (!txParams) {
|
||||
return false
|
||||
}
|
||||
let currentTxIsLatest = false
|
||||
const currentNonce = txParams.nonce
|
||||
const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
|
||||
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
||||
const currentSubmittedTxs = selectedAddressTxList.filter(tx => tx.status === 'submitted')
|
||||
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[currentNonceSubmittedTxs.length - 1]
|
||||
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
||||
lastSubmittedTxWithCurrentNonce.id === transactionId
|
||||
if (currentSubmittedTxs.length > 0) {
|
||||
const lastTx = currentSubmittedTxs.reduce((tx1, tx2) => {
|
||||
if (tx1.submittedTime < tx2.submittedTime) return tx1
|
||||
return tx2
|
||||
})
|
||||
currentTxIsLatest = lastTx.id === transactionId
|
||||
}
|
||||
|
||||
return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
|
||||
return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000 && currentTxIsLatest
|
||||
}
|
||||
|
||||
TxListItem.prototype.setSelectedToken = function (tokenAddress) {
|
||||
|
@ -16,6 +16,8 @@ import {
|
||||
conversionGreaterThan,
|
||||
} from '../../conversion-util'
|
||||
|
||||
import { unconfirmedTransactionsCountSelector } from '../../selectors/confirm-transaction'
|
||||
|
||||
export function getTokenData (data = {}) {
|
||||
return abiDecoder.decodeMethod(data)
|
||||
}
|
||||
@ -131,3 +133,7 @@ export function convertTokenToFiat ({
|
||||
conversionRate: totalExchangeRate,
|
||||
})
|
||||
}
|
||||
|
||||
export function hasUnconfirmedTransactions (state) {
|
||||
return unconfirmedTransactionsCountSelector(state) > 0
|
||||
}
|
||||
|
@ -62,6 +62,34 @@ export const unconfirmedTransactionsHashSelector = createSelector(
|
||||
}
|
||||
)
|
||||
|
||||
const unapprovedMsgCountSelector = state => state.metamask.unapprovedMsgCount
|
||||
const unapprovedPersonalMsgCountSelector = state => state.metamask.unapprovedPersonalMsgCount
|
||||
const unapprovedTypedMessagesCountSelector = state => state.metamask.unapprovedTypedMessagesCount
|
||||
|
||||
export const unconfirmedTransactionsCountSelector = createSelector(
|
||||
unapprovedTxsSelector,
|
||||
unapprovedMsgCountSelector,
|
||||
unapprovedPersonalMsgCountSelector,
|
||||
unapprovedTypedMessagesCountSelector,
|
||||
networkSelector,
|
||||
(
|
||||
unapprovedTxs = {},
|
||||
unapprovedMsgCount = 0,
|
||||
unapprovedPersonalMsgCount = 0,
|
||||
unapprovedTypedMessagesCount = 0,
|
||||
network
|
||||
) => {
|
||||
const filteredUnapprovedTxIds = Object.keys(unapprovedTxs).filter(txId => {
|
||||
const { metamaskNetworkId } = unapprovedTxs[txId]
|
||||
return metamaskNetworkId === network
|
||||
})
|
||||
|
||||
return filteredUnapprovedTxIds.length + unapprovedTypedMessagesCount + unapprovedMsgCount +
|
||||
unapprovedPersonalMsgCount
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
export const currentCurrencySelector = state => state.metamask.currentCurrency
|
||||
export const conversionRateSelector = state => state.metamask.conversionRate
|
||||
|
||||
@ -156,7 +184,6 @@ export const sendTokenTokenAmountAndToAddressSelector = createSelector(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
export const contractExchangeRateSelector = createSelector(
|
||||
contractExchangeRatesSelector,
|
||||
tokenAddressSelector,
|
||||
|
Loading…
Reference in New Issue
Block a user