2017-11-14 08:04:23 -08:00
|
|
|
const Component = require('react').Component
|
|
|
|
const h = require('react-hyperscript')
|
|
|
|
const inherits = require('util').inherits
|
2018-08-22 05:32:45 -07:00
|
|
|
const TokenTracker = require('eth-token-watcher')
|
2017-11-14 08:04:23 -08:00
|
|
|
const TokenCell = require('./token-cell.js')
|
2018-09-19 08:38:39 -07:00
|
|
|
const connect = require('react-redux').connect
|
|
|
|
const selectors = require('../../../ui/app/selectors')
|
2018-04-12 14:06:59 -07:00
|
|
|
const log = require('loglevel')
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2018-09-19 08:38:39 -07:00
|
|
|
function mapStateToProps (state) {
|
|
|
|
return {
|
|
|
|
network: state.metamask.network,
|
|
|
|
tokens: state.metamask.tokens,
|
|
|
|
userAddress: selectors.getSelectedAddress(state),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const defaultTokens = []
|
|
|
|
|
|
|
|
const contractsETH = require('eth-contract-metadata')
|
|
|
|
const contractsPOA = require('poa-contract-metadata')
|
|
|
|
for (const address in contractsETH) {
|
|
|
|
const contract = contractsETH[address]
|
|
|
|
if (contract.erc20) {
|
|
|
|
contract.address = address
|
|
|
|
defaultTokens.push(contract)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const address in contractsPOA) {
|
|
|
|
const contract = contractsPOA[address]
|
|
|
|
if (contract.erc20) {
|
|
|
|
contract.address = address
|
|
|
|
defaultTokens.push(contract)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = connect(mapStateToProps)(TokenList)
|
2017-11-14 08:04:23 -08:00
|
|
|
|
|
|
|
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, network } = this.props
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
return this.message('Loading')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
log.error(error)
|
|
|
|
return h('.hotFix', {
|
|
|
|
style: {
|
2018-08-08 03:33:56 -07:00
|
|
|
padding: '30px',
|
2017-11-14 08:04:23 -08:00
|
|
|
},
|
|
|
|
}, [
|
|
|
|
'We had trouble loading your token balances. You can view them ',
|
|
|
|
h('span.hotFix', {
|
|
|
|
style: {
|
2018-08-20 09:45:52 -07:00
|
|
|
color: '#60db97',
|
2017-11-14 08:04:23 -08:00
|
|
|
cursor: 'pointer',
|
|
|
|
},
|
|
|
|
onClick: () => {
|
|
|
|
global.platform.openWindow({
|
|
|
|
url: `https://ethplorer.io/address/${userAddress}`,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
}, 'here'),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
2018-08-22 05:32:45 -07:00
|
|
|
const tokensFromCurrentNetwork = tokens.filter(token => (parseInt(token.network) === parseInt(network) || !token.network))
|
|
|
|
|
|
|
|
const tokenViews = tokensFromCurrentNetwork.map((tokenData, ind) => {
|
2017-11-14 08:04:23 -08:00
|
|
|
tokenData.userAddress = userAddress
|
2018-09-20 07:36:44 -07:00
|
|
|
const isLastTokenCell = ind === (tokensFromCurrentNetwork.length - 1)
|
2018-09-20 08:38:04 -07:00
|
|
|
const menuToTop = true
|
2018-07-24 04:59:24 -07:00
|
|
|
return h(TokenCell, {
|
2018-09-20 08:38:04 -07:00
|
|
|
ind,
|
2018-07-24 04:59:24 -07:00
|
|
|
...tokenData,
|
2018-07-26 13:45:52 -07:00
|
|
|
isLastTokenCell,
|
2018-09-20 07:36:44 -07:00
|
|
|
menuToTop,
|
2018-07-24 04:59:24 -07:00
|
|
|
removeToken: this.props.removeToken,
|
2018-10-26 02:37:52 -07:00
|
|
|
network: this.props.network,
|
2018-07-24 04:59:24 -07:00
|
|
|
})
|
2017-11-14 08:04:23 -08:00
|
|
|
})
|
|
|
|
|
|
|
|
return h('.full-flex-height', [
|
|
|
|
this.renderTokenStatusBar(),
|
|
|
|
|
|
|
|
h('ol.full-flex-height.flex-column', {
|
|
|
|
style: {
|
|
|
|
display: 'flex',
|
|
|
|
flexDirection: 'column',
|
|
|
|
},
|
|
|
|
}, [
|
|
|
|
h('style', `
|
|
|
|
|
|
|
|
li.token-cell {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: row;
|
|
|
|
align-items: center;
|
|
|
|
padding: 10px;
|
|
|
|
min-height: 50px;
|
|
|
|
}
|
|
|
|
|
|
|
|
li.token-cell > h3 {
|
|
|
|
margin-left: 12px;
|
|
|
|
}
|
|
|
|
|
|
|
|
li.token-cell:hover {
|
|
|
|
background: white;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
`),
|
|
|
|
...tokenViews,
|
|
|
|
h('.flex-grow'),
|
|
|
|
]),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
TokenList.prototype.renderTokenStatusBar = function () {
|
|
|
|
const { tokens } = this.state
|
2018-08-22 05:32:45 -07:00
|
|
|
const { network } = this.props
|
|
|
|
const tokensFromCurrentNetwork = tokens.filter(token => (parseInt(token.network) === parseInt(network) || !token.network))
|
2017-11-14 08:04:23 -08:00
|
|
|
|
|
|
|
let msg
|
2019-01-22 08:01:43 -08:00
|
|
|
let noTokens = false
|
2018-08-22 05:32:45 -07:00
|
|
|
if (tokensFromCurrentNetwork.length === 1) {
|
2017-11-14 08:04:23 -08:00
|
|
|
msg = `You own 1 token`
|
2018-08-22 05:32:45 -07:00
|
|
|
} else if (tokensFromCurrentNetwork.length > 1) {
|
|
|
|
msg = `You own ${tokensFromCurrentNetwork.length} tokens`
|
2017-11-14 08:04:23 -08:00
|
|
|
} else {
|
|
|
|
msg = `No tokens found`
|
2019-01-22 08:01:43 -08:00
|
|
|
noTokens = true
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|
|
|
|
|
2019-01-22 08:01:43 -08:00
|
|
|
return h('div', [
|
|
|
|
h('div', {
|
2017-11-14 08:04:23 -08:00
|
|
|
style: {
|
|
|
|
display: 'flex',
|
2019-01-22 08:01:43 -08:00
|
|
|
justifyContent: 'space-between',
|
2017-11-14 08:04:23 -08:00
|
|
|
alignItems: 'center',
|
2019-01-22 08:01:43 -08:00
|
|
|
minHeight: '70px',
|
|
|
|
padding: '30px 30px 10px',
|
2017-11-14 08:04:23 -08:00
|
|
|
},
|
|
|
|
}, [
|
2019-01-22 08:01:43 -08:00
|
|
|
h('span', msg),
|
|
|
|
h('button.btn-primary.wallet-view__add-token-button', {
|
|
|
|
key: 'reveal-account-bar',
|
|
|
|
onClick: (event) => {
|
|
|
|
event.preventDefault()
|
|
|
|
this.props.addToken()
|
|
|
|
},
|
|
|
|
style: {
|
|
|
|
display: 'flex',
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
},
|
|
|
|
}, [
|
|
|
|
'Add Token',
|
|
|
|
]),
|
2017-11-14 08:04:23 -08:00
|
|
|
]),
|
2019-01-22 08:01:43 -08:00
|
|
|
noTokens ? h('div', {
|
|
|
|
style: {
|
|
|
|
height: '70px',
|
|
|
|
},
|
|
|
|
}) : null,
|
2017-11-14 08:04:23 -08:00
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
TokenList.prototype.message = function (body) {
|
|
|
|
return h('div', {
|
|
|
|
style: {
|
|
|
|
display: 'flex',
|
|
|
|
height: '250px',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
padding: '30px',
|
|
|
|
},
|
|
|
|
}, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
TokenList.prototype.componentDidMount = function () {
|
|
|
|
this.createFreshTokenTracker()
|
|
|
|
}
|
|
|
|
|
2018-09-19 08:38:39 -07:00
|
|
|
TokenList.prototype.createFreshTokenTracker = function () {
|
2017-11-14 08:04:23 -08:00
|
|
|
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
|
2018-09-19 08:38:39 -07:00
|
|
|
const { userAddress } = this.props
|
|
|
|
|
2018-09-28 07:17:30 -07:00
|
|
|
const tokensFromCurrentNetwork = this.props.tokens.filter(token => (parseInt(token.network) === parseInt(this.props.network) || !token.network))
|
2017-11-14 08:04:23 -08:00
|
|
|
this.tracker = new TokenTracker({
|
2018-09-19 08:38:39 -07:00
|
|
|
userAddress,
|
2017-11-14 08:04:23 -08:00
|
|
|
provider: global.ethereumProvider,
|
2018-09-28 07:17:30 -07:00
|
|
|
tokens: tokensFromCurrentNetwork,
|
2017-11-14 08:04:23 -08:00
|
|
|
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 })
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-09-19 08:38:39 -07:00
|
|
|
TokenList.prototype.componentDidUpdate = function (nextProps) {
|
|
|
|
const {
|
|
|
|
network: oldNet,
|
|
|
|
userAddress: oldAddress,
|
|
|
|
tokens,
|
|
|
|
} = this.props
|
|
|
|
const {
|
|
|
|
network: newNet,
|
|
|
|
userAddress: newAddress,
|
|
|
|
tokens: newTokens,
|
|
|
|
} = nextProps
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2018-09-19 08:38:39 -07:00
|
|
|
const isLoading = newNet === 'loading'
|
|
|
|
const missingInfo = !oldNet || !newNet || !oldAddress || !newAddress
|
|
|
|
const sameUserAndNetwork = oldAddress === newAddress && oldNet === newNet
|
|
|
|
const shouldUpdateTokens = isLoading || missingInfo || sameUserAndNetwork
|
2018-09-10 05:22:43 -07:00
|
|
|
|
2018-09-19 08:38:39 -07:00
|
|
|
const oldTokensLength = tokens ? tokens.length : 0
|
|
|
|
const tokensLengthUnchanged = oldTokensLength === newTokens.length
|
|
|
|
|
|
|
|
if (tokensLengthUnchanged && shouldUpdateTokens) return
|
|
|
|
|
|
|
|
this.setState({ isLoading: true })
|
|
|
|
this.createFreshTokenTracker()
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
TokenList.prototype.updateBalances = function (tokens) {
|
2018-09-19 08:38:39 -07:00
|
|
|
if (!this.tracker.running) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
this.setState({ tokens, isLoading: false })
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
TokenList.prototype.componentWillUnmount = function () {
|
|
|
|
if (!this.tracker) return
|
|
|
|
this.tracker.stop()
|
2018-09-19 08:38:39 -07:00
|
|
|
this.tracker.removeListener('update', this.balanceUpdater)
|
|
|
|
this.tracker.removeListener('error', this.showError)
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|