mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Integrate Add Token
This commit is contained in:
parent
24fd16b1be
commit
3ec2f53463
@ -147,6 +147,7 @@ var actions = {
|
|||||||
SHOW_ADD_TOKEN_PAGE: 'SHOW_ADD_TOKEN_PAGE',
|
SHOW_ADD_TOKEN_PAGE: 'SHOW_ADD_TOKEN_PAGE',
|
||||||
showAddTokenPage,
|
showAddTokenPage,
|
||||||
addToken,
|
addToken,
|
||||||
|
addTokens,
|
||||||
setRpcTarget: setRpcTarget,
|
setRpcTarget: setRpcTarget,
|
||||||
setDefaultRpcTarget: setDefaultRpcTarget,
|
setDefaultRpcTarget: setDefaultRpcTarget,
|
||||||
setProviderType: setProviderType,
|
setProviderType: setProviderType,
|
||||||
@ -700,18 +701,40 @@ function showAddTokenPage () {
|
|||||||
function addToken (address, symbol, decimals) {
|
function addToken (address, symbol, decimals) {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(actions.showLoadingIndication())
|
dispatch(actions.showLoadingIndication())
|
||||||
background.addToken(address, symbol, decimals, (err) => {
|
return new Promise((resolve, reject) => {
|
||||||
dispatch(actions.hideLoadingIndication())
|
background.addToken(address, symbol, decimals, (err) => {
|
||||||
if (err) {
|
dispatch(actions.hideLoadingIndication())
|
||||||
return dispatch(actions.displayWarning(err.message))
|
if (err) {
|
||||||
}
|
dispatch(actions.displayWarning(err.message))
|
||||||
setTimeout(() => {
|
reject(err)
|
||||||
dispatch(actions.goHome())
|
}
|
||||||
}, 250)
|
resolve()
|
||||||
|
// setTimeout(() => {
|
||||||
|
// dispatch(actions.goHome())
|
||||||
|
// }, 250)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addTokens (tokens) {
|
||||||
|
return dispatch => {
|
||||||
|
if (Array.isArray(tokens)) {
|
||||||
|
return Promise.all(tokens.map(({ address, symbol, decimals }) => (
|
||||||
|
dispatch(addToken(address, symbol, decimals))
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
return Promise.all(
|
||||||
|
Object
|
||||||
|
.entries(tokens)
|
||||||
|
.map(([_, { address, symbol, decimals }]) => (
|
||||||
|
dispatch(addToken(address, symbol, decimals))
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function goBackToInitView () {
|
function goBackToInitView () {
|
||||||
return {
|
return {
|
||||||
type: actions.BACK_TO_INIT_MENU,
|
type: actions.BACK_TO_INIT_MENU,
|
||||||
|
@ -5,6 +5,8 @@ const h = require('react-hyperscript')
|
|||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const Fuse = require('fuse.js')
|
const Fuse = require('fuse.js')
|
||||||
const contractMap = require('eth-contract-metadata')
|
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)
|
const contractList = Object.entries(contractMap).map(([ _, tokenData]) => tokenData)
|
||||||
const fuse = new Fuse(contractList, {
|
const fuse = new Fuse(contractList, {
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
@ -16,9 +18,6 @@ const fuse = new Fuse(contractList, {
|
|||||||
keys: ['address', 'name', 'symbol'],
|
keys: ['address', 'name', 'symbol'],
|
||||||
})
|
})
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
// const Tooltip = require('./components/tooltip.js')
|
|
||||||
|
|
||||||
|
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
const abi = require('human-standard-token-abi')
|
const abi = require('human-standard-token-abi')
|
||||||
const Eth = require('ethjs-query')
|
const Eth = require('ethjs-query')
|
||||||
@ -37,275 +36,27 @@ function mapStateToProps (state) {
|
|||||||
function mapDispatchToProps (dispatch) {
|
function mapDispatchToProps (dispatch) {
|
||||||
return {
|
return {
|
||||||
goHome: () => dispatch(actions.goHome()),
|
goHome: () => dispatch(actions.goHome()),
|
||||||
|
addTokens: tokens => dispatch(actions.addTokens(tokens)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(AddTokenScreen, Component)
|
inherits(AddTokenScreen, Component)
|
||||||
function AddTokenScreen () {
|
function AddTokenScreen () {
|
||||||
this.state = {
|
this.state = {
|
||||||
// warning: null,
|
isShowingConfirmation: false,
|
||||||
// address: null,
|
|
||||||
// symbol: 'TOKEN',
|
|
||||||
// decimals: 18,
|
|
||||||
customAddress: '',
|
customAddress: '',
|
||||||
customSymbol: '',
|
customSymbol: '',
|
||||||
customDecimals: 0,
|
customDecimals: 0,
|
||||||
searchQuery: '',
|
searchQuery: '',
|
||||||
isCollapsed: true,
|
isCollapsed: true,
|
||||||
selectedToken: {},
|
selectedTokens: {},
|
||||||
|
errors: {},
|
||||||
}
|
}
|
||||||
this.tokenAddressDidChange = this.tokenAddressDidChange.bind(this)
|
this.tokenAddressDidChange = this.tokenAddressDidChange.bind(this)
|
||||||
|
this.onNext = this.onNext.bind(this)
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
AddTokenScreen.prototype.toggleToken = function (symbol) {
|
|
||||||
const { selectedToken } = this.state
|
|
||||||
const { [symbol]: isSelected } = selectedToken
|
|
||||||
this.setState({
|
|
||||||
selectedToken: {
|
|
||||||
...selectedToken,
|
|
||||||
[symbol]: !isSelected,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
AddTokenScreen.prototype.renderCustomForm = function () {
|
|
||||||
const { customAddress, customSymbol, customDecimals } = this.state
|
|
||||||
|
|
||||||
return !this.state.isCollapsed && (
|
|
||||||
h('div.add-token__add-custom-form', [
|
|
||||||
h('div.add-token__add-custom-field', [
|
|
||||||
h('div.add-token__add-custom-label', 'Token Address'),
|
|
||||||
h('input.add-token__add-custom-input', {
|
|
||||||
type: 'text',
|
|
||||||
onChange: this.tokenAddressDidChange,
|
|
||||||
value: customAddress,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
h('div.add-token__add-custom-field', [
|
|
||||||
h('div.add-token__add-custom-label', 'Token Symbol'),
|
|
||||||
h('input.add-token__add-custom-input', {
|
|
||||||
type: 'text',
|
|
||||||
value: customSymbol,
|
|
||||||
disabled: true,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
h('div.add-token__add-custom-field', [
|
|
||||||
h('div.add-token__add-custom-label', 'Decimals of Precision'),
|
|
||||||
h('input.add-token__add-custom-input', {
|
|
||||||
type: 'number',
|
|
||||||
value: customDecimals,
|
|
||||||
disabled: true,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AddTokenScreen.prototype.renderTokenList = function () {
|
|
||||||
const { searchQuery = '', selectedToken } = this.state
|
|
||||||
const results = searchQuery
|
|
||||||
? fuse.search(searchQuery) || []
|
|
||||||
: contractList
|
|
||||||
|
|
||||||
return Array(6).fill(undefined)
|
|
||||||
.map((_, i) => {
|
|
||||||
const { logo, symbol, name } = results[i] || {}
|
|
||||||
return Boolean(logo || symbol || name) && (
|
|
||||||
h('div.add-token__token-wrapper', {
|
|
||||||
className: classnames('add-token__token-wrapper', {
|
|
||||||
'add-token__token-wrapper--selected': selectedToken[symbol],
|
|
||||||
}),
|
|
||||||
onClick: () => this.toggleToken(symbol),
|
|
||||||
}, [
|
|
||||||
h('div.add-token__token-icon', {
|
|
||||||
style: {
|
|
||||||
backgroundImage: `url(images/contract/${logo})`,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
h('div.add-token__token-data', [
|
|
||||||
h('div.add-token__token-symbol', symbol),
|
|
||||||
h('div.add-token__token-name', name),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
AddTokenScreen.prototype.render = function () {
|
|
||||||
const { isCollapsed } = this.state
|
|
||||||
const { goHome } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('div.add-token', [
|
|
||||||
h('div.add-token__wrapper', [
|
|
||||||
h('div.add-token__title-container', [
|
|
||||||
h('div.add-token__title', 'Add Token'),
|
|
||||||
h('div.add-token__description', 'Keep track of the tokens you’ve bought with your MetaMask account. If you bought tokens using a different account, those tokens will not appear here.'),
|
|
||||||
h('div.add-token__description', 'Search for tokens or select from our list of popular tokens.'),
|
|
||||||
]),
|
|
||||||
h('div.add-token__content-container', [
|
|
||||||
h('div.add-token__input-container', [
|
|
||||||
h('input.add-token__input', {
|
|
||||||
type: 'text',
|
|
||||||
placeholder: 'Search',
|
|
||||||
onChange: e => this.setState({ searchQuery: e.target.value }),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
h(
|
|
||||||
'div.add-token__token-icons-container',
|
|
||||||
this.renderTokenList(),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
h('div.add-token__footers', [
|
|
||||||
h('div.add-token__add-custom', {
|
|
||||||
onClick: () => this.setState({ isCollapsed: !isCollapsed }),
|
|
||||||
}, 'Add custom token'),
|
|
||||||
this.renderCustomForm(),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
h('div.add-token__buttons', [
|
|
||||||
h('button.btn-secondary', 'Next'),
|
|
||||||
h('button.btn-tertiary', {
|
|
||||||
onClick: goHome,
|
|
||||||
}, 'Cancel'),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTokenScreen.prototype.render = function () {
|
|
||||||
// const state = this.state
|
|
||||||
// const props = this.props
|
|
||||||
// const { warning, symbol, decimals } = state
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// h('.flex-column.flex-grow', [
|
|
||||||
|
|
||||||
// // subtitle and nav
|
|
||||||
// h('.section-title.flex-row.flex-center', [
|
|
||||||
// h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
|
||||||
// onClick: (event) => {
|
|
||||||
// props.dispatch(actions.goHome())
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
// h('h2.page-subtitle', 'Add Token'),
|
|
||||||
// ]),
|
|
||||||
|
|
||||||
// h('.error', {
|
|
||||||
// style: {
|
|
||||||
// display: warning ? 'block' : 'none',
|
|
||||||
// padding: '0 20px',
|
|
||||||
// textAlign: 'center',
|
|
||||||
// },
|
|
||||||
// }, warning),
|
|
||||||
|
|
||||||
// // conf view
|
|
||||||
// h('.flex-column.flex-justify-center.flex-grow.select-none', [
|
|
||||||
// h('.flex-space-around', {
|
|
||||||
// style: {
|
|
||||||
// padding: '20px',
|
|
||||||
// },
|
|
||||||
// }, [
|
|
||||||
|
|
||||||
// h('div', [
|
|
||||||
// h(Tooltip, {
|
|
||||||
// position: 'top',
|
|
||||||
// title: 'The contract of the actual token contract. Click for more info.',
|
|
||||||
// }, [
|
|
||||||
// h('a', {
|
|
||||||
// style: { fontWeight: 'bold', paddingRight: '10px'},
|
|
||||||
// href: 'https://consensyssupport.happyfox.com/staff/kb/article/24-what-is-a-token-contract-address',
|
|
||||||
// target: '_blank',
|
|
||||||
// }, [
|
|
||||||
// h('span', 'Token Contract Address '),
|
|
||||||
// h('i.fa.fa-question-circle'),
|
|
||||||
// ]),
|
|
||||||
// ]),
|
|
||||||
// ]),
|
|
||||||
|
|
||||||
// h('section.flex-row.flex-center', [
|
|
||||||
// h('input#token-address', {
|
|
||||||
// name: 'address',
|
|
||||||
// placeholder: 'Token Contract Address',
|
|
||||||
// onChange: this.tokenAddressDidChange.bind(this),
|
|
||||||
// style: {
|
|
||||||
// width: 'inherit',
|
|
||||||
// flex: '1 0 auto',
|
|
||||||
// height: '30px',
|
|
||||||
// margin: '8px',
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
// ]),
|
|
||||||
|
|
||||||
// h('div', [
|
|
||||||
// h('span', {
|
|
||||||
// style: { fontWeight: 'bold', paddingRight: '10px'},
|
|
||||||
// }, 'Token Symbol'),
|
|
||||||
// ]),
|
|
||||||
|
|
||||||
// h('div', { style: {display: 'flex'} }, [
|
|
||||||
// h('input#token_symbol', {
|
|
||||||
// placeholder: `Like "ETH"`,
|
|
||||||
// value: symbol,
|
|
||||||
// style: {
|
|
||||||
// width: 'inherit',
|
|
||||||
// flex: '1 0 auto',
|
|
||||||
// height: '30px',
|
|
||||||
// margin: '8px',
|
|
||||||
// },
|
|
||||||
// onChange: (event) => {
|
|
||||||
// var element = event.target
|
|
||||||
// var symbol = element.value
|
|
||||||
// this.setState({ symbol })
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
// ]),
|
|
||||||
|
|
||||||
// h('div', [
|
|
||||||
// h('span', {
|
|
||||||
// style: { fontWeight: 'bold', paddingRight: '10px'},
|
|
||||||
// }, 'Decimals of Precision'),
|
|
||||||
// ]),
|
|
||||||
|
|
||||||
// h('div', { style: {display: 'flex'} }, [
|
|
||||||
// h('input#token_decimals', {
|
|
||||||
// value: decimals,
|
|
||||||
// type: 'number',
|
|
||||||
// min: 0,
|
|
||||||
// max: 36,
|
|
||||||
// style: {
|
|
||||||
// width: 'inherit',
|
|
||||||
// flex: '1 0 auto',
|
|
||||||
// height: '30px',
|
|
||||||
// margin: '8px',
|
|
||||||
// },
|
|
||||||
// onChange: (event) => {
|
|
||||||
// var element = event.target
|
|
||||||
// var decimals = element.value.trim()
|
|
||||||
// this.setState({ decimals })
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
// ]),
|
|
||||||
|
|
||||||
// h('button', {
|
|
||||||
// style: {
|
|
||||||
// alignSelf: 'center',
|
|
||||||
// },
|
|
||||||
// onClick: (event) => {
|
|
||||||
// const valid = this.validateInputs()
|
|
||||||
// if (!valid) return
|
|
||||||
|
|
||||||
// const { address, symbol, decimals } = this.state
|
|
||||||
// this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals))
|
|
||||||
// },
|
|
||||||
// }, 'Add'),
|
|
||||||
// ]),
|
|
||||||
// ]),
|
|
||||||
// ])
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
AddTokenScreen.prototype.componentWillMount = function () {
|
AddTokenScreen.prototype.componentWillMount = function () {
|
||||||
if (typeof global.ethereumProvider === 'undefined') return
|
if (typeof global.ethereumProvider === 'undefined') return
|
||||||
|
|
||||||
@ -314,6 +65,29 @@ AddTokenScreen.prototype.componentWillMount = function () {
|
|||||||
this.TokenContract = this.contract(abi)
|
this.TokenContract = this.contract(abi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddTokenScreen.prototype.toggleToken = function (address, token) {
|
||||||
|
const { selectedTokens, errors } = this.state
|
||||||
|
const { [address]: selectedToken } = selectedTokens
|
||||||
|
this.setState({
|
||||||
|
selectedTokens: {
|
||||||
|
...selectedTokens,
|
||||||
|
[address]: selectedToken ? null : token,
|
||||||
|
},
|
||||||
|
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) {
|
AddTokenScreen.prototype.tokenAddressDidChange = function (e) {
|
||||||
const customAddress = e.target.value.trim()
|
const customAddress = e.target.value.trim()
|
||||||
this.setState({ customAddress })
|
this.setState({ customAddress })
|
||||||
@ -327,45 +101,46 @@ AddTokenScreen.prototype.tokenAddressDidChange = function (e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddTokenScreen.prototype.validateInputs = function () {
|
AddTokenScreen.prototype.validate = function () {
|
||||||
let msg = ''
|
const errors = {}
|
||||||
const state = this.state
|
|
||||||
const identitiesList = Object.keys(this.props.identities)
|
const identitiesList = Object.keys(this.props.identities)
|
||||||
const { address, symbol, decimals } = state
|
const { customAddress, customSymbol, customDecimals, selectedTokens } = this.state
|
||||||
const standardAddress = ethUtil.addHexPrefix(address).toLowerCase()
|
const standardAddress = ethUtil.addHexPrefix(customAddress).toLowerCase()
|
||||||
|
|
||||||
const validAddress = ethUtil.isValidAddress(address)
|
if (customAddress) {
|
||||||
if (!validAddress) {
|
const validAddress = ethUtil.isValidAddress(customAddress)
|
||||||
msg += 'Address is invalid. '
|
if (!validAddress) {
|
||||||
|
errors.customAddress = 'Address is invalid. '
|
||||||
|
}
|
||||||
|
|
||||||
|
const validDecimals = customDecimals >= 0 && customDecimals < 36
|
||||||
|
if (!validDecimals) {
|
||||||
|
errors.customDecimals = 'Decimals must be at least 0, and not over 36.'
|
||||||
|
}
|
||||||
|
|
||||||
|
const symbolLen = customSymbol.trim().length
|
||||||
|
const validSymbol = symbolLen > 0 && symbolLen < 10
|
||||||
|
if (!validSymbol) {
|
||||||
|
errors.customSymbol = 'Symbol must be between 0 and 10 characters.'
|
||||||
|
}
|
||||||
|
|
||||||
|
const ownAddress = identitiesList.includes(standardAddress)
|
||||||
|
if (ownAddress) {
|
||||||
|
errors.customAddress = 'Personal address detected. Input the token contract address.'
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
Object.entries(selectedTokens)
|
||||||
|
.reduce((isEmpty, [ symbol, isSelected ]) => (
|
||||||
|
isEmpty && !isSelected
|
||||||
|
), true)
|
||||||
|
) {
|
||||||
|
errors.tokenSelector = 'Must select at least 1 token.'
|
||||||
}
|
}
|
||||||
|
|
||||||
const validDecimals = decimals >= 0 && decimals < 36
|
return {
|
||||||
if (!validDecimals) {
|
isValid: !Object.keys(errors).length,
|
||||||
msg += 'Decimals must be at least 0, and not over 36. '
|
errors,
|
||||||
}
|
}
|
||||||
|
|
||||||
const symbolLen = symbol.trim().length
|
|
||||||
const validSymbol = symbolLen > 0 && symbolLen < 10
|
|
||||||
if (!validSymbol) {
|
|
||||||
msg += 'Symbol must be between 0 and 10 characters.'
|
|
||||||
}
|
|
||||||
|
|
||||||
const ownAddress = identitiesList.includes(standardAddress)
|
|
||||||
if (ownAddress) {
|
|
||||||
msg = 'Personal address detected. Input the token contract address.'
|
|
||||||
}
|
|
||||||
|
|
||||||
const isValid = validAddress && validDecimals && !ownAddress
|
|
||||||
|
|
||||||
if (!isValid) {
|
|
||||||
this.setState({
|
|
||||||
warning: msg,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.setState({ warning: null })
|
|
||||||
}
|
|
||||||
|
|
||||||
return isValid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) {
|
AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) {
|
||||||
@ -384,3 +159,184 @@ AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddTokenScreen.prototype.renderCustomForm = function () {
|
||||||
|
const { customAddress, customSymbol, customDecimals, errors } = this.state
|
||||||
|
|
||||||
|
return !this.state.isCollapsed && (
|
||||||
|
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', 'Token Address'),
|
||||||
|
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', 'Token Symbol'),
|
||||||
|
h('input.add-token__add-custom-input', {
|
||||||
|
type: 'text',
|
||||||
|
value: customSymbol,
|
||||||
|
disabled: true,
|
||||||
|
}),
|
||||||
|
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', 'Decimals of Precision'),
|
||||||
|
h('input.add-token__add-custom-input', {
|
||||||
|
type: 'number',
|
||||||
|
value: customDecimals,
|
||||||
|
disabled: true,
|
||||||
|
}),
|
||||||
|
h('div.add-token__add-custom-error-message', errors.customDecimals),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTokenScreen.prototype.renderTokenList = function () {
|
||||||
|
const { searchQuery = '', selectedTokens } = this.state
|
||||||
|
const results = searchQuery
|
||||||
|
? fuse.search(searchQuery) || []
|
||||||
|
: contractList
|
||||||
|
|
||||||
|
return Array(6).fill(undefined)
|
||||||
|
.map((_, i) => {
|
||||||
|
const { logo, symbol, name, address } = results[i] || {}
|
||||||
|
return Boolean(logo || symbol || name) && (
|
||||||
|
h('div.add-token__token-wrapper', {
|
||||||
|
className: classnames('add-token__token-wrapper', {
|
||||||
|
'add-token__token-wrapper--selected': selectedTokens[address],
|
||||||
|
}),
|
||||||
|
onClick: () => this.toggleToken(address, results[i]),
|
||||||
|
}, [
|
||||||
|
h('div.add-token__token-icon', {
|
||||||
|
style: {
|
||||||
|
backgroundImage: `url(images/contract/${logo})`,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
h('div.add-token__token-data', [
|
||||||
|
h('div.add-token__token-symbol', symbol),
|
||||||
|
h('div.add-token__token-name', name),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTokenScreen.prototype.renderConfirmation = function () {
|
||||||
|
const {
|
||||||
|
customAddress: address,
|
||||||
|
customSymbol: symbol,
|
||||||
|
customDecimals: decimals,
|
||||||
|
selectedTokens,
|
||||||
|
} = this.state
|
||||||
|
|
||||||
|
const { addTokens, goHome } = 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__title-container.add-token__confirmation-title', [
|
||||||
|
h('div.add-token__title', 'Add Token'),
|
||||||
|
h('div.add-token__description', 'Would you like to add these tokens?'),
|
||||||
|
]),
|
||||||
|
h('div.add-token__content-container.add-token__confirmation-content', [
|
||||||
|
h('div.add-token__description.add-token__confirmation-description', 'Your 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', {
|
||||||
|
onClick: () => addTokens(tokens).then(goHome),
|
||||||
|
}, 'Add Tokens'),
|
||||||
|
h('button.btn-tertiary', {
|
||||||
|
onClick: () => this.setState({ isShowingConfirmation: false }),
|
||||||
|
}, 'Back'),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTokenScreen.prototype.render = function () {
|
||||||
|
const { isCollapsed, errors, isShowingConfirmation } = this.state
|
||||||
|
const { goHome } = this.props
|
||||||
|
|
||||||
|
return isShowingConfirmation
|
||||||
|
? this.renderConfirmation()
|
||||||
|
: (
|
||||||
|
h('div.add-token', [
|
||||||
|
h('div.add-token__wrapper', [
|
||||||
|
h('div.add-token__title-container', [
|
||||||
|
h('div.add-token__title', 'Add Token'),
|
||||||
|
h('div.add-token__description', 'Keep track of the tokens you’ve bought with your MetaMask account. If you bought tokens using a different account, those tokens will not appear here.'),
|
||||||
|
h('div.add-token__description', 'Search for tokens or select from our list of popular tokens.'),
|
||||||
|
]),
|
||||||
|
h('div.add-token__content-container', [
|
||||||
|
h('div.add-token__input-container', [
|
||||||
|
h('input.add-token__input', {
|
||||||
|
type: 'text',
|
||||||
|
placeholder: 'Search',
|
||||||
|
onChange: e => this.setState({ searchQuery: e.target.value }),
|
||||||
|
}),
|
||||||
|
h('div.add-token__search-input-error-message', errors.tokenSelector),
|
||||||
|
]),
|
||||||
|
h(
|
||||||
|
'div.add-token__token-icons-container',
|
||||||
|
this.renderTokenList(),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
h('div.add-token__footers', [
|
||||||
|
h('div.add-token__add-custom', {
|
||||||
|
onClick: () => this.setState({ isCollapsed: !isCollapsed }),
|
||||||
|
}, 'Add custom token'),
|
||||||
|
this.renderCustomForm(),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
h('div.add-token__buttons', [
|
||||||
|
h('button.btn-secondary', {
|
||||||
|
onClick: this.onNext,
|
||||||
|
}, 'Next'),
|
||||||
|
h('button.btn-tertiary', {
|
||||||
|
onClick: goHome,
|
||||||
|
}, 'Cancel'),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -17,7 +17,8 @@ module.exports = connect(mapStateToProps)(TokenBalance)
|
|||||||
inherits(TokenBalance, Component)
|
inherits(TokenBalance, Component)
|
||||||
function TokenBalance () {
|
function TokenBalance () {
|
||||||
this.state = {
|
this.state = {
|
||||||
balance: '',
|
string: '',
|
||||||
|
symbol: '',
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
error: null,
|
error: null,
|
||||||
}
|
}
|
||||||
@ -26,11 +27,14 @@ function TokenBalance () {
|
|||||||
|
|
||||||
TokenBalance.prototype.render = function () {
|
TokenBalance.prototype.render = function () {
|
||||||
const state = this.state
|
const state = this.state
|
||||||
const { balance, isLoading } = state
|
const { symbol, string, balanceOnly, isLoading } = state
|
||||||
|
|
||||||
return isLoading
|
return isLoading
|
||||||
? h('span', '')
|
? h('span', '')
|
||||||
: h('span', balance)
|
: h('span.token-balance', [
|
||||||
|
h('span.token-balance__amount', string),
|
||||||
|
!balanceOnly && h('span.token-balance__symbol', symbol),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenBalance.prototype.componentDidMount = function () {
|
TokenBalance.prototype.componentDidMount = function () {
|
||||||
@ -93,10 +97,10 @@ TokenBalance.prototype.componentDidUpdate = function (nextProps) {
|
|||||||
|
|
||||||
TokenBalance.prototype.updateBalance = function (tokens = []) {
|
TokenBalance.prototype.updateBalance = function (tokens = []) {
|
||||||
const [{ string, symbol }] = tokens
|
const [{ string, symbol }] = tokens
|
||||||
const { balanceOnly } = this.props
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
balance: balanceOnly ? string : `${string} ${symbol}`,
|
string,
|
||||||
|
symbol,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
|
|||||||
const { showConfTxPage } = this.props
|
const { showConfTxPage } = this.props
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
key: transActionId,
|
key: transActionId || transactionHash,
|
||||||
txParams: transaction.txParams,
|
txParams: transaction.txParams,
|
||||||
transactionStatus,
|
transactionStatus,
|
||||||
transActionId,
|
transActionId,
|
||||||
|
@ -56,6 +56,10 @@
|
|||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__confirmation-description {
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
&__content-container {
|
&__content-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-bottom: 1px solid $gallery;
|
border-bottom: 1px solid $gallery;
|
||||||
@ -65,6 +69,18 @@
|
|||||||
padding: 11px 0;
|
padding: 11px 0;
|
||||||
width: 263px;
|
width: 263px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__search-input-error-message {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -10px;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 100%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: $red;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__input {
|
&__input {
|
||||||
@ -89,9 +105,13 @@
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 11px 0 19px;
|
padding: 12px 0;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $gallery;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__add-custom-form {
|
&__add-custom-form {
|
||||||
@ -103,6 +123,24 @@
|
|||||||
&__add-custom-field {
|
&__add-custom-field {
|
||||||
width: 290px;
|
width: 290px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
.add-token__add-custom-input {
|
||||||
|
border-color: $red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__add-custom-error-message {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -21px;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 100%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: $red;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__add-custom-label {
|
&__add-custom-label {
|
||||||
@ -152,9 +190,12 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
|
|
||||||
&:hover,
|
&:hover {
|
||||||
|
border: 2px solid rgba($malibu-blue, .5);
|
||||||
|
}
|
||||||
|
|
||||||
&--selected {
|
&--selected {
|
||||||
border: 2px solid $malibu-blue;
|
border: 2px solid $malibu-blue !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,4 +222,52 @@
|
|||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__confirmation-token-list {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
|
||||||
|
.token-balance {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
&__amount {
|
||||||
|
color: $scorpion;
|
||||||
|
font-size: 43px;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 43px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__symbol {
|
||||||
|
color: $scorpion;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__confirmation-title {
|
||||||
|
padding: 30px 120px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__confirmation-content {
|
||||||
|
padding-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__confirmation-token-list-item {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
padding: 0 120px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__confirmation-token-list-item + &__confirmation-token-list-item {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__confirmation-token-icon {
|
||||||
|
margin-right: 18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,9 @@ button.btn-clear {
|
|||||||
button[disabled],
|
button[disabled],
|
||||||
input[type="submit"][disabled] {
|
input[type="submit"][disabled] {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
background: rgba(197, 197, 197, 1);
|
opacity: .5;
|
||||||
box-shadow: 0 3px 6px rgba(197, 197, 197, .36);
|
// background: rgba(197, 197, 197, 1);
|
||||||
|
// box-shadow: 0 3px 6px rgba(197, 197, 197, .36);
|
||||||
}
|
}
|
||||||
|
|
||||||
// button.spaced {
|
// button.spaced {
|
||||||
@ -90,6 +91,11 @@ button.btn-thin {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
padding: 16px 42px;
|
padding: 16px 42px;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
background-color: $white !important;
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-tertiary {
|
.btn-tertiary {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user