import React, { Component } from 'react' import PropTypes from 'prop-types' import ethUtil from 'ethereumjs-util' import { checkExistingAddresses } from './util' import { tokenInfoGetter } from '../../../token-util' import { DEFAULT_ROUTE, CONFIRM_ADD_TOKEN_ROUTE } from '../../../routes' import TextField from '../../text-field' import TokenList from './token-list' import TokenSearch from './token-search' import PageContainer from '../../page-container' import { Tabs, Tab } from '../../tabs' const emptyAddr = '0x0000000000000000000000000000000000000000' const SEARCH_TAB = 'SEARCH' const CUSTOM_TOKEN_TAB = 'CUSTOM_TOKEN' class AddToken extends Component { static contextTypes = { t: PropTypes.func, } static propTypes = { history: PropTypes.object, setPendingTokens: PropTypes.func, pendingTokens: PropTypes.object, clearPendingTokens: PropTypes.func, tokens: PropTypes.array, identities: PropTypes.object, } constructor (props) { super(props) this.state = { customAddress: '', customSymbol: '', customDecimals: 0, searchResults: [], selectedTokens: {}, tokenSelectorError: null, customAddressError: null, customSymbolError: null, customDecimalsError: null, autoFilled: false, displayedTab: SEARCH_TAB, } } componentDidMount () { this.tokenInfoGetter = tokenInfoGetter() const { pendingTokens = {} } = this.props const pendingTokenKeys = Object.keys(pendingTokens) if (pendingTokenKeys.length > 0) { let selectedTokens = {} let customToken = {} pendingTokenKeys.forEach(tokenAddress => { const token = pendingTokens[tokenAddress] const { isCustom } = token if (isCustom) { customToken = { ...token } } else { selectedTokens = { ...selectedTokens, [tokenAddress]: { ...token } } } }) const { address: customAddress = '', symbol: customSymbol = '', decimals: customDecimals = 0, } = customToken const displayedTab = Object.keys(selectedTokens).length > 0 ? SEARCH_TAB : CUSTOM_TOKEN_TAB this.setState({ selectedTokens, customAddress, customSymbol, customDecimals, displayedTab }) } } handleToggleToken (token) { const { address } = token const { selectedTokens = {} } = this.state const selectedTokensCopy = { ...selectedTokens } if (address in selectedTokensCopy) { delete selectedTokensCopy[address] } else { selectedTokensCopy[address] = token } this.setState({ selectedTokens: selectedTokensCopy, tokenSelectorError: null, }) } hasError () { const { tokenSelectorError, customAddressError, customSymbolError, customDecimalsError, } = this.state return tokenSelectorError || customAddressError || customSymbolError || customDecimalsError } hasSelected () { const { customAddress = '', selectedTokens = {} } = this.state return customAddress || Object.keys(selectedTokens).length > 0 } handleNext () { if (this.hasError()) { return } if (!this.hasSelected()) { this.setState({ tokenSelectorError: this.context.t('mustSelectOne') }) return } const { setPendingTokens, history } = this.props const { customAddress: address, customSymbol: symbol, customDecimals: decimals, selectedTokens, } = this.state const customToken = { address, symbol, decimals, } setPendingTokens({ customToken, selectedTokens }) history.push(CONFIRM_ADD_TOKEN_ROUTE) } async attemptToAutoFillTokenParams (address) { const { symbol = '', decimals = 0 } = await this.tokenInfoGetter(address) const autoFilled = Boolean(symbol && decimals) this.setState({ autoFilled }) this.handleCustomSymbolChange(symbol || '') this.handleCustomDecimalsChange(decimals) } handleCustomAddressChange (value) { const customAddress = value.trim() this.setState({ customAddress, customAddressError: null, tokenSelectorError: null, autoFilled: false, }) const isValidAddress = ethUtil.isValidAddress(customAddress) const standardAddress = ethUtil.addHexPrefix(customAddress).toLowerCase() switch (true) { case !isValidAddress: this.setState({ customAddressError: this.context.t('invalidAddress'), customSymbol: '', customDecimals: 0, customSymbolError: null, customDecimalsError: null, }) break case Boolean(this.props.identities[standardAddress]): this.setState({ customAddressError: this.context.t('personalAddressDetected'), }) break case checkExistingAddresses(customAddress, this.props.tokens): this.setState({ customAddressError: this.context.t('tokenAlreadyAdded'), }) break default: if (customAddress !== emptyAddr) { this.attemptToAutoFillTokenParams(customAddress) } } } handleCustomSymbolChange (value) { const customSymbol = value.trim() const symbolLength = customSymbol.length let customSymbolError = null if (symbolLength <= 0 || symbolLength >= 10) { customSymbolError = this.context.t('symbolBetweenZeroTen') } this.setState({ customSymbol, customSymbolError }) } handleCustomDecimalsChange (value) { const customDecimals = value.trim() const validDecimals = customDecimals !== null && customDecimals !== '' && customDecimals >= 0 && customDecimals <= 36 let customDecimalsError = null if (!validDecimals) { customDecimalsError = this.context.t('decimalsMustZerotoTen') } this.setState({ customDecimals, customDecimalsError }) } renderCustomTokenForm () { const { customAddress, customSymbol, customDecimals, customAddressError, customSymbolError, customDecimalsError, autoFilled, } = this.state return (
this.handleCustomAddressChange(e.target.value)} error={customAddressError} fullWidth margin="normal" /> this.handleCustomSymbolChange(e.target.value)} error={customSymbolError} fullWidth margin="normal" disabled={autoFilled} /> this.handleCustomDecimalsChange(e.target.value)} error={customDecimalsError} fullWidth margin="normal" disabled={autoFilled} />
) } renderSearchToken () { const { tokenSelectorError, selectedTokens, searchResults } = this.state return (
this.setState({ searchResults: results })} error={tokenSelectorError} />
this.handleToggleToken(token)} />
) } renderTabs () { return ( { this.renderSearchToken() } { this.renderCustomTokenForm() } ) } render () { const { history, clearPendingTokens } = this.props return ( this.handleNext()} disabled={this.hasError() || !this.hasSelected()} onCancel={() => { clearPendingTokens() history.push(DEFAULT_ROUTE) }} /> ) } } export default AddToken /+10 * databases/p5-DBI: update to 1.644Philippe Audeoud2024-08-302-4/+4 * Remove WWW entries moved into port MakefilesStefan Eßer2022-09-081-2/+1 * Add WWW entries to port MakefilesStefan Eßer2022-09-081-0/+1 * databases: remove 'Created by' linesTobias C. Berner2022-07-201-2/+0 * databases/p5-DBI: Add CPE informationBernhard Froehlich2021-09-091-1/+3 * Remove # $FreeBSD$ from Makefiles.Mathieu Arnold2021-04-061-1/+0 * - Update to 1.643Wen Heping2020-02-022-4/+4 * Update to 1.642Sunpoet Po-Chuan Hsieh2018-10-312-4/+4 * Update WWWSunpoet Po-Chuan Hsieh2018-05-281-1/+1 * Update to 1.641Sunpoet Po-Chuan Hsieh2018-03-222-4/+4 * Update to 1.640Sunpoet Po-Chuan Hsieh2018-01-292-4/+4 * Update to 1.639Sunpoet Po-Chuan Hsieh2017-12-303-4/+6 * Fix license information for portgs that use "the same license as Perl".Mathieu Arnold2017-09-151-1/+1 * Update to 1.637Sunpoet Po-Chuan Hsieh2017-08-172-6/+5 * Strip shared librarySunpoet Po-Chuan Hsieh2017-01-091-0/+4 * databases/p5-DBI: 1.634 -> 1.636Kurt Jaeger2016-04-253-5/+3 * Remove ${PORTSDIR}/ from dependencies, categories d, e, f, and g.Mathieu Arnold2016-04-011-2/+2 * - Sort PLISTSunpoet Po-Chuan Hsieh2015-11-151-3/+3 * Make it so that the default Perl is always called perl5.Mathieu Arnold2015-09-141-3/+3 * - Update to 1.634Sunpoet Po-Chuan Hsieh2015-08-142-4/+3 * - Remove DOCS optionSunpoet Po-Chuan Hsieh2015-06-291-8/+2 * Some OCD cleanups on some of the perl@ ports.Adam Weinberger2015-03-131-8/+2 * - Update to 1.633Sunpoet Po-Chuan Hsieh2015-01-133-4/+4 * - Update to 1.632Wen Heping2014-12-072-4/+3 * Change the way Perl modules are installed, update the default Perl to 5.18.Mathieu Arnold2014-11-262-64/+64 * Cleanup plistBaptiste Daroussin2014-10-201-19/+0 * Remove all the bootstrap files (.bs) from the plists.Mathieu Arnold2014-06-101-1/+0 * - Update to 1.631 [1]Frederic Culot2014-02-042-4/+4 * - Wrap up cd && foo in parenthesis, it'd break with bmake -jMathieu Arnold2013-11-251-2/+2 * - Update to 1.630Sunpoet Po-Chuan Hsieh2013-11-033-57/+60 * Try and be consistent with what commands are silent and not in *install.Mathieu Arnold2013-10-231-1/+1 * Add NO_STAGE all over the place in preparation for the staging support (cat: ...Baptiste Daroussin2013-09-211-0/+1 * - Convert to new perl frameworkMathieu Arnold2013-08-031-1/+2 * - Update to 1.628Frederic Culot2013-07-302-3/+3 * Update to 1.627.Anton Berezin2013-05-172-3/+3 * Update to 1.626.Anton Berezin2013-05-162-4/+4 * - Update to 1.625Martin Wilke2013-03-302-3/+3 * - Update to 1.624Sunpoet Po-Chuan Hsieh2013-03-242-8/+7 * Update to 1.623.Anton Berezin2013-01-032-3/+3 * Finish converting perl@ ports to new Options FrameworkBaptiste Daroussin2012-10-051-8/+5 * - Update to 1.622Sunpoet Po-Chuan Hsieh2012-06-072-3/+3 * - Update to 1.621Sunpoet Po-Chuan Hsieh2012-05-232-3/+3 * - Update to 1.620Sunpoet Po-Chuan Hsieh2012-04-262-3/+3 * - Update to 1.619Sunpoet Po-Chuan Hsieh2012-04-252-4/+4 * - Update to 1.618Sunpoet Po-Chuan Hsieh2012-02-292-3/+3 * - Update to 1.617Sunpoet Po-Chuan Hsieh2012-01-312-13/+9