|
|
|
@ -1,7 +1,6 @@ |
|
|
|
|
import PropTypes from 'prop-types' |
|
|
|
|
import React, { Component } from 'react' |
|
|
|
|
import TokenCell from './token-cell' |
|
|
|
|
import { inherits } from 'util' |
|
|
|
|
import TokenTracker from 'eth-token-tracker' |
|
|
|
|
import { connect } from 'react-redux' |
|
|
|
|
import { getSelectedAddress } from '../../selectors/selectors' |
|
|
|
@ -27,166 +26,169 @@ for (const address in contracts) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TokenList.contextTypes = { |
|
|
|
|
t: PropTypes.func, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export default connect(mapStateToProps)(TokenList) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inherits(TokenList, Component) |
|
|
|
|
function TokenList () { |
|
|
|
|
this.state = { |
|
|
|
|
tokens: [], |
|
|
|
|
isLoading: true, |
|
|
|
|
network: null, |
|
|
|
|
class TokenList extends Component { |
|
|
|
|
static contextTypes = { |
|
|
|
|
t: PropTypes.func, |
|
|
|
|
} |
|
|
|
|
Component.call(this) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TokenList.prototype.render = function TokenList () { |
|
|
|
|
const { userAddress, assetImages } = this.props |
|
|
|
|
const state = this.state |
|
|
|
|
const { tokens, isLoading, error } = state |
|
|
|
|
if (isLoading) { |
|
|
|
|
return ( |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
display: 'flex', |
|
|
|
|
height: '250px', |
|
|
|
|
alignItems: 'center', |
|
|
|
|
justifyContent: 'center', |
|
|
|
|
padding: '30px', |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
{this.context.t('loadingTokens')} |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
static propTypes = { |
|
|
|
|
tokens: PropTypes.array.isRequired, |
|
|
|
|
userAddress: PropTypes.string.isRequired, |
|
|
|
|
network: PropTypes.string.isRequired, |
|
|
|
|
assetImages: PropTypes.object.isRequired, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
log.error(error) |
|
|
|
|
return ( |
|
|
|
|
<div |
|
|
|
|
className="hotFix" |
|
|
|
|
style={{ |
|
|
|
|
padding: '80px', |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
{this.context.t('troubleTokenBalances')} |
|
|
|
|
<span |
|
|
|
|
className="hotFix" |
|
|
|
|
style={{ |
|
|
|
|
color: 'rgba(247, 134, 28, 1)', |
|
|
|
|
cursor: 'pointer', |
|
|
|
|
}} |
|
|
|
|
onClick={() => { |
|
|
|
|
global.platform.openWindow({ |
|
|
|
|
url: `https://ethplorer.io/address/${userAddress}`, |
|
|
|
|
}) |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
{this.context.t('here')} |
|
|
|
|
</span> |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
state = { |
|
|
|
|
tokens: [], |
|
|
|
|
isLoading: true, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<div> |
|
|
|
|
{tokens.map((tokenData, index) => { |
|
|
|
|
tokenData.image = assetImages[tokenData.address] |
|
|
|
|
return ( |
|
|
|
|
<TokenCell key={index} {...tokenData} /> |
|
|
|
|
) |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
createFreshTokenTracker () { |
|
|
|
|
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: this.props.tokens, |
|
|
|
|
pollingInterval: 8000, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
// 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 }) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!global.ethereumProvider) { |
|
|
|
|
return |
|
|
|
|
updateBalances = function (tokens) { |
|
|
|
|
if (!this.tracker.running) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
this.setState({ tokens, isLoading: false }) |
|
|
|
|
} |
|
|
|
|
const { userAddress } = this.props |
|
|
|
|
|
|
|
|
|
this.tracker = new TokenTracker({ |
|
|
|
|
userAddress, |
|
|
|
|
provider: global.ethereumProvider, |
|
|
|
|
tokens: this.props.tokens, |
|
|
|
|
pollingInterval: 8000, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set up listener instances for cleaning up
|
|
|
|
|
this.balanceUpdater = this.updateBalances.bind(this) |
|
|
|
|
this.showError = (error) => { |
|
|
|
|
this.setState({ error, isLoading: false }) |
|
|
|
|
componentDidMount () { |
|
|
|
|
this.createFreshTokenTracker() |
|
|
|
|
} |
|
|
|
|
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 }) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
componentDidUpdate (prevProps) { |
|
|
|
|
const { |
|
|
|
|
network: oldNet, |
|
|
|
|
userAddress: oldAddress, |
|
|
|
|
tokens, |
|
|
|
|
} = prevProps |
|
|
|
|
const { |
|
|
|
|
network: newNet, |
|
|
|
|
userAddress: newAddress, |
|
|
|
|
tokens: newTokens, |
|
|
|
|
} = this.props |
|
|
|
|
|
|
|
|
|
const isLoading = newNet === 'loading' |
|
|
|
|
const missingInfo = !oldNet || !newNet || !oldAddress || !newAddress |
|
|
|
|
const sameUserAndNetwork = oldAddress === newAddress && oldNet === newNet |
|
|
|
|
const shouldUpdateTokens = isLoading || missingInfo || sameUserAndNetwork |
|
|
|
|
|
|
|
|
|
const oldTokensLength = tokens ? tokens.length : 0 |
|
|
|
|
const tokensLengthUnchanged = oldTokensLength === newTokens.length |
|
|
|
|
|
|
|
|
|
if (tokensLengthUnchanged && shouldUpdateTokens) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.setState({ isLoading: true }) |
|
|
|
|
this.createFreshTokenTracker() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TokenList.prototype.componentDidUpdate = function (prevProps) { |
|
|
|
|
const { |
|
|
|
|
network: oldNet, |
|
|
|
|
userAddress: oldAddress, |
|
|
|
|
tokens, |
|
|
|
|
} = prevProps |
|
|
|
|
const { |
|
|
|
|
network: newNet, |
|
|
|
|
userAddress: newAddress, |
|
|
|
|
tokens: newTokens, |
|
|
|
|
} = this.props |
|
|
|
|
|
|
|
|
|
const isLoading = newNet === 'loading' |
|
|
|
|
const missingInfo = !oldNet || !newNet || !oldAddress || !newAddress |
|
|
|
|
const sameUserAndNetwork = oldAddress === newAddress && oldNet === newNet |
|
|
|
|
const shouldUpdateTokens = isLoading || missingInfo || sameUserAndNetwork |
|
|
|
|
|
|
|
|
|
const oldTokensLength = tokens ? tokens.length : 0 |
|
|
|
|
const tokensLengthUnchanged = oldTokensLength === newTokens.length |
|
|
|
|
|
|
|
|
|
if (tokensLengthUnchanged && shouldUpdateTokens) { |
|
|
|
|
return |
|
|
|
|
componentWillUnmount () { |
|
|
|
|
if (!this.tracker) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
this.tracker.stop() |
|
|
|
|
this.tracker.removeListener('update', this.balanceUpdater) |
|
|
|
|
this.tracker.removeListener('error', this.showError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.setState({ isLoading: true }) |
|
|
|
|
this.createFreshTokenTracker() |
|
|
|
|
} |
|
|
|
|
render () { |
|
|
|
|
const { userAddress, assetImages } = this.props |
|
|
|
|
const state = this.state |
|
|
|
|
const { tokens, isLoading, error } = state |
|
|
|
|
if (isLoading) { |
|
|
|
|
return ( |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
display: 'flex', |
|
|
|
|
height: '250px', |
|
|
|
|
alignItems: 'center', |
|
|
|
|
justifyContent: 'center', |
|
|
|
|
padding: '30px', |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
{this.context.t('loadingTokens')} |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
log.error(error) |
|
|
|
|
return ( |
|
|
|
|
<div |
|
|
|
|
className="hotFix" |
|
|
|
|
style={{ |
|
|
|
|
padding: '80px', |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
{this.context.t('troubleTokenBalances')} |
|
|
|
|
<span |
|
|
|
|
className="hotFix" |
|
|
|
|
style={{ |
|
|
|
|
color: 'rgba(247, 134, 28, 1)', |
|
|
|
|
cursor: 'pointer', |
|
|
|
|
}} |
|
|
|
|
onClick={() => { |
|
|
|
|
global.platform.openWindow({ |
|
|
|
|
url: `https://ethplorer.io/address/${userAddress}`, |
|
|
|
|
}) |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
{this.context.t('here')} |
|
|
|
|
</span> |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TokenList.prototype.updateBalances = function (tokens) { |
|
|
|
|
if (!this.tracker.running) { |
|
|
|
|
return |
|
|
|
|
return ( |
|
|
|
|
<div> |
|
|
|
|
{tokens.map((tokenData, index) => { |
|
|
|
|
tokenData.image = assetImages[tokenData.address] |
|
|
|
|
return ( |
|
|
|
|
<TokenCell key={index} {...tokenData} /> |
|
|
|
|
) |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
this.setState({ tokens, isLoading: false }) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TokenList.prototype.componentWillUnmount = function () { |
|
|
|
|
if (!this.tracker) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
this.tracker.stop() |
|
|
|
|
this.tracker.removeListener('update', this.balanceUpdater) |
|
|
|
|
this.tracker.removeListener('error', this.showError) |
|
|
|
|
} |
|
|
|
|
export default connect(mapStateToProps)(TokenList) |
|
|
|
|