aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG.md2
-rw-r--r--app/_locales/es/messages.json10
-rw-r--r--app/_locales/es_419/messages.json10
-rw-r--r--app/_locales/zh_CN/messages.json10
-rw-r--r--app/scripts/inpage.js2
-rw-r--r--app/scripts/lib/config-manager.js6
-rw-r--r--app/scripts/lib/inpage-provider.js30
-rw-r--r--package.json3
-rw-r--r--test/unit/account-link-test.js12
-rw-r--r--test/unit/config-manager-test.js11
-rw-r--r--ui/app/account-detail.js13
-rw-r--r--ui/app/components/account-info-link.js42
-rw-r--r--ui/app/components/shift-list-item.js2
-rw-r--r--ui/app/components/transaction-list-item.js3
-rw-r--r--ui/app/components/transaction-list.js29
-rw-r--r--ui/lib/account-link.js18
17 files changed, 186 insertions, 18 deletions
diff --git a/.gitignore b/.gitignore
index 689cc3240..fa8a9151f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ builds/
notes.txt
app/.DS_Store
development/bundle.js
+builds.zip
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2193342c..2b5e55e22 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
## Current Master
- Added static image as fallback for when WebGL isn't supported.
+- Transaction history now has a hard limit.
+- Added info link on account screen that visits Etherscan.
## 2.9.0 2016-08-22
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
new file mode 100644
index 000000000..78fc64dbf
--- /dev/null
+++ b/app/_locales/es/messages.json
@@ -0,0 +1,10 @@
+{
+ "appName": {
+ "message": "MetaMask",
+ "description": "The name of the application"
+ },
+ "appDescription": {
+ "message": "Administración de identidad en Ethereum",
+ "description": "The description of the application"
+ }
+}
diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json
new file mode 100644
index 000000000..78fc64dbf
--- /dev/null
+++ b/app/_locales/es_419/messages.json
@@ -0,0 +1,10 @@
+{
+ "appName": {
+ "message": "MetaMask",
+ "description": "The name of the application"
+ },
+ "appDescription": {
+ "message": "Administración de identidad en Ethereum",
+ "description": "The description of the application"
+ }
+}
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
new file mode 100644
index 000000000..fc87384e5
--- /dev/null
+++ b/app/_locales/zh_CN/messages.json
@@ -0,0 +1,10 @@
+{
+ "appName": {
+ "message": "MetaMask",
+ "description": "The name of the application"
+ },
+ "appDescription": {
+ "message": "以太坊身份管理",
+ "description": "The description of the application"
+ }
+}
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 7c508c66f..28a1223ac 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -54,7 +54,7 @@ var __define
function cleanContextForImports () {
__define = global.define
try {
- delete global.define
+ global.define = undefined
} catch (_) {
console.warn('MetaMask - global.define could not be deleted.')
}
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 6f5cb3a4a..4d270bcdb 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -5,6 +5,7 @@ const rp = require('request-promise')
const TESTNET_RPC = MetamaskConfig.network.testnet
const MAINNET_RPC = MetamaskConfig.network.mainnet
+const txLimit = 40
/* The config-manager is a convenience object
* wrapping a pojo-migrator.
@@ -15,6 +16,8 @@ const MAINNET_RPC = MetamaskConfig.network.mainnet
*/
module.exports = ConfigManager
function ConfigManager (opts) {
+ this.txLimit = txLimit
+
// ConfigManager is observable and will emit updates
this._subs = []
@@ -181,6 +184,9 @@ ConfigManager.prototype._saveTxList = function (txList) {
ConfigManager.prototype.addTx = function (tx) {
var transactions = this.getTxList()
+ while (transactions.length > this.txLimit - 1) {
+ transactions.shift()
+ }
transactions.push(tx)
this._saveTxList(transactions)
}
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index b3ed3d9e2..65354cd3d 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -33,8 +33,16 @@ function MetamaskInpageProvider (connectionStream) {
})
asyncProvider.on('error', console.error.bind(console))
self.asyncProvider = asyncProvider
- // overwrite own sendAsync method
- self.sendAsync = asyncProvider.sendAsync.bind(asyncProvider)
+ // handle sendAsync requests via asyncProvider
+ self.sendAsync = function(payload, cb){
+ // rewrite request ids
+ var request = jsonrpcMessageTransform(payload, (message) => {
+ message.id = createRandomId()
+ return message
+ })
+ // forward to asyncProvider
+ asyncProvider.sendAsync(request, cb)
+ }
}
MetamaskInpageProvider.prototype.send = function (payload) {
@@ -92,3 +100,21 @@ function remoteStoreWithLocalStorageCache (storageKey) {
return store
}
+
+function createRandomId(){
+ const extraDigits = 3
+ // 13 time digits
+ const datePart = new Date().getTime() * Math.pow(10, extraDigits)
+ // 3 random digits
+ const extraPart = Math.floor(Math.random() * Math.pow(10, extraDigits))
+ // 16 digits
+ return datePart + extraPart
+}
+
+function jsonrpcMessageTransform(payload, transformFn){
+ if (Array.isArray(payload)) {
+ return payload.map(transformFn)
+ } else {
+ return transformFn(payload)
+ }
+} \ No newline at end of file
diff --git a/package.json b/package.json
index 963831029..7df61a2bb 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,9 @@
"private": true,
"scripts": {
"start": "gulp dev",
+ "lint": "gulp lint",
+ "dev": "gulp dev",
+ "dist": "gulp dist",
"test": "npm run fastTest && npm run ci",
"fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
"watch": "mocha watch --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
diff --git a/test/unit/account-link-test.js b/test/unit/account-link-test.js
new file mode 100644
index 000000000..39889b6be
--- /dev/null
+++ b/test/unit/account-link-test.js
@@ -0,0 +1,12 @@
+var assert = require('assert')
+var linkGen = require('../../ui/lib/account-link')
+
+describe('account-link', function() {
+
+ it('adds testnet prefix to morden test network', function() {
+ var result = linkGen('account', '2')
+ assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected')
+ assert.notEqual(result.indexOf('account'), -1, 'account included')
+ })
+
+})
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
index b34089163..eaa5376fd 100644
--- a/test/unit/config-manager-test.js
+++ b/test/unit/config-manager-test.js
@@ -233,6 +233,17 @@ describe('config-manager', function() {
assert.equal(result.length, 1)
assert.equal(result[0].id, 1)
})
+
+ it('cuts off early txs beyond a limit', function() {
+ const limit = configManager.txLimit
+ for (let i = 0; i < limit + 1; i++) {
+ let tx = { id: i }
+ configManager.addTx(tx)
+ }
+ var result = configManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 1, 'early txs truncted')
+ })
})
describe('#confirmTx', function() {
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
index 836f4bcb9..486a1a633 100644
--- a/ui/app/account-detail.js
+++ b/ui/app/account-detail.js
@@ -4,6 +4,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const CopyButton = require('./components/copyButton')
+const AccountInfoLink = require('./components/account-info-link')
const actions = require('./actions')
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const valuesFor = require('./util').valuesFor
@@ -44,6 +45,7 @@ AccountDetailScreen.prototype.render = function () {
var selected = props.address || Object.keys(props.accounts)[0]
var identity = props.identities[selected]
var account = props.accounts[selected]
+ const { network } = props
return (
@@ -127,6 +129,9 @@ AccountDetailScreen.prototype.render = function () {
bottom: '15px',
},
}, [
+
+ h(AccountInfoLink, { selected, network }),
+
h(CopyButton, {
value: ethUtil.toChecksumAddress(selected),
}),
@@ -136,16 +141,15 @@ AccountDetailScreen.prototype.render = function () {
}, [
h('div', {
style: {
- margin: '5px',
+ display: 'flex',
+ alignItems: 'center',
},
}, [
h('img.cursor-pointer.color-orange', {
src: 'images/key-32.png',
onClick: () => this.requestAccountExport(selected),
style: {
- margin: '0px 5px',
- width: '20px',
- height: '20px',
+ height: '19px',
},
}),
]),
@@ -244,6 +248,7 @@ AccountDetailScreen.prototype.transactionList = function () {
network,
unconfTxs,
unconfMsgs,
+ address,
shapeShiftTxList,
viewPendingTx: (txId) => {
this.props.dispatch(actions.viewPendingTx(txId))
diff --git a/ui/app/components/account-info-link.js b/ui/app/components/account-info-link.js
new file mode 100644
index 000000000..4fe3b8b5d
--- /dev/null
+++ b/ui/app/components/account-info-link.js
@@ -0,0 +1,42 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const Tooltip = require('./tooltip')
+const genAccountLink = require('../../lib/account-link')
+const extension = require('../../../app/scripts/lib/extension')
+
+module.exports = AccountInfoLink
+
+inherits(AccountInfoLink, Component)
+function AccountInfoLink () {
+ Component.call(this)
+}
+
+AccountInfoLink.prototype.render = function () {
+ const { selected, network } = this.props
+ const title = 'View account on etherscan'
+ const url = genAccountLink(selected, network)
+
+ if (!url) {
+ return null
+ }
+
+ return h('.account-info-link', {
+ style: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+ }, [
+
+ h(Tooltip, {
+ title,
+ }, [
+ h('i.fa.fa-info-circle.cursor-pointer.color-orange', {
+ style: {
+ margin: '5px',
+ },
+ onClick () { extension.tabs.create({ url }) },
+ }),
+ ]),
+ ])
+}
diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js
index 11e11cd37..38c19eb28 100644
--- a/ui/app/components/shift-list-item.js
+++ b/ui/app/components/shift-list-item.js
@@ -26,6 +26,7 @@ function ShiftListItem () {
}
ShiftListItem.prototype.render = function () {
+
return (
h('.transaction-list-item.flex-row', {
style: {
@@ -113,7 +114,6 @@ ShiftListItem.prototype.renderUtilComponents = function () {
default:
return ''
}
-
}
ShiftListItem.prototype.renderInfo = function () {
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
index b03ca11ad..1b85464e1 100644
--- a/ui/app/components/transaction-list-item.js
+++ b/ui/app/components/transaction-list-item.js
@@ -19,7 +19,7 @@ function TransactionListItem () {
}
TransactionListItem.prototype.render = function () {
- const { transaction, i, network } = this.props
+ const { transaction, network } = this.props
if (transaction.key === 'shapeshift') {
if (network === '1') return h(ShiftListItem, transaction)
}
@@ -44,7 +44,6 @@ TransactionListItem.prototype.render = function () {
return (
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
- key: `tx-${transaction.id + i}`,
onClick: (event) => {
if (isPending) {
this.props.showTx(transaction.id)
diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js
index 9bf0f6d81..7e1bedb05 100644
--- a/ui/app/components/transaction-list.js
+++ b/ui/app/components/transaction-list.js
@@ -15,7 +15,7 @@ function TransactionList () {
TransactionList.prototype.render = function () {
const { txsToRender, network, unconfMsgs } = this.props
var shapeShiftTxList
- if (network === '1'){
+ if (network === '1') {
shapeShiftTxList = this.props.shapeShiftTxList
}
const transactions = !shapeShiftTxList ? txsToRender.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList)
@@ -43,33 +43,46 @@ TransactionList.prototype.render = function () {
paddingBottom: '4px',
},
}, [
- 'Transactions',
+ 'History',
]),
h('.tx-list', {
style: {
overflowY: 'auto',
- height: '305px',
+ height: '300px',
padding: '0 20px',
textAlign: 'center',
},
- }, (
+ }, [
transactions.length
? transactions.map((transaction, i) => {
+ let key
+ switch (transaction.key) {
+ case 'shapeshift':
+ const { depositAddress, time } = transaction
+ key = `shift-tx-${depositAddress}-${time}-${i}`
+ break
+ default:
+ key = `tx-${transaction.id}-${i}`
+ }
return h(TransactionListItem, {
- transaction, i, network,
+ transaction, i, network, key,
showTx: (txId) => {
this.props.viewPendingTx(txId)
},
})
})
- : [h('.flex-center', {
+ : h('.flex-center', {
style: {
+ flexDirection: 'column',
height: '100%',
},
- }, 'No transaction history...')]
- )),
+ }, [
+ 'No transaction history.',
+ ]),
+ ]),
])
)
}
+
diff --git a/ui/lib/account-link.js b/ui/lib/account-link.js
new file mode 100644
index 000000000..eb958e22d
--- /dev/null
+++ b/ui/lib/account-link.js
@@ -0,0 +1,18 @@
+module.exports = function(address, network) {
+ const net = parseInt(network)
+ let link
+
+ switch (net) {
+ case 1: // main net
+ link = `http://etherscan.io/address/${address}`
+ break
+ case 2: // morden test net
+ link = `http://testnet.etherscan.io/address/${address}`
+ break
+ default:
+ link = ''
+ break
+ }
+
+ return link
+}