Merge pull request #1603 from MetaMask/AddTokenList

Add popular token list
feature/default_network_editable
Kevin Serrano 8 years ago committed by GitHub
commit f022c7c714
  1. 4
      .babelrc
  2. 1
      CHANGELOG.md
  3. 8
      app/scripts/controllers/preferences.js
  4. 2
      app/scripts/controllers/transactions.js
  5. 1
      app/scripts/metamask-controller.js
  6. 2
      circle.yml
  7. 2
      gulpfile.js
  8. 6
      package.json
  9. 4
      test/integration/index.js
  10. 40
      ui/app/account-detail.js
  11. 25
      ui/app/actions.js
  12. 21
      ui/app/app.js
  13. 2
      ui/app/components/account-export.js
  14. 89
      ui/app/components/balance.js
  15. 14
      ui/app/components/identicon.js
  16. 5
      ui/app/components/loading.js
  17. 1
      ui/app/components/tab-bar.js
  18. 46
      ui/app/components/token-cell.js
  19. 147
      ui/app/components/token-list.js
  20. 11
      ui/app/components/transaction-list.js
  21. 1
      ui/app/info.js
  22. 5
      ui/lib/icon-factory.js

@ -1,4 +1,4 @@
{ {
"presets": ["es2015"], "presets": ["es2015", "stage-0"],
"plugins": ["transform-runtime"] "plugins": ["transform-runtime", "transform-async-to-generator"]
} }

@ -2,6 +2,7 @@
## Current Master ## Current Master
- Add list of popular tokens held to the account detail view.
- Add a warning to JSON file import. - Add a warning to JSON file import.
- Fix bug where slowly mined txs would sometimes be incorrectly marked as failed. - Fix bug where slowly mined txs would sometimes be incorrectly marked as failed.
- Fix bug where badge count did not reflect personal_sign pending messages. - Fix bug where badge count did not reflect personal_sign pending messages.

@ -7,6 +7,7 @@ class PreferencesController {
constructor (opts = {}) { constructor (opts = {}) {
const initState = extend({ const initState = extend({
frequentRpcList: [], frequentRpcList: [],
currentAccountTab: 'history',
}, opts.initState) }, opts.initState)
this.store = new ObservableStore(initState) this.store = new ObservableStore(initState)
} }
@ -35,6 +36,13 @@ class PreferencesController {
}) })
} }
setCurrentAccountTab (currentAccountTab) {
return new Promise((resolve, reject) => {
this.store.updateState({ currentAccountTab })
resolve()
})
}
addToFrequentRpcList (_url) { addToFrequentRpcList (_url) {
const rpcList = this.getFrequentRpcList() const rpcList = this.getFrequentRpcList()
const index = rpcList.findIndex((element) => { return element === _url }) const index = rpcList.findIndex((element) => { return element === _url })

@ -382,13 +382,13 @@ module.exports = class TransactionController extends EventEmitter {
// - `'signed'` the tx is signed // - `'signed'` the tx is signed
// - `'submitted'` the tx is sent to a server // - `'submitted'` the tx is sent to a server
// - `'confirmed'` the tx has been included in a block. // - `'confirmed'` the tx has been included in a block.
// - `'failed'` the tx failed for some reason, included on tx data.
_setTxStatus (txId, status) { _setTxStatus (txId, status) {
var txMeta = this.getTx(txId) var txMeta = this.getTx(txId)
txMeta.status = status txMeta.status = status
this.emit(`${txMeta.id}:${status}`, txId) this.emit(`${txMeta.id}:${status}`, txId)
if (status === 'submitted' || status === 'rejected') { if (status === 'submitted' || status === 'rejected') {
this.emit(`${txMeta.id}:finished`, txMeta) this.emit(`${txMeta.id}:finished`, txMeta)
} }
this.updateTx(txMeta) this.updateTx(txMeta)
this.emit('updateBadge') this.emit('updateBadge')

@ -280,6 +280,7 @@ module.exports = class MetamaskController extends EventEmitter {
// PreferencesController // PreferencesController
setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController), setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController),
setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab).bind(preferencesController),
setDefaultRpc: nodeify(this.setDefaultRpc).bind(this), setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
setCustomRpc: nodeify(this.setCustomRpc).bind(this), setCustomRpc: nodeify(this.setCustomRpc).bind(this),

@ -1,6 +1,6 @@
machine: machine:
node: node:
version: 7.6.0 version: 8.0.0
dependencies: dependencies:
pre: pre:
- "npm i -g testem" - "npm i -g testem"

@ -53,7 +53,7 @@ gulp.task('copy:images', copyTask({
], ],
})) }))
gulp.task('copy:contractImages', copyTask({ gulp.task('copy:contractImages', copyTask({
source: './node_modules/ethereum-contract-icons/images/', source: './node_modules/eth-contract-metadata/images/',
destinations: [ destinations: [
'./dist/firefox/images/contract', './dist/firefox/images/contract',
'./dist/chrome/images/contract', './dist/chrome/images/contract',

@ -62,11 +62,12 @@
"end-of-stream": "^1.1.0", "end-of-stream": "^1.1.0",
"ensnare": "^1.0.0", "ensnare": "^1.0.0",
"eth-bin-to-ops": "^1.0.1", "eth-bin-to-ops": "^1.0.1",
"eth-contract-metadata": "^1.0.0", "eth-contract-metadata": "^1.1.3",
"eth-hd-keyring": "^1.1.1", "eth-hd-keyring": "^1.1.1",
"eth-query": "^2.1.2", "eth-query": "^2.1.2",
"eth-sig-util": "^1.1.1", "eth-sig-util": "^1.1.1",
"eth-simple-keyring": "^1.1.1", "eth-simple-keyring": "^1.1.1",
"eth-token-tracker": "^1.0.9",
"ethereumjs-tx": "^1.3.0", "ethereumjs-tx": "^1.3.0",
"ethereumjs-util": "ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9", "ethereumjs-util": "ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
"ethereumjs-wallet": "^0.6.0", "ethereumjs-wallet": "^0.6.0",
@ -128,8 +129,11 @@
"xtend": "^4.0.1" "xtend": "^4.0.1"
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.24.1",
"babel-eslint": "^6.0.5", "babel-eslint": "^6.0.5",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.23.0",
"babel-preset-stage-0": "^6.24.1", "babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.7.2", "babel-register": "^6.7.2",
"babelify": "^7.2.0", "babelify": "^7.2.0",

@ -9,7 +9,6 @@ var b = browserify()
// Remove old bundle // Remove old bundle
try { try {
fs.unlinkSync(bundlePath) fs.unlinkSync(bundlePath)
} catch (e) {}
var writeStream = fs.createWriteStream(bundlePath) var writeStream = fs.createWriteStream(bundlePath)
@ -18,4 +17,7 @@ tests.forEach(function (fileName) {
}) })
b.bundle().pipe(writeStream) b.bundle().pipe(writeStream)
} catch (e) {
console.error('Integration build failure', e)
}

@ -16,6 +16,9 @@ const ExportAccountView = require('./components/account-export')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const EditableLabel = require('./components/editable-label') const EditableLabel = require('./components/editable-label')
const Tooltip = require('./components/tooltip') const Tooltip = require('./components/tooltip')
const TabBar = require('./components/tab-bar')
const TokenList = require('./components/token-list')
module.exports = connect(mapStateToProps)(AccountDetailScreen) module.exports = connect(mapStateToProps)(AccountDetailScreen)
function mapStateToProps (state) { function mapStateToProps (state) {
@ -31,6 +34,7 @@ function mapStateToProps (state) {
transactions: state.metamask.selectedAddressTxList || [], transactions: state.metamask.selectedAddressTxList || [],
conversionRate: state.metamask.conversionRate, conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency, currentCurrency: state.metamask.currentCurrency,
currentAccountTab: state.metamask.currentAccountTab,
} }
} }
@ -237,10 +241,43 @@ AccountDetailScreen.prototype.subview = function () {
switch (subview) { switch (subview) {
case 'transactions': case 'transactions':
return this.transactionList() return this.tabSections()
case 'export': case 'export':
var state = extend({key: 'export'}, this.props) var state = extend({key: 'export'}, this.props)
return h(ExportAccountView, state) return h(ExportAccountView, state)
default:
return this.tabSections()
}
}
AccountDetailScreen.prototype.tabSections = function () {
const { currentAccountTab } = this.props
return h('section.tabSection', [
h(TabBar, {
tabs: [
{ content: 'Sent', key: 'history' },
{ content: 'Tokens', key: 'tokens' },
],
defaultTab: currentAccountTab || 'history',
tabSelected: (key) => {
this.props.dispatch(actions.setCurrentAccountTab(key))
},
}),
this.tabSwitchView(),
])
}
AccountDetailScreen.prototype.tabSwitchView = function () {
const props = this.props
const { address, network } = props
const { currentAccountTab } = this.props
switch (currentAccountTab) {
case 'tokens':
return h(TokenList, { userAddress: address, network })
default: default:
return this.transactionList() return this.transactionList()
} }
@ -249,6 +286,7 @@ AccountDetailScreen.prototype.subview = function () {
AccountDetailScreen.prototype.transactionList = function () { AccountDetailScreen.prototype.transactionList = function () {
const {transactions, unapprovedMsgs, address, const {transactions, unapprovedMsgs, address,
network, shapeShiftTxList, conversionRate } = this.props network, shapeShiftTxList, conversionRate } = this.props
return h(TransactionList, { return h(TransactionList, {
transactions: transactions.sort((a, b) => b.time - a.time), transactions: transactions.sort((a, b) => b.time - a.time),
network, network,

@ -74,6 +74,7 @@ var actions = {
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE', SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT', SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
setCurrentCurrency: setCurrentCurrency, setCurrentCurrency: setCurrentCurrency,
setCurrentAccountTab,
// account detail screen // account detail screen
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE', SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
showSendPage: showSendPage, showSendPage: showSendPage,
@ -218,7 +219,7 @@ function confirmSeedWords () {
return dispatch(actions.displayWarning(err.message)) return dispatch(actions.displayWarning(err.message))
} }
console.log('Seed word cache cleared. ' + account) log.info('Seed word cache cleared. ' + account)
dispatch(actions.showAccountDetail(account)) dispatch(actions.showAccountDetail(account))
}) })
} }
@ -338,7 +339,7 @@ function setCurrentCurrency (currencyCode) {
background.setCurrentCurrency(currencyCode, (err, data) => { background.setCurrentCurrency(currencyCode, (err, data) => {
dispatch(this.hideLoadingIndication()) dispatch(this.hideLoadingIndication())
if (err) { if (err) {
console.error(err.stack) log.error(err.stack)
return dispatch(actions.displayWarning(err.message)) return dispatch(actions.displayWarning(err.message))
} }
dispatch({ dispatch({
@ -409,7 +410,7 @@ function sendTx (txData) {
background.approveTransaction(txData.id, (err) => { background.approveTransaction(txData.id, (err) => {
if (err) { if (err) {
dispatch(actions.txError(err)) dispatch(actions.txError(err))
return console.error(err.message) return log.error(err.message)
} }
dispatch(actions.completedTx(txData.id)) dispatch(actions.completedTx(txData.id))
}) })
@ -424,7 +425,7 @@ function updateAndApproveTx (txData) {
dispatch(actions.hideLoadingIndication()) dispatch(actions.hideLoadingIndication())
if (err) { if (err) {
dispatch(actions.txError(err)) dispatch(actions.txError(err))
return console.error(err.message) return log.error(err.message)
} }
dispatch(actions.completedTx(txData.id)) dispatch(actions.completedTx(txData.id))
}) })
@ -558,6 +559,11 @@ function lockMetamask () {
return callBackgroundThenUpdate(background.setLocked) return callBackgroundThenUpdate(background.setLocked)
} }
function setCurrentAccountTab (newTabName) {
log.debug(`background.setCurrentAccountTab: ${newTabName}`)
return callBackgroundThenUpdateNoSpinner(background.setCurrentAccountTab, newTabName)
}
function showAccountDetail (address) { function showAccountDetail (address) {
return (dispatch) => { return (dispatch) => {
dispatch(actions.showLoadingIndication()) dispatch(actions.showLoadingIndication())
@ -965,6 +971,17 @@ function shapeShiftRequest (query, options, cb) {
// We hide loading indication. // We hide loading indication.
// If it errored, we show a warning. // If it errored, we show a warning.
// If it didn't, we update the state. // If it didn't, we update the state.
function callBackgroundThenUpdateNoSpinner (method, ...args) {
return (dispatch) => {
method.call(background, ...args, (err) => {
if (err) {
return dispatch(actions.displayWarning(err.message))
}
forceUpdateMetamaskState(dispatch)
})
}
}
function callBackgroundThenUpdate (method, ...args) { function callBackgroundThenUpdate (method, ...args) {
return (dispatch) => { return (dispatch) => {
dispatch(actions.showLoadingIndication()) dispatch(actions.showLoadingIndication())

@ -21,7 +21,7 @@ const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
const ConfigScreen = require('./config') const ConfigScreen = require('./config')
const Import = require('./accounts/import') const Import = require('./accounts/import')
const InfoScreen = require('./info') const InfoScreen = require('./info')
const LoadingIndicator = require('./components/loading') const Loading = require('./components/loading')
const SandwichExpando = require('sandwich-expando') const SandwichExpando = require('sandwich-expando')
const MenuDroppo = require('menu-droppo') const MenuDroppo = require('menu-droppo')
const DropMenuItem = require('./components/drop-menu-item') const DropMenuItem = require('./components/drop-menu-item')
@ -64,7 +64,11 @@ function mapStateToProps (state) {
App.prototype.render = function () { App.prototype.render = function () {
var props = this.props var props = this.props
const { isLoading, loadingMessage, transForward } = props const { isLoading, loadingMessage, transForward, network } = props
const isLoadingNetwork = network === 'loading'
const loadMessage = loadingMessage || isLoadingNetwork ?
'Searching for Network' : null
log.debug('Main ui render function') log.debug('Main ui render function')
return ( return (
@ -77,13 +81,16 @@ App.prototype.render = function () {
}, },
}, [ }, [
h(LoadingIndicator, { isLoading, loadingMessage }),
// app bar // app bar
this.renderAppBar(), this.renderAppBar(),
this.renderNetworkDropdown(), this.renderNetworkDropdown(),
this.renderDropdown(), this.renderDropdown(),
h(Loading, {
isLoading: isLoading || isLoadingNetwork,
loadingMessage: loadMessage,
}),
// panel content // panel content
h('.app-primary.flex-grow' + (transForward ? '.from-right' : '.from-left'), { h('.app-primary.flex-grow' + (transForward ? '.from-right' : '.from-left'), {
style: { style: {
@ -124,7 +131,7 @@ App.prototype.renderAppBar = function () {
background: props.isUnlocked ? 'white' : 'none', background: props.isUnlocked ? 'white' : 'none',
height: '36px', height: '36px',
position: 'relative', position: 'relative',
zIndex: 10, zIndex: 12,
}, },
}, [ }, [
@ -221,7 +228,7 @@ App.prototype.renderNetworkDropdown = function () {
onClickOutside: (event) => { onClickOutside: (event) => {
this.setState({ isNetworkMenuOpen: !isOpen }) this.setState({ isNetworkMenuOpen: !isOpen })
}, },
zIndex: 1, zIndex: 11,
style: { style: {
position: 'absolute', position: 'absolute',
left: 0, left: 0,
@ -300,7 +307,7 @@ App.prototype.renderDropdown = function () {
return h(MenuDroppo, { return h(MenuDroppo, {
isOpen: isOpen, isOpen: isOpen,
zIndex: 1, zIndex: 11,
onClickOutside: (event) => { onClickOutside: (event) => {
this.setState({ isMainMenuOpen: !isOpen }) this.setState({ isMainMenuOpen: !isOpen })
}, },

@ -20,8 +20,6 @@ function mapStateToProps (state) {
} }
ExportAccountView.prototype.render = function () { ExportAccountView.prototype.render = function () {
console.log('EXPORT VIEW')
console.dir(this.props)
var state = this.props var state = this.props
var accountDetail = state.accountDetail var accountDetail = state.accountDetail

@ -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,
]))
)
}

@ -23,7 +23,9 @@ IdenticonComponent.prototype.render = function () {
h('div', { h('div', {
key: 'identicon-' + this.props.address, key: 'identicon-' + this.props.address,
style: { style: {
display: 'inline-block', display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: diameter, height: diameter,
width: diameter, width: diameter,
borderRadius: diameter / 2, borderRadius: diameter / 2,
@ -35,21 +37,22 @@ IdenticonComponent.prototype.render = function () {
IdenticonComponent.prototype.componentDidMount = function () { IdenticonComponent.prototype.componentDidMount = function () {
var props = this.props var props = this.props
var address = props.address const { address } = props
if (!address) return if (!address) return
var container = findDOMNode(this) var container = findDOMNode(this)
var diameter = props.diameter || this.defaultDiameter var diameter = props.diameter || this.defaultDiameter
if (!isNode) { if (!isNode) {
var img = iconFactory.iconForAddress(address, diameter, false) var img = iconFactory.iconForAddress(address, diameter)
container.appendChild(img) container.appendChild(img)
} }
} }
IdenticonComponent.prototype.componentDidUpdate = function () { IdenticonComponent.prototype.componentDidUpdate = function () {
var props = this.props var props = this.props
var address = props.address const { address } = props
if (!address) return if (!address) return
@ -62,7 +65,8 @@ IdenticonComponent.prototype.componentDidUpdate = function () {
var diameter = props.diameter || this.defaultDiameter var diameter = props.diameter || this.defaultDiameter
if (!isNode) { if (!isNode) {
var img = iconFactory.iconForAddress(address, diameter, false) var img = iconFactory.iconForAddress(address, diameter)
container.appendChild(img) container.appendChild(img)
} }
} }

@ -26,18 +26,21 @@ LoadingIndicator.prototype.render = function () {
style: { style: {
zIndex: 10, zIndex: 10,
position: 'absolute', position: 'absolute',
flexDirection: 'column',
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
height: '100%', height: '100%',
width: '100%', width: '100%',
background: 'rgba(255, 255, 255, 0.5)', background: 'rgba(255, 255, 255, 0.8)',
}, },
}, [ }, [
h('img', { h('img', {
src: 'images/loading.svg', src: 'images/loading.svg',
}), }),
h('br'),
showMessageIfAny(loadingMessage), showMessageIfAny(loadingMessage),
]) : null, ]) : null,
]) ])

@ -33,3 +33,4 @@ TabBar.prototype.render = function () {
})) }))
) )
} }

@ -0,0 +1,46 @@
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, userAddress } = props
return (
h('li.token-cell', {
style: { cursor: network === '1' ? 'pointer' : 'default' },
onClick: (event) => {
const url = urlFor(address, userAddress, network)
if (url) {
navigateTo(url)
}
},
}, [
h(Identicon, {
diameter: 50,
address,
network,
}),
h('h3', `${string || 0} ${symbol}`),
])
)
}
function navigateTo (url) {
global.platform.openWindow({ url })
}
function urlFor (tokenAddress, address, network) {
return `https://etherscan.io/token/${tokenAddress}?a=${address}`
}

@ -0,0 +1,147 @@
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')
const contracts = require('eth-contract-metadata')
const tokens = []
for (const address in contracts) {
const contract = contracts[address]
if (contract.erc20) {
contract.address = address
tokens.push(contract)
}
}
module.exports = TokenList
inherits(TokenList, Component)
function TokenList () {
this.state = { tokens, isLoading: true, network: null }
Component.call(this)
}
TokenList.prototype.render = function () {
const state = this.state
const { tokens, isLoading, error } = state
const { userAddress } = this.props
if (isLoading) {
return this.message('Loading')
}
if (error) {
log.error(error)
return this.message('There was a problem loading your token balances.')
}
const network = this.props.network
const tokenViews = tokens.map((tokenData) => {
tokenData.network = network
tokenData.userAddress = userAddress
return h(TokenCell, tokenData)
})
return (
h('ol', {
style: {
height: '302px',
overflowY: 'auto',
},
}, [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.length ? tokenViews : this.message('No Tokens Found.')))
)
}
TokenList.prototype.message = function (body) {
return h('div', {
style: {
display: 'flex',
height: '250px',
alignItems: 'center',
justifyContent: 'center',
},
}, body)
}
TokenList.prototype.componentDidMount = function () {
this.createFreshTokenTracker()
}
TokenList.prototype.createFreshTokenTracker = function () {
if (this.tracker) {
// Clean up old trackers when refreshing:
this.tracker.stop()
this.tracker.removeListener('update', this.balanceUpdater)
this.tracker.removeListener('error', this.showError)
}
if (!global.ethereumProvider) return
const { userAddress } = this.props
this.tracker = new TokenTracker({
userAddress,
provider: global.ethereumProvider,
tokens: tokens,
pollingInterval: 8000,
})
// Set up listener instances for cleaning up
this.balanceUpdater = this.updateBalances.bind(this)
this.showError = (error) => {
this.setState({ error, isLoading: false })
}
this.tracker.on('update', this.balanceUpdater)
this.tracker.on('error', this.showError)
this.tracker.updateBalances()
.then(() => {
this.updateBalances(this.tracker.serialize())
})
.catch((reason) => {
log.error(`Problem updating balances`, reason)
this.setState({ isLoading: false })
})
}
TokenList.prototype.componentWillUpdate = function (nextProps) {
if (nextProps.network === 'loading') return
const oldNet = this.props.network
const newNet = nextProps.network
if (oldNet && newNet && newNet !== oldNet) {
this.setState({ isLoading: true })
this.createFreshTokenTracker()
}
}
TokenList.prototype.updateBalances = function (tokenData) {
const heldTokens = tokenData.filter(token => token.balance !== '0' && token.string !== '0.000')
this.setState({ tokens: heldTokens, isLoading: false })
}
TokenList.prototype.componentWillUnmount = function () {
if (!this.tracker) return
this.tracker.stop()
}

@ -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', { h('.tx-list', {
style: { style: {
overflowY: 'auto', overflowY: 'auto',

@ -151,3 +151,4 @@ InfoScreen.prototype.render = function () {
InfoScreen.prototype.navigateTo = function (url) { InfoScreen.prototype.navigateTo = function (url) {
global.platform.openWindow({ url }) global.platform.openWindow({ url })
} }

@ -20,6 +20,7 @@ IconFactory.prototype.iconForAddress = function (address, diameter) {
if (iconExistsFor(addr)) { if (iconExistsFor(addr)) {
return imageElFor(addr) return imageElFor(addr)
} }
return this.generateIdenticonSvg(address, diameter) return this.generateIdenticonSvg(address, diameter)
} }
@ -43,7 +44,7 @@ IconFactory.prototype.generateNewIdenticon = function (address, diameter) {
// util // util
function iconExistsFor (address) { function iconExistsFor (address) {
return (contractMap.address) && isValidAddress(address) && (contractMap[address].logo) return contractMap[address] && isValidAddress(address) && contractMap[address].logo
} }
function imageElFor (address) { function imageElFor (address) {
@ -52,7 +53,7 @@ function imageElFor (address) {
const path = `images/contract/${fileName}` const path = `images/contract/${fileName}`
const img = document.createElement('img') const img = document.createElement('img')
img.src = path img.src = path
img.style.width = '100%' img.style.width = '75%'
return img return img
} }

Loading…
Cancel
Save