aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.babelrc4
-rw-r--r--app/scripts/controllers/transactions.js1
-rw-r--r--development/states/token-list.json93
-rw-r--r--package.json4
-rw-r--r--ui/app/account-detail.js44
-rw-r--r--ui/app/components/account-export.js2
-rw-r--r--ui/app/components/balance.js89
-rw-r--r--ui/app/components/identicon.js10
-rw-r--r--ui/app/components/token-cell.js31
-rw-r--r--ui/app/components/token-list.js72
-rw-r--r--ui/app/components/transaction-list.js11
-rw-r--r--ui/lib/icon-factory.js1
12 files changed, 342 insertions, 20 deletions
diff --git a/.babelrc b/.babelrc
index 3ca197980..bca3364fc 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,4 +1,4 @@
{
- "presets": ["es2015"],
- "plugins": ["transform-runtime"]
+ "presets": ["es2015", "stage-0"],
+ "plugins": ["transform-runtime", "transform-async-to-generator"]
}
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index faccf1ab1..8be73fad8 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -382,6 +382,7 @@ module.exports = class TransactionController extends EventEmitter {
// - `'signed'` the tx is signed
// - `'submitted'` the tx is sent to a server
// - `'confirmed'` the tx has been included in a block.
+ // - `'failed'` the tx failed for some reason, included on tx data.
_setTxStatus (txId, status) {
var txMeta = this.getTx(txId)
txMeta.status = status
diff --git a/development/states/token-list.json b/development/states/token-list.json
new file mode 100644
index 000000000..404f1aedd
--- /dev/null
+++ b/development/states/token-list.json
@@ -0,0 +1,93 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0x55e2780588aa5000f464f700d2676fd0a22ee160": {
+ "address": "0x55e2780588aa5000f464f700d2676fd0a22ee160",
+ "name": "Account 1"
+ },
+ "0x1c3d6d41dcb245c11a449ec46c9cf9eb7dada4af": {
+ "address": "0x1c3d6d41dcb245c11a449ec46c9cf9eb7dada4af",
+ "name": "Account 2"
+ },
+ "0xe34b1ac3074121418152c7a68b4ae6cb7803d725": {
+ "address": "0xe34b1ac3074121418152c7a68b4ae6cb7803d725",
+ "name": "Account 3"
+ }
+ },
+ "unapprovedTxs": {},
+ "noActiveNotices": true,
+ "frequentRpcList": [],
+ "addressBook": [],
+ "network": "1",
+ "accounts": {
+ "0x55e2780588aa5000f464f700d2676fd0a22ee160": {
+ "balance": "0x4622f471c28b8a53",
+ "nonce": "0x17",
+ "code": "0x",
+ "address": "0x55e2780588aa5000f464f700d2676fd0a22ee160"
+ },
+ "0x1c3d6d41dcb245c11a449ec46c9cf9eb7dada4af": {
+ "balance": "0x0",
+ "nonce": "0x0",
+ "code": "0x",
+ "address": "0x1c3d6d41dcb245c11a449ec46c9cf9eb7dada4af"
+ },
+ "0xe34b1ac3074121418152c7a68b4ae6cb7803d725": {
+ "balance": "0x0",
+ "nonce": "0x0",
+ "code": "0x",
+ "address": "0xe34b1ac3074121418152c7a68b4ae6cb7803d725"
+ }
+ },
+ "transactions": {},
+ "currentBlockNumber": 3575443,
+ "currentBlockHash": "0xf03bdb8ad844336723473865a5368fa618de837d8290ad380fadbc9fa2bf87f6",
+ "selectedAddressTxList": [],
+ "unapprovedMsgs": {},
+ "unapprovedMsgCount": 0,
+ "unapprovedPersonalMsgs": {},
+ "unapprovedPersonalMsgCount": 0,
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "keyrings": [
+ {
+ "type": "HD Key Tree",
+ "accounts": [
+ "55e2780588aa5000f464f700d2676fd0a22ee160",
+ "1c3d6d41dcb245c11a449ec46c9cf9eb7dada4af",
+ "e34b1ac3074121418152c7a68b4ae6cb7803d725"
+ ]
+ }
+ ],
+ "selectedAddress": "0x55e2780588aa5000f464f700d2676fd0a22ee160",
+ "currentCurrency": "USD",
+ "conversionRate": 51.12009214,
+ "conversionDate": 1492788481,
+ "provider": {
+ "type": "mainnet"
+ },
+ "shapeShiftTxList": [],
+ "lostAccounts": []
+ },
+ "appState": {
+ "shouldClose": false,
+ "menuOpen": false,
+ "currentView": {
+ "name": "accountDetail",
+ "detailView": "tokens",
+ "context": "0x55e2780588aa5000f464f700d2676fd0a22ee160"
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/package.json b/package.json
index 2c23d9e10..07f2c488c 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
"eth-query": "^2.1.1",
"eth-sig-util": "^1.1.1",
"eth-simple-keyring": "^1.1.1",
+ "eth-token-tracker": "^1.0.4",
"ethereumjs-tx": "^1.3.0",
"ethereumjs-util": "ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
"ethereumjs-wallet": "^0.6.0",
@@ -128,8 +129,11 @@
"xtend": "^4.0.1"
},
"devDependencies": {
+ "babel-core": "^6.24.1",
"babel-eslint": "^6.0.5",
+ "babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
+ "babel-polyfill": "^6.23.0",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.7.2",
"babelify": "^7.2.0",
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
index 7a78a360c..5b2588ec5 100644
--- a/ui/app/account-detail.js
+++ b/ui/app/account-detail.js
@@ -16,6 +16,9 @@ const ExportAccountView = require('./components/account-export')
const ethUtil = require('ethereumjs-util')
const EditableLabel = require('./components/editable-label')
const Tooltip = require('./components/tooltip')
+const TabBar = require('./components/tab-bar')
+const TokenList = require('./components/token-list')
+
module.exports = connect(mapStateToProps)(AccountDetailScreen)
function mapStateToProps (state) {
@@ -36,6 +39,7 @@ function mapStateToProps (state) {
inherits(AccountDetailScreen, Component)
function AccountDetailScreen () {
+ this.state = {}
Component.call(this)
}
@@ -237,11 +241,48 @@ AccountDetailScreen.prototype.subview = function () {
switch (subview) {
case 'transactions':
- return this.transactionList()
+ return this.tabSections()
case 'export':
var state = extend({key: 'export'}, this.props)
return h(ExportAccountView, state)
default:
+ return this.tabSections()
+ }
+}
+
+AccountDetailScreen.prototype.tabSections = function () {
+ var subview
+ try {
+ subview = this.props.accountDetail.subview
+ } catch (e) {
+ subview = null
+ }
+
+ return h('section.tabSection', [
+
+ h(TabBar, {
+ tabs: [
+ { content: 'History', key: 'history' },
+ { content: 'Tokens', key: 'tokens' },
+ ],
+ defaultTab: subview || 'history',
+ tabSelected: (key) => {
+ this.setState({ tabSelection: key })
+ },
+ }),
+
+ this.tabSwitchView(),
+ ])
+}
+
+AccountDetailScreen.prototype.tabSwitchView = function () {
+ const userAddress = this.props.address
+ const tabSelection = this.state.tabSelection || 'history'
+
+ switch (tabSelection) {
+ case 'tokens':
+ return h(TokenList, { userAddress })
+ default:
return this.transactionList()
}
}
@@ -249,6 +290,7 @@ AccountDetailScreen.prototype.subview = function () {
AccountDetailScreen.prototype.transactionList = function () {
const {transactions, unapprovedMsgs, address,
network, shapeShiftTxList, conversionRate } = this.props
+
return h(TransactionList, {
transactions: transactions.sort((a, b) => b.time - a.time),
network,
diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js
index 888196c5d..394d878f7 100644
--- a/ui/app/components/account-export.js
+++ b/ui/app/components/account-export.js
@@ -20,8 +20,6 @@ function mapStateToProps (state) {
}
ExportAccountView.prototype.render = function () {
- console.log('EXPORT VIEW')
- console.dir(this.props)
var state = this.props
var accountDetail = state.accountDetail
diff --git a/ui/app/components/balance.js b/ui/app/components/balance.js
new file mode 100644
index 000000000..57ca84564
--- /dev/null
+++ b/ui/app/components/balance.js
@@ -0,0 +1,89 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const formatBalance = require('../util').formatBalance
+const generateBalanceObject = require('../util').generateBalanceObject
+const Tooltip = require('./tooltip.js')
+const FiatValue = require('./fiat-value.js')
+
+module.exports = EthBalanceComponent
+
+inherits(EthBalanceComponent, Component)
+function EthBalanceComponent () {
+ Component.call(this)
+}
+
+EthBalanceComponent.prototype.render = function () {
+ var props = this.props
+ let { value } = props
+ var style = props.style
+ var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
+ value = value ? formatBalance(value, 6, needsParse) : '...'
+ var width = props.width
+
+ return (
+
+ h('.ether-balance.ether-balance-amount', {
+ style: style,
+ }, [
+ h('div', {
+ style: {
+ display: 'inline',
+ width: width,
+ },
+ }, this.renderBalance(value)),
+ ])
+
+ )
+}
+EthBalanceComponent.prototype.renderBalance = function (value) {
+ var props = this.props
+ if (value === 'None') return value
+ if (value === '...') return value
+ var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3)
+ var balance
+ var splitBalance = value.split(' ')
+ var ethNumber = splitBalance[0]
+ var ethSuffix = splitBalance[1]
+ const showFiat = 'showFiat' in props ? props.showFiat : true
+
+ if (props.shorten) {
+ balance = balanceObj.shortBalance
+ } else {
+ balance = balanceObj.balance
+ }
+
+ var label = balanceObj.label
+
+ return (
+ h(Tooltip, {
+ position: 'bottom',
+ title: `${ethNumber} ${ethSuffix}`,
+ }, h('div.flex-column', [
+ h('.flex-row', {
+ style: {
+ alignItems: 'flex-end',
+ lineHeight: '13px',
+ fontFamily: 'Montserrat Light',
+ textRendering: 'geometricPrecision',
+ },
+ }, [
+ h('div', {
+ style: {
+ width: '100%',
+ textAlign: 'right',
+ },
+ }, this.props.incoming ? `+${balance}` : balance),
+ h('div', {
+ style: {
+ color: ' #AEAEAE',
+ fontSize: '12px',
+ marginLeft: '5px',
+ },
+ }, label),
+ ]),
+
+ showFiat ? h(FiatValue, { value: props.value }) : null,
+ ]))
+ )
+}
diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js
index 9de854b54..58bd2bdc4 100644
--- a/ui/app/components/identicon.js
+++ b/ui/app/components/identicon.js
@@ -35,21 +35,22 @@ IdenticonComponent.prototype.render = function () {
IdenticonComponent.prototype.componentDidMount = function () {
var props = this.props
- var address = props.address
+ const { address } = props
if (!address) return
var container = findDOMNode(this)
var diameter = props.diameter || this.defaultDiameter
+
if (!isNode) {
- var img = iconFactory.iconForAddress(address, diameter, false)
+ var img = iconFactory.iconForAddress(address, diameter)
container.appendChild(img)
}
}
IdenticonComponent.prototype.componentDidUpdate = function () {
var props = this.props
- var address = props.address
+ const { address } = props
if (!address) return
@@ -62,7 +63,8 @@ IdenticonComponent.prototype.componentDidUpdate = function () {
var diameter = props.diameter || this.defaultDiameter
if (!isNode) {
- var img = iconFactory.iconForAddress(address, diameter, false)
+ var img = iconFactory.iconForAddress(address, diameter)
container.appendChild(img)
}
}
+
diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js
new file mode 100644
index 000000000..879dc01d1
--- /dev/null
+++ b/ui/app/components/token-cell.js
@@ -0,0 +1,31 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const Identicon = require('./identicon')
+
+module.exports = TokenCell
+
+inherits(TokenCell, Component)
+function TokenCell () {
+ Component.call(this)
+}
+
+TokenCell.prototype.render = function () {
+ const props = this.props
+ const { address, symbol, string, network } = props
+ log.info({ address, symbol, string })
+
+ return (
+ h('li.token-cell', [
+
+ h(Identicon, {
+ diameter: 50,
+ address,
+ network,
+ }),
+
+ h('h3', `${string || 0} ${symbol}`),
+ ])
+ )
+}
+
diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js
new file mode 100644
index 000000000..6589dea62
--- /dev/null
+++ b/ui/app/components/token-list.js
@@ -0,0 +1,72 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const TokenTracker = require('eth-token-tracker')
+const TokenCell = require('./token-cell.js')
+
+module.exports = TokenList
+
+inherits(TokenList, Component)
+function TokenList () {
+
+ // Hard coded for development for now:
+ const tokens = [
+ { address: '0x48c80F1f4D53D5951e5D5438B54Cba84f29F32a5', symbol: 'REP', balance: 'aa'},
+ { address: '0xc66ea802717bfb9833400264dd12c2bceaa34a6d', symbol: 'MKR', balance: '1000', decimals: 18},
+ { address: '0xa74476443119A942dE498590Fe1f2454d7D4aC0d', symbol: 'GOL', balance: 'ff'},
+ { address: '0xaec2e87e0a235266d9c5adc9deb4b2e29b54d009', symbol: 'SNGLS', balance: '0' },
+ ]
+
+ this.state = { tokens }
+ Component.call(this)
+}
+
+TokenList.prototype.render = function () {
+ const tokens = this.state.tokens
+ const network = this.props.network
+
+ const tokenViews = tokens.map((tokenData) => {
+ tokenData.network = network
+ return h(TokenCell, tokenData)
+ })
+
+ return (
+ h('ol', [h('style', `
+
+ li.token-cell {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 10px;
+ }
+
+ li.token-cell > h3 {
+ margin-left: 12px;
+ }
+
+ li.token-cell:hover {
+ background: white;
+ cursor: pointer;
+ }
+
+ `)].concat(tokenViews))
+ )
+}
+
+TokenList.prototype.componentDidMount = function () {
+ const { userAddress } = this.props
+
+ this.tracker = new TokenTracker({
+ userAddress,
+ provider: web3.currentProvider,
+ tokens: this.state.tokens,
+ })
+
+ this.setState({ tokens: this.tracker.serialize() })
+ this.tracker.on('update', (tokenData) => this.setState({ tokens: tokenData }))
+ this.tracker.updateBalances()
+}
+
+TokenList.prototype.componentWillUnmount = function () {
+ this.tracker.stop()
+}
diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js
index 37a757309..3b4ba741e 100644
--- a/ui/app/components/transaction-list.js
+++ b/ui/app/components/transaction-list.js
@@ -36,17 +36,6 @@ TransactionList.prototype.render = function () {
}
`),
- h('h3.flex-center.text-transform-uppercase', {
- style: {
- background: '#EBEBEB',
- color: '#AEAEAE',
- paddingTop: '4px',
- paddingBottom: '4px',
- },
- }, [
- 'History',
- ]),
-
h('.tx-list', {
style: {
overflowY: 'auto',
diff --git a/ui/lib/icon-factory.js b/ui/lib/icon-factory.js
index 45be47b7a..4ee6b600b 100644
--- a/ui/lib/icon-factory.js
+++ b/ui/lib/icon-factory.js
@@ -20,6 +20,7 @@ IconFactory.prototype.iconForAddress = function (address, diameter) {
if (iconExistsFor(addr)) {
return imageElFor(addr)
}
+
return this.generateIdenticonSvg(address, diameter)
}