mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-24 19:10:22 +01:00
b0014a9b35
When a suggested token was resolved in a different window, the popup or notification UI could get stuck with an empty suggested token list, where either action would throw an error. This case is now handled by either redirecting or closing the window, in the popup and notification cases respectively. This check is performed on both component mount and update.
184 lines
5.9 KiB
JavaScript
184 lines
5.9 KiB
JavaScript
import React, { Component } from 'react'
|
|
import PropTypes from 'prop-types'
|
|
import Button from '../../components/ui/button'
|
|
import Identicon from '../../components/ui/identicon'
|
|
import TokenBalance from '../../components/ui/token-balance'
|
|
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
|
|
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../app/scripts/lib/enums'
|
|
|
|
export default class ConfirmAddSuggestedToken extends Component {
|
|
static contextTypes = {
|
|
t: PropTypes.func,
|
|
}
|
|
|
|
static propTypes = {
|
|
history: PropTypes.object,
|
|
addToken: PropTypes.func,
|
|
mostRecentOverviewPage: PropTypes.string.isRequired,
|
|
pendingTokens: PropTypes.object,
|
|
removeSuggestedTokens: PropTypes.func,
|
|
tokens: PropTypes.array,
|
|
}
|
|
|
|
componentDidMount () {
|
|
this._checkPendingTokens()
|
|
}
|
|
|
|
componentDidUpdate () {
|
|
this._checkPendingTokens()
|
|
}
|
|
|
|
_checkPendingTokens () {
|
|
const { mostRecentOverviewPage, pendingTokens = {}, history } = this.props
|
|
|
|
if (Object.keys(pendingTokens).length > 0) {
|
|
return
|
|
}
|
|
|
|
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
|
|
global.platform.closeCurrentWindow()
|
|
} else {
|
|
history.push(mostRecentOverviewPage)
|
|
}
|
|
}
|
|
|
|
getTokenName (name, symbol) {
|
|
return typeof name === 'undefined'
|
|
? symbol
|
|
: `${name} (${symbol})`
|
|
}
|
|
|
|
render () {
|
|
const { addToken, pendingTokens, tokens, removeSuggestedTokens, history, mostRecentOverviewPage } = this.props
|
|
const pendingTokenKey = Object.keys(pendingTokens)[0]
|
|
const pendingToken = pendingTokens[pendingTokenKey]
|
|
const hasTokenDuplicates = this.checkTokenDuplicates(pendingTokens, tokens)
|
|
const reusesName = this.checkNameReuse(pendingTokens, tokens)
|
|
|
|
return (
|
|
<div className="page-container">
|
|
<div className="page-container__header">
|
|
<div className="page-container__title">
|
|
{ this.context.t('addSuggestedTokens') }
|
|
</div>
|
|
<div className="page-container__subtitle">
|
|
{ this.context.t('likeToAddTokens') }
|
|
</div>
|
|
{ hasTokenDuplicates ?
|
|
(
|
|
<div className="warning">
|
|
{ this.context.t('knownTokenWarning') }
|
|
</div>
|
|
) : null
|
|
}
|
|
{ reusesName ?
|
|
(
|
|
<div className="warning">
|
|
{ this.context.t('reusedTokenNameWarning') }
|
|
</div>
|
|
) : null
|
|
}
|
|
</div>
|
|
<div className="page-container__content">
|
|
<div className="confirm-add-token">
|
|
<div className="confirm-add-token__header">
|
|
<div className="confirm-add-token__token">
|
|
{ this.context.t('token') }
|
|
</div>
|
|
<div className="confirm-add-token__balance">
|
|
{ this.context.t('balance') }
|
|
</div>
|
|
</div>
|
|
<div className="confirm-add-token__token-list">
|
|
{
|
|
Object.entries(pendingTokens)
|
|
.map(([ address, token ]) => {
|
|
const { name, symbol, image } = token
|
|
|
|
return (
|
|
<div
|
|
className="confirm-add-token__token-list-item"
|
|
key={address}
|
|
>
|
|
<div className="confirm-add-token__token confirm-add-token__data">
|
|
<Identicon
|
|
className="confirm-add-token__token-icon"
|
|
diameter={48}
|
|
address={address}
|
|
image={image}
|
|
/>
|
|
<div className="confirm-add-token__name">
|
|
{ this.getTokenName(name, symbol) }
|
|
</div>
|
|
</div>
|
|
<div className="confirm-add-token__balance">
|
|
<TokenBalance token={token} />
|
|
</div>
|
|
</div>
|
|
)
|
|
})
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="page-container__footer">
|
|
<footer>
|
|
<Button
|
|
type="default"
|
|
large
|
|
className="page-container__footer-button"
|
|
onClick={() => {
|
|
removeSuggestedTokens()
|
|
.then(() => history.push(mostRecentOverviewPage))
|
|
}}
|
|
>
|
|
{ this.context.t('cancel') }
|
|
</Button>
|
|
<Button
|
|
type="secondary"
|
|
large
|
|
className="page-container__footer-button"
|
|
disabled={pendingTokens.length === 0}
|
|
onClick={() => {
|
|
addToken(pendingToken)
|
|
.then(() => removeSuggestedTokens())
|
|
.then(() => history.push(mostRecentOverviewPage))
|
|
}}
|
|
>
|
|
{ this.context.t('addToken') }
|
|
</Button>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
checkTokenDuplicates (pendingTokens, tokens) {
|
|
const pending = Object.keys(pendingTokens)
|
|
const existing = tokens.map((token) => token.address)
|
|
const dupes = pending.filter((proposed) => {
|
|
return existing.includes(proposed)
|
|
})
|
|
|
|
return dupes.length > 0
|
|
}
|
|
|
|
/**
|
|
* Returns true if any pendingTokens both:
|
|
* - Share a symbol with an existing `tokens` member.
|
|
* - Does not share an address with that same `tokens` member.
|
|
* This should be flagged as possibly deceptive or confusing.
|
|
*/
|
|
checkNameReuse (pendingTokens, tokens) {
|
|
const duplicates = Object.keys(pendingTokens)
|
|
.map((addr) => pendingTokens[addr])
|
|
.filter((token) => {
|
|
const dupes = tokens.filter((old) => old.symbol === token.symbol)
|
|
.filter((old) => old.address !== token.address)
|
|
return dupes.length > 0
|
|
})
|
|
return duplicates.length > 0
|
|
}
|
|
|
|
}
|