aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--README.md2
-rw-r--r--app/scripts/controllers/transactions/tx-state-manager.js6
-rw-r--r--app/scripts/migrations/027.js35
-rw-r--r--test/unit/app/controllers/transactions/tx-controller-test.js13
-rw-r--r--test/unit/app/controllers/transactions/tx-state-manager-test.js19
-rw-r--r--test/unit/migrations/027-test.js50
-rw-r--r--ui/app/app.js3
-rw-r--r--ui/app/components/pages/unlock-page/unlock-page.component.js72
-rw-r--r--ui/app/main-container.js3
10 files changed, 160 insertions, 45 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d86fcd713..4ad52b795 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## Current Master
+ - Remove rejected transactions from transaction history
+
## 4.8.0 Thur Jun 14 2018
- [#4513](https://github.com/MetaMask/metamask-extension/pull/4513): Attempting to import an empty private key will now show a clear error.
diff --git a/README.md b/README.md
index 70faa8856..513a1d1cb 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ If you're a user seeking support, [here is our support site](https://metamask.he
[Mission Statement](./MISSION.md)
-[Internal documentation](./docs/jsdocs)
+[Internal documentation](./docs#documentation)
## Developing Compatible Dapps
diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js
index 0aae4774b..28a18ca2e 100644
--- a/app/scripts/controllers/transactions/tx-state-manager.js
+++ b/app/scripts/controllers/transactions/tx-state-manager.js
@@ -288,6 +288,7 @@ class TransactionStateManager extends EventEmitter {
*/
setTxStatusRejected (txId) {
this._setTxStatus(txId, 'rejected')
+ this._removeTx(txId)
}
/**
@@ -422,6 +423,11 @@ class TransactionStateManager extends EventEmitter {
_saveTxList (transactions) {
this.store.updateState({ transactions })
}
+
+ _removeTx (txId) {
+ const transactionList = this.getFullTxList()
+ this._saveTxList(transactionList.filter((txMeta) => txMeta.id !== txId))
+ }
}
module.exports = TransactionStateManager
diff --git a/app/scripts/migrations/027.js b/app/scripts/migrations/027.js
new file mode 100644
index 000000000..d6ebef580
--- /dev/null
+++ b/app/scripts/migrations/027.js
@@ -0,0 +1,35 @@
+// next version number
+const version = 27
+
+/*
+
+normalizes txParams on unconfirmed txs
+
+*/
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: async function (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ return versionedData
+ },
+}
+
+function transformState (state) {
+ const newState = state
+
+ if (newState.TransactionController) {
+ if (newState.TransactionController.transactions) {
+ const transactions = newState.TransactionController.transactions
+ newState.TransactionController.transactions = transactions.filter((txMeta) => txMeta.status !== 'rejected')
+ }
+ }
+
+ return newState
+}
diff --git a/test/unit/app/controllers/transactions/tx-controller-test.js b/test/unit/app/controllers/transactions/tx-controller-test.js
index d4c5ff0ec..26dc7b656 100644
--- a/test/unit/app/controllers/transactions/tx-controller-test.js
+++ b/test/unit/app/controllers/transactions/tx-controller-test.js
@@ -353,9 +353,16 @@ describe('Transaction Controller', function () {
])
})
- it('should set the transaction to rejected from unapproved', async function () {
- await txController.cancelTransaction(0)
- assert.equal(txController.txStateManager.getTx(0).status, 'rejected')
+ it('should emit a status change to rejected', function (done) {
+ txController.once('tx:status-update', (txId, status) => {
+ try {
+ assert.equal(status, 'rejected', 'status should e rejected')
+ assert.equal(txId, 0, 'id should e 0')
+ done()
+ } catch (e) { done(e) }
+ })
+
+ txController.cancelTransaction(0)
})
})
diff --git a/test/unit/app/controllers/transactions/tx-state-manager-test.js b/test/unit/app/controllers/transactions/tx-state-manager-test.js
index 2f91b1545..88bdaa60e 100644
--- a/test/unit/app/controllers/transactions/tx-state-manager-test.js
+++ b/test/unit/app/controllers/transactions/tx-state-manager-test.js
@@ -43,14 +43,13 @@ describe('TransactionStateManager', function () {
})
describe('#setTxStatusRejected', function () {
- it('sets the tx status to rejected', function () {
+ it('sets the tx status to rejected and removes it from history', function () {
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
txStateManager.addTx(tx)
txStateManager.setTxStatusRejected(1)
const result = txStateManager.getTxList()
assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].status, 'rejected')
+ assert.equal(result.length, 0)
})
it('should emit a rejected event to signal the exciton of callback', (done) => {
@@ -287,4 +286,18 @@ describe('TransactionStateManager', function () {
})
})
+
+ describe('#_removeTx', function () {
+ it('should remove the transaction from the storage', () => {
+ txStateManager._saveTxList([ {id: 1} ])
+ txStateManager._removeTx(1)
+ assert(!txStateManager.getFullTxList().length, 'txList should be empty')
+ })
+
+ it('should only remove the transaction with ID 1 from the storage', () => {
+ txStateManager._saveTxList([ {id: 1}, {id: 2} ])
+ txStateManager._removeTx(1)
+ assert.equal(txStateManager.getFullTxList()[0].id, 2, 'txList should have a id of 2')
+ })
+ })
})
diff --git a/test/unit/migrations/027-test.js b/test/unit/migrations/027-test.js
new file mode 100644
index 000000000..3ec9f0c0e
--- /dev/null
+++ b/test/unit/migrations/027-test.js
@@ -0,0 +1,50 @@
+const assert = require('assert')
+const migration27 = require('../../../app/scripts/migrations/027')
+
+const oldStorage = {
+ 'meta': {},
+ 'data': {
+ 'TransactionController': {
+ 'transactions': [
+ ],
+ },
+ },
+}
+
+const transactions = []
+
+
+while (transactions.length < 9) {
+ transactions.push({status: 'rejected'})
+ transactions.push({status: 'unapproved'})
+ transactions.push({status: 'approved'})
+}
+
+
+oldStorage.data.TransactionController.transactions = transactions
+
+describe('migration #27', () => {
+ it('should remove rejected transactions', (done) => {
+ migration27.migrate(oldStorage)
+ .then((newStorage) => {
+ const newTransactions = newStorage.data.TransactionController.transactions
+ assert.equal(newTransactions.length, 6, 'transactions is expected to have the length of 6')
+ newTransactions.forEach((txMeta) => {
+ if (txMeta.status === 'rejected') done(new Error('transaction was found with a status of rejected'))
+ })
+ done()
+ })
+ .catch(done)
+ })
+
+ it('should successfully migrate first time state', (done) => {
+ migration27.migrate({
+ meta: {},
+ data: require('../../../app/scripts/first-time-state'),
+ })
+ .then((migratedData) => {
+ assert.equal(migratedData.meta.version, migration27.version)
+ done()
+ }).catch(done)
+ })
+})
diff --git a/ui/app/app.js b/ui/app/app.js
index 4e600fe26..74d360d3c 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -22,7 +22,6 @@ const Home = require('./components/pages/home')
const Authenticated = require('./components/pages/authenticated')
const Initialized = require('./components/pages/initialized')
const Settings = require('./components/pages/settings')
-const UnlockPage = require('./components/pages/unlock-page')
const RestoreVaultPage = require('./components/pages/keychains/restore-vault').default
const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed')
const AddTokenPage = require('./components/pages/add-token')
@@ -40,6 +39,8 @@ const Modal = require('./components/modals/index').Modal
const AppHeader = require('./components/app-header')
+import UnlockPage from './components/pages/unlock-page'
+
// Routes
const {
DEFAULT_ROUTE,
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 a1d3f9181..94915df76 100644
--- a/ui/app/components/pages/unlock-page/unlock-page.component.js
+++ b/ui/app/components/pages/unlock-page/unlock-page.component.js
@@ -2,19 +2,27 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Button from '@material-ui/core/Button'
import TextField from '../../text-field'
-
-const { ENVIRONMENT_TYPE_POPUP } = require('../../../../../app/scripts/lib/enums')
-const { getEnvironmentType } = require('../../../../../app/scripts/lib/util')
-const getCaretCoordinates = require('textarea-caret')
-const EventEmitter = require('events').EventEmitter
-const Mascot = require('../../mascot')
-const { DEFAULT_ROUTE, RESTORE_VAULT_ROUTE } = require('../../../routes')
-
-class UnlockPage extends Component {
+import { ENVIRONMENT_TYPE_POPUP } from '../../../../../app/scripts/lib/enums'
+import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
+import getCaretCoordinates from 'textarea-caret'
+import { EventEmitter } from 'events'
+import Mascot from '../../mascot'
+import { DEFAULT_ROUTE, RESTORE_VAULT_ROUTE } from '../../../routes'
+
+export default class UnlockPage extends Component {
static contextTypes = {
t: PropTypes.func,
}
+ static propTypes = {
+ forgotPassword: PropTypes.func,
+ tryUnlockMetamask: PropTypes.func,
+ markPasswordForgotten: PropTypes.func,
+ history: PropTypes.object,
+ isUnlocked: PropTypes.bool,
+ useOldInterface: PropTypes.func,
+ }
+
constructor (props) {
super(props)
@@ -23,6 +31,7 @@ class UnlockPage extends Component {
error: null,
}
+ this.submitting = false
this.animationEventEmitter = new EventEmitter()
}
@@ -41,20 +50,21 @@ class UnlockPage extends Component {
const { password } = this.state
const { tryUnlockMetamask, history } = this.props
- if (password === '') {
+ if (password === '' || this.submitting) {
return
}
this.setState({ error: null })
+ this.submitting = true
try {
await tryUnlockMetamask(password)
+ this.submitting = false
+ history.push(DEFAULT_ROUTE)
} catch ({ message }) {
this.setState({ error: message })
- return
+ this.submitting = false
}
-
- history.push(DEFAULT_ROUTE)
}
handleInputChange ({ target }) {
@@ -98,7 +108,9 @@ class UnlockPage extends Component {
}
render () {
- const { error } = this.state
+ const { password, error } = this.state
+ const { t } = this.context
+ const { markPasswordForgotten, history } = this.props
return (
<div className="unlock-page__container">
@@ -111,18 +123,18 @@ class UnlockPage extends Component {
/>
</div>
<h1 className="unlock-page__title">
- { this.context.t('welcomeBack') }
+ { t('welcomeBack') }
</h1>
- <div>{ this.context.t('unlockMessage') }</div>
+ <div>{ t('unlockMessage') }</div>
<form
className="unlock-page__form"
onSubmit={event => this.handleSubmit(event)}
>
<TextField
id="password"
- label={this.context.t('password')}
+ label={t('password')}
type="password"
- value={this.state.password}
+ value={password}
onChange={event => this.handleInputChange(event)}
error={error}
autoFocus
@@ -136,28 +148,28 @@ class UnlockPage extends Component {
<div
className="unlock-page__link"
onClick={() => {
- this.props.markPasswordForgotten()
- this.props.history.push(RESTORE_VAULT_ROUTE)
+ markPasswordForgotten()
+ history.push(RESTORE_VAULT_ROUTE)
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
global.platform.openExtensionInBrowser()
}
}}
>
- { this.context.t('restoreFromSeed') }
+ { t('restoreFromSeed') }
</div>
<div
className="unlock-page__link unlock-page__link--import"
onClick={() => {
- this.props.markPasswordForgotten()
- this.props.history.push(RESTORE_VAULT_ROUTE)
+ markPasswordForgotten()
+ history.push(RESTORE_VAULT_ROUTE)
if (getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP) {
global.platform.openExtensionInBrowser()
}
}}
>
- { this.context.t('importUsingSeed') }
+ { t('importUsingSeed') }
</div>
</div>
</div>
@@ -165,15 +177,3 @@ class UnlockPage extends Component {
)
}
}
-
-UnlockPage.propTypes = {
- forgotPassword: PropTypes.func,
- tryUnlockMetamask: PropTypes.func,
- markPasswordForgotten: PropTypes.func,
- history: PropTypes.object,
- isUnlocked: PropTypes.bool,
- t: PropTypes.func,
- useOldInterface: PropTypes.func,
-}
-
-export default UnlockPage
diff --git a/ui/app/main-container.js b/ui/app/main-container.js
index b49a52363..8a0708025 100644
--- a/ui/app/main-container.js
+++ b/ui/app/main-container.js
@@ -3,9 +3,10 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const AccountAndTransactionDetails = require('./account-and-transaction-details')
const Settings = require('./components/pages/settings')
-const UnlockScreen = require('./components/pages/unlock-page')
const log = require('loglevel')
+import UnlockScreen from './components/pages/unlock-page'
+
module.exports = MainContainer
inherits(MainContainer, Component)