diff options
Diffstat (limited to 'ui/app/components/pages/add-token.js')
-rw-r--r-- | ui/app/components/pages/add-token.js | 431 |
1 files changed, 0 insertions, 431 deletions
diff --git a/ui/app/components/pages/add-token.js b/ui/app/components/pages/add-token.js deleted file mode 100644 index 8d52571d0..000000000 --- a/ui/app/components/pages/add-token.js +++ /dev/null @@ -1,431 +0,0 @@ -const inherits = require('util').inherits -const Component = require('react').Component -const classnames = require('classnames') -const h = require('react-hyperscript') -const PropTypes = require('prop-types') -const connect = require('react-redux').connect -const R = require('ramda') -const Fuse = require('fuse.js') -const contractMap = require('eth-contract-metadata') -const TokenBalance = require('../../components/token-balance') -const Identicon = require('../../components/identicon') -const contractList = Object.entries(contractMap) - .map(([ _, tokenData]) => tokenData) - .filter(tokenData => Boolean(tokenData.erc20)) -const fuse = new Fuse(contractList, { - shouldSort: true, - threshold: 0.45, - location: 0, - distance: 100, - maxPatternLength: 32, - minMatchCharLength: 1, - keys: [ - { name: 'name', weight: 0.5 }, - { name: 'symbol', weight: 0.5 }, - ], -}) -const actions = require('../../actions') -const ethUtil = require('ethereumjs-util') -const { tokenInfoGetter } = require('../../token-util') -const { DEFAULT_ROUTE } = require('../../routes') - -const emptyAddr = '0x0000000000000000000000000000000000000000' - -AddTokenScreen.contextTypes = { - t: PropTypes.func, -} - -module.exports = connect(mapStateToProps, mapDispatchToProps)(AddTokenScreen) - - -function mapStateToProps (state) { - const { identities, tokens } = state.metamask - return { - identities, - tokens, - } -} - -function mapDispatchToProps (dispatch) { - return { - addTokens: tokens => dispatch(actions.addTokens(tokens)), - } -} - -inherits(AddTokenScreen, Component) -function AddTokenScreen () { - this.state = { - isShowingConfirmation: false, - isShowingInfoBox: true, - customAddress: '', - customSymbol: '', - customDecimals: '', - searchQuery: '', - selectedTokens: {}, - errors: {}, - autoFilled: false, - displayedTab: 'SEARCH', - } - this.tokenAddressDidChange = this.tokenAddressDidChange.bind(this) - this.tokenSymbolDidChange = this.tokenSymbolDidChange.bind(this) - this.tokenDecimalsDidChange = this.tokenDecimalsDidChange.bind(this) - this.onNext = this.onNext.bind(this) - Component.call(this) -} - -AddTokenScreen.prototype.componentWillMount = function () { - this.tokenInfoGetter = tokenInfoGetter() -} - -AddTokenScreen.prototype.toggleToken = function (address, token) { - const { selectedTokens = {}, errors } = this.state - const selectedTokensCopy = { ...selectedTokens } - - if (address in selectedTokensCopy) { - delete selectedTokensCopy[address] - } else { - selectedTokensCopy[address] = token - } - - this.setState({ - selectedTokens: selectedTokensCopy, - errors: { - ...errors, - tokenSelector: null, - }, - }) -} - -AddTokenScreen.prototype.onNext = function () { - const { isValid, errors } = this.validate() - - return !isValid - ? this.setState({ errors }) - : this.setState({ isShowingConfirmation: true }) -} - -AddTokenScreen.prototype.tokenAddressDidChange = function (e) { - const customAddress = e.target.value.trim() - this.setState({ customAddress }) - if (ethUtil.isValidAddress(customAddress) && customAddress !== emptyAddr) { - this.attemptToAutoFillTokenParams(customAddress) - } else { - this.setState({ - customSymbol: '', - customDecimals: 0, - }) - } -} - -AddTokenScreen.prototype.tokenSymbolDidChange = function (e) { - const customSymbol = e.target.value.trim() - this.setState({ customSymbol }) -} - -AddTokenScreen.prototype.tokenDecimalsDidChange = function (e) { - const customDecimals = e.target.value.trim() - this.setState({ customDecimals }) -} - -AddTokenScreen.prototype.checkExistingAddresses = function (address) { - if (!address) return false - const tokensList = this.props.tokens - const matchesAddress = existingToken => { - return existingToken.address.toLowerCase() === address.toLowerCase() - } - - return R.any(matchesAddress)(tokensList) -} - -AddTokenScreen.prototype.validate = function () { - const errors = {} - const identitiesList = Object.keys(this.props.identities) - const { customAddress, customSymbol, customDecimals, selectedTokens } = this.state - const standardAddress = ethUtil.addHexPrefix(customAddress).toLowerCase() - - if (customAddress) { - const validAddress = ethUtil.isValidAddress(customAddress) - if (!validAddress) { - errors.customAddress = this.context.t('invalidAddress') - } - - const validDecimals = customDecimals !== null - && customDecimals !== '' - && customDecimals >= 0 - && customDecimals < 36 - if (!validDecimals) { - errors.customDecimals = this.context.t('decimalsMustZerotoTen') - } - - const symbolLen = customSymbol.trim().length - const validSymbol = symbolLen > 0 && symbolLen < 10 - if (!validSymbol) { - errors.customSymbol = this.context.t('symbolBetweenZeroTen') - } - - const ownAddress = identitiesList.includes(standardAddress) - if (ownAddress) { - errors.customAddress = this.context.t('personalAddressDetected') - } - - const tokenAlreadyAdded = this.checkExistingAddresses(customAddress) - if (tokenAlreadyAdded) { - errors.customAddress = this.context.t('tokenAlreadyAdded') - } - } else if ( - Object.entries(selectedTokens) - .reduce((isEmpty, [ symbol, isSelected ]) => ( - isEmpty && !isSelected - ), true) - ) { - errors.tokenSelector = this.context.t('mustSelectOne') - } - - return { - isValid: !Object.keys(errors).length, - errors, - } -} - -AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) { - const { symbol, decimals } = await this.tokenInfoGetter(address) - if (symbol && decimals) { - this.setState({ - customSymbol: symbol, - customDecimals: decimals, - autoFilled: true, - }) - } -} - -AddTokenScreen.prototype.renderCustomForm = function () { - const { autoFilled, customAddress, customSymbol, customDecimals, errors } = this.state - - return ( - h('div.add-token__add-custom-form', [ - h('div', { - className: classnames('add-token__add-custom-field', { - 'add-token__add-custom-field--error': errors.customAddress, - }), - }, [ - h('div.add-token__add-custom-label', this.context.t('tokenAddress')), - h('input.add-token__add-custom-input', { - type: 'text', - onChange: this.tokenAddressDidChange, - value: customAddress, - }), - h('div.add-token__add-custom-error-message', errors.customAddress), - ]), - h('div', { - className: classnames('add-token__add-custom-field', { - 'add-token__add-custom-field--error': errors.customSymbol, - }), - }, [ - h('div.add-token__add-custom-label', this.context.t('tokenSymbol')), - h('input.add-token__add-custom-input', { - type: 'text', - onChange: this.tokenSymbolDidChange, - value: customSymbol, - disabled: autoFilled, - }), - h('div.add-token__add-custom-error-message', errors.customSymbol), - ]), - h('div', { - className: classnames('add-token__add-custom-field', { - 'add-token__add-custom-field--error': errors.customDecimals, - }), - }, [ - h('div.add-token__add-custom-label', this.context.t('decimal')), - h('input.add-token__add-custom-input', { - type: 'number', - onChange: this.tokenDecimalsDidChange, - value: customDecimals, - disabled: autoFilled, - }), - h('div.add-token__add-custom-error-message', errors.customDecimals), - ]), - ]) - ) -} - -AddTokenScreen.prototype.renderTokenList = function () { - const { searchQuery = '', selectedTokens } = this.state - const fuseSearchResult = fuse.search(searchQuery) - const addressSearchResult = contractList.filter(token => { - return token.address.toLowerCase() === searchQuery.toLowerCase() - }) - const results = [...addressSearchResult, ...fuseSearchResult] - - return h('div', [ - results.length > 0 && h('div.add-token__token-icons-title', this.context.t('popularTokens')), - h('div.add-token__token-icons-container', Array(6).fill(undefined) - .map((_, i) => { - const { logo, symbol, name, address } = results[i] || {} - const tokenAlreadyAdded = this.checkExistingAddresses(address) - return Boolean(logo || symbol || name) && ( - h('div.add-token__token-wrapper', { - className: classnames({ - 'add-token__token-wrapper--selected': selectedTokens[address], - 'add-token__token-wrapper--disabled': tokenAlreadyAdded, - }), - onClick: () => !tokenAlreadyAdded && this.toggleToken(address, results[i]), - }, [ - h('div.add-token__token-icon', { - style: { - backgroundImage: logo && `url(images/contract/${logo})`, - }, - }), - h('div.add-token__token-data', [ - h('div.add-token__token-symbol', symbol), - h('div.add-token__token-name', name), - ]), - // tokenAlreadyAdded && ( - // h('div.add-token__token-message', 'Already added') - // ), - ]) - ) - })), - ]) -} - -AddTokenScreen.prototype.renderConfirmation = function () { - const { - customAddress: address, - customSymbol: symbol, - customDecimals: decimals, - selectedTokens, - } = this.state - - const { addTokens, history } = this.props - - const customToken = { - address, - symbol, - decimals, - } - - const tokens = address && symbol && decimals - ? { ...selectedTokens, [address]: customToken } - : selectedTokens - - return ( - h('div.add-token', [ - h('div.add-token__wrapper', [ - h('div.add-token__content-container.add-token__confirmation-content', [ - h('div.add-token__description.add-token__confirmation-description', this.context.t('balances')), - h('div.add-token__confirmation-token-list', - Object.entries(tokens) - .map(([ address, token ]) => ( - h('span.add-token__confirmation-token-list-item', [ - h(Identicon, { - className: 'add-token__confirmation-token-icon', - diameter: 75, - address, - }), - h(TokenBalance, { token }), - ]) - )) - ), - ]), - ]), - h('div.add-token__buttons', [ - h('button.btn-secondary--lg.add-token__cancel-button', { - onClick: () => this.setState({ isShowingConfirmation: false }), - }, this.context.t('back')), - h('button.btn-primary--lg', { - onClick: () => addTokens(tokens).then(() => history.push(DEFAULT_ROUTE)), - }, this.context.t('addTokens')), - ]), - ]) - ) -} - -AddTokenScreen.prototype.displayTab = function (selectedTab) { - this.setState({ displayedTab: selectedTab }) -} - -AddTokenScreen.prototype.renderTabs = function () { - const { isShowingInfoBox, displayedTab, errors } = this.state - - return displayedTab === 'CUSTOM_TOKEN' - ? this.renderCustomForm() - : h('div', [ - h('div.add-token__wrapper', [ - h('div.add-token__content-container', [ - isShowingInfoBox && h('div.add-token__info-box', [ - h('div.add-token__info-box__close', { - onClick: () => this.setState({ isShowingInfoBox: false }), - }), - h('div.add-token__info-box__title', this.context.t('whatsThis')), - h('div.add-token__info-box__copy', this.context.t('keepTrackTokens')), - h('a.add-token__info-box__copy--blue', { - href: 'http://metamask.helpscoutdocs.com/article/16-managing-erc20-tokens', - target: '_blank', - }, this.context.t('learnMore')), - ]), - h('div.add-token__input-container', [ - h('input.add-token__input', { - type: 'text', - placeholder: this.context.t('searchTokens'), - onChange: e => this.setState({ searchQuery: e.target.value }), - }), - h('div.add-token__search-input-error-message', errors.tokenSelector), - ]), - this.renderTokenList(), - ]), - ]), - ]) -} - -AddTokenScreen.prototype.render = function () { - const { - isShowingConfirmation, - displayedTab, - } = this.state - const { history } = this.props - - return h('div.add-token', [ - h('div.add-token__header', [ - h('div.add-token__header__cancel', { - onClick: () => history.push(DEFAULT_ROUTE), - }, [ - h('i.fa.fa-angle-left.fa-lg'), - h('span', this.context.t('cancel')), - ]), - h('div.add-token__header__title', this.context.t('addTokens')), - isShowingConfirmation && h('div.add-token__header__subtitle', this.context.t('likeToAddTokens')), - !isShowingConfirmation && h('div.add-token__header__tabs', [ - - h('div.add-token__header__tabs__tab', { - className: classnames('add-token__header__tabs__tab', { - 'add-token__header__tabs__selected': displayedTab === 'SEARCH', - 'add-token__header__tabs__unselected': displayedTab !== 'SEARCH', - }), - onClick: () => this.displayTab('SEARCH'), - }, this.context.t('search')), - - h('div.add-token__header__tabs__tab', { - className: classnames('add-token__header__tabs__tab', { - 'add-token__header__tabs__selected': displayedTab === 'CUSTOM_TOKEN', - 'add-token__header__tabs__unselected': displayedTab !== 'CUSTOM_TOKEN', - }), - onClick: () => this.displayTab('CUSTOM_TOKEN'), - }, this.context.t('customToken')), - - ]), - ]), - - isShowingConfirmation - ? this.renderConfirmation() - : this.renderTabs(), - - !isShowingConfirmation && h('div.add-token__buttons', [ - h('button.btn-secondary--lg.add-token__cancel-button', { - onClick: () => history.push(DEFAULT_ROUTE), - }, this.context.t('cancel')), - h('button.btn-primary--lg.add-token__confirm-button', { - onClick: this.onNext, - }, this.context.t('next')), - ]), - ]) -} |