6th iteration: remove unused components
|
@ -1,188 +0,0 @@
|
||||||
const Component = require('react').Component
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const ethUtil = require('ethereumjs-util')
|
|
||||||
const BN = ethUtil.BN
|
|
||||||
const extend = require('xtend')
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
|
|
||||||
BnAsDecimalInput.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect()(BnAsDecimalInput)
|
|
||||||
|
|
||||||
|
|
||||||
inherits(BnAsDecimalInput, Component)
|
|
||||||
function BnAsDecimalInput () {
|
|
||||||
this.state = { invalid: null }
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bn as Decimal Input
|
|
||||||
*
|
|
||||||
* A component for allowing easy, decimal editing
|
|
||||||
* of a passed in bn string value.
|
|
||||||
*
|
|
||||||
* On change, calls back its `onChange` function parameter
|
|
||||||
* and passes it an updated bn string.
|
|
||||||
*/
|
|
||||||
|
|
||||||
BnAsDecimalInput.prototype.render = function () {
|
|
||||||
const props = this.props
|
|
||||||
const state = this.state
|
|
||||||
|
|
||||||
const { value, scale, precision, onChange, min, max } = props
|
|
||||||
|
|
||||||
const suffix = props.suffix
|
|
||||||
const style = props.style
|
|
||||||
const valueString = value.toString(10)
|
|
||||||
const newMin = min && this.downsize(min.toString(10), scale)
|
|
||||||
const newMax = max && this.downsize(max.toString(10), scale)
|
|
||||||
const newValue = this.downsize(valueString, scale)
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('.flex-column', [
|
|
||||||
h('.flex-row', {
|
|
||||||
style: {
|
|
||||||
alignItems: 'flex-end',
|
|
||||||
lineHeight: '13px',
|
|
||||||
fontFamily: 'Montserrat Light',
|
|
||||||
textRendering: 'geometricPrecision',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('input.hex-input', {
|
|
||||||
type: 'number',
|
|
||||||
step: 'any',
|
|
||||||
required: true,
|
|
||||||
min: newMin,
|
|
||||||
max: newMax,
|
|
||||||
style: extend({
|
|
||||||
display: 'block',
|
|
||||||
textAlign: 'right',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
border: '1px solid #bdbdbd',
|
|
||||||
|
|
||||||
}, style),
|
|
||||||
value: newValue,
|
|
||||||
onBlur: (event) => {
|
|
||||||
this.updateValidity(event)
|
|
||||||
},
|
|
||||||
onChange: (event) => {
|
|
||||||
this.updateValidity(event)
|
|
||||||
const value = (event.target.value === '') ? '' : event.target.value
|
|
||||||
|
|
||||||
|
|
||||||
const scaledNumber = this.upsize(value, scale, precision)
|
|
||||||
const precisionBN = new BN(scaledNumber, 10)
|
|
||||||
onChange(precisionBN, event.target.checkValidity())
|
|
||||||
},
|
|
||||||
onInvalid: (event) => {
|
|
||||||
const msg = this.constructWarning()
|
|
||||||
if (msg === state.invalid) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.setState({ invalid: msg })
|
|
||||||
event.preventDefault()
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
color: ' #AEAEAE',
|
|
||||||
fontSize: '12px',
|
|
||||||
marginLeft: '5px',
|
|
||||||
marginRight: '6px',
|
|
||||||
width: '20px',
|
|
||||||
},
|
|
||||||
}, suffix),
|
|
||||||
]),
|
|
||||||
|
|
||||||
state.invalid ? h('span.error', {
|
|
||||||
style: {
|
|
||||||
position: 'absolute',
|
|
||||||
right: '0px',
|
|
||||||
textAlign: 'right',
|
|
||||||
transform: 'translateY(26px)',
|
|
||||||
padding: '3px',
|
|
||||||
background: 'rgba(255,255,255,0.85)',
|
|
||||||
zIndex: '1',
|
|
||||||
textTransform: 'capitalize',
|
|
||||||
border: '2px solid #E20202',
|
|
||||||
},
|
|
||||||
}, state.invalid) : null,
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BnAsDecimalInput.prototype.setValid = function (message) {
|
|
||||||
this.setState({ invalid: null })
|
|
||||||
}
|
|
||||||
|
|
||||||
BnAsDecimalInput.prototype.updateValidity = function (event) {
|
|
||||||
const target = event.target
|
|
||||||
const value = this.props.value
|
|
||||||
const newValue = target.value
|
|
||||||
|
|
||||||
if (value === newValue) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const valid = target.checkValidity()
|
|
||||||
|
|
||||||
if (valid) {
|
|
||||||
this.setState({ invalid: null })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BnAsDecimalInput.prototype.constructWarning = function () {
|
|
||||||
const { name, min, max, scale, suffix } = this.props
|
|
||||||
const newMin = min && this.downsize(min.toString(10), scale)
|
|
||||||
const newMax = max && this.downsize(max.toString(10), scale)
|
|
||||||
let message = name ? name + ' ' : ''
|
|
||||||
|
|
||||||
if (min && max) {
|
|
||||||
message += this.context.t('betweenMinAndMax', [`${newMin} ${suffix}`, `${newMax} ${suffix}`])
|
|
||||||
} else if (min) {
|
|
||||||
message += this.context.t('greaterThanMin', [`${newMin} ${suffix}`])
|
|
||||||
} else if (max) {
|
|
||||||
message += this.context.t('lessThanMax', [`${newMax} ${suffix}`])
|
|
||||||
} else {
|
|
||||||
message += this.context.t('invalidInput')
|
|
||||||
}
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BnAsDecimalInput.prototype.downsize = function (number, scale) {
|
|
||||||
// if there is no scaling, simply return the number
|
|
||||||
if (scale === 0) {
|
|
||||||
return Number(number)
|
|
||||||
} else {
|
|
||||||
// if the scale is the same as the precision, account for this edge case.
|
|
||||||
var adjustedNumber = number
|
|
||||||
while (adjustedNumber.length < scale) {
|
|
||||||
adjustedNumber = '0' + adjustedNumber
|
|
||||||
}
|
|
||||||
return Number(adjustedNumber.slice(0, -scale) + '.' + adjustedNumber.slice(-scale))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BnAsDecimalInput.prototype.upsize = function (number, scale, precision) {
|
|
||||||
var stringArray = number.toString().split('.')
|
|
||||||
var decimalLength = stringArray[1] ? stringArray[1].length : 0
|
|
||||||
var newString = stringArray[0]
|
|
||||||
|
|
||||||
// If there is scaling and decimal parts exist, integrate them in.
|
|
||||||
if ((scale !== 0) && (decimalLength !== 0)) {
|
|
||||||
newString += stringArray[1].slice(0, precision)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 0s to account for the upscaling.
|
|
||||||
for (var i = decimalLength; i < scale; i++) {
|
|
||||||
newString += '0'
|
|
||||||
}
|
|
||||||
return newString
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
const Component = require('react').Component
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const actions = require('../actions')
|
|
||||||
|
|
||||||
CoinbaseForm.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(CoinbaseForm)
|
|
||||||
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
return {
|
|
||||||
warning: state.appState.warning,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(CoinbaseForm, Component)
|
|
||||||
|
|
||||||
function CoinbaseForm () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
CoinbaseForm.prototype.render = function () {
|
|
||||||
var props = this.props
|
|
||||||
|
|
||||||
return h('.flex-column', {
|
|
||||||
style: {
|
|
||||||
marginTop: '35px',
|
|
||||||
padding: '25px',
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('.flex-row', {
|
|
||||||
style: {
|
|
||||||
justifyContent: 'space-around',
|
|
||||||
margin: '33px',
|
|
||||||
marginTop: '0px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('button.btn-green', {
|
|
||||||
onClick: this.toCoinbase.bind(this),
|
|
||||||
}, this.context.t('continueToCoinbase')),
|
|
||||||
|
|
||||||
h('button.btn-red', {
|
|
||||||
onClick: () => props.dispatch(actions.goHome()),
|
|
||||||
}, this.context.t('cancel')),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
CoinbaseForm.prototype.toCoinbase = function () {
|
|
||||||
const props = this.props
|
|
||||||
const address = props.buyView.buyAddress
|
|
||||||
props.dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
|
|
||||||
}
|
|
||||||
|
|
||||||
CoinbaseForm.prototype.renderLoading = function () {
|
|
||||||
return h('img', {
|
|
||||||
style: {
|
|
||||||
width: '27px',
|
|
||||||
marginRight: '-27px',
|
|
||||||
},
|
|
||||||
src: 'images/loading.svg',
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
const { Component } = require('react')
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const classnames = require('classnames')
|
|
||||||
|
|
||||||
class EditableLabel extends Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isEditing: false,
|
|
||||||
value: props.defaultValue || '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit () {
|
|
||||||
const { value } = this.state
|
|
||||||
|
|
||||||
if (value === '') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.resolve(this.props.onSubmit(value))
|
|
||||||
.then(() => this.setState({ isEditing: false }))
|
|
||||||
}
|
|
||||||
|
|
||||||
saveIfEnter (event) {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
this.handleSubmit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderEditing () {
|
|
||||||
const { value } = this.state
|
|
||||||
|
|
||||||
return ([
|
|
||||||
h('input.large-input.editable-label__input', {
|
|
||||||
type: 'text',
|
|
||||||
required: true,
|
|
||||||
value: this.state.value,
|
|
||||||
onKeyPress: (event) => {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
this.handleSubmit()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onChange: event => this.setState({ value: event.target.value }),
|
|
||||||
className: classnames({ 'editable-label__input--error': value === '' }),
|
|
||||||
}),
|
|
||||||
h('div.editable-label__icon-wrapper', [
|
|
||||||
h('i.fa.fa-check.editable-label__icon', {
|
|
||||||
onClick: () => this.handleSubmit(),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
renderReadonly () {
|
|
||||||
return ([
|
|
||||||
h('div.editable-label__value', this.state.value),
|
|
||||||
h('div.editable-label__icon-wrapper', [
|
|
||||||
h('i.fa.fa-pencil.editable-label__icon', {
|
|
||||||
onClick: () => this.setState({ isEditing: true }),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { isEditing } = this.state
|
|
||||||
const { className } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('div.editable-label', { className: classnames(className) },
|
|
||||||
isEditing
|
|
||||||
? this.renderEditing()
|
|
||||||
: this.renderReadonly()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditableLabel.propTypes = {
|
|
||||||
onSubmit: PropTypes.func.isRequired,
|
|
||||||
defaultValue: PropTypes.string,
|
|
||||||
className: PropTypes.string,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = EditableLabel
|
|
|
@ -1,181 +0,0 @@
|
||||||
const Component = require('react').Component
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const extend = require('xtend')
|
|
||||||
const debounce = require('debounce')
|
|
||||||
const copyToClipboard = require('copy-to-clipboard')
|
|
||||||
const ENS = require('ethjs-ens')
|
|
||||||
const networkMap = require('ethjs-ens/lib/network-map.json')
|
|
||||||
const ensRE = /.+\..+$/
|
|
||||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const ToAutoComplete = require('./send/to-autocomplete').default
|
|
||||||
const log = require('loglevel')
|
|
||||||
const { isValidENSAddress } = require('../util')
|
|
||||||
|
|
||||||
EnsInput.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect()(EnsInput)
|
|
||||||
|
|
||||||
|
|
||||||
inherits(EnsInput, Component)
|
|
||||||
function EnsInput () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsInput.prototype.onChange = function (recipient) {
|
|
||||||
|
|
||||||
const network = this.props.network
|
|
||||||
const networkHasEnsSupport = getNetworkEnsSupport(network)
|
|
||||||
|
|
||||||
this.props.onChange({ toAddress: recipient })
|
|
||||||
|
|
||||||
if (!networkHasEnsSupport) return
|
|
||||||
|
|
||||||
if (recipient.match(ensRE) === null) {
|
|
||||||
return this.setState({
|
|
||||||
loadingEns: false,
|
|
||||||
ensResolution: null,
|
|
||||||
ensFailure: null,
|
|
||||||
toError: null,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
loadingEns: true,
|
|
||||||
})
|
|
||||||
this.checkName(recipient)
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsInput.prototype.render = function () {
|
|
||||||
const props = this.props
|
|
||||||
const opts = extend(props, {
|
|
||||||
list: 'addresses',
|
|
||||||
onChange: this.onChange.bind(this),
|
|
||||||
qrScanner: true,
|
|
||||||
})
|
|
||||||
return h('div', {
|
|
||||||
style: { width: '100%', position: 'relative' },
|
|
||||||
}, [
|
|
||||||
h(ToAutoComplete, { ...opts }),
|
|
||||||
this.ensIcon(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsInput.prototype.componentDidMount = function () {
|
|
||||||
const network = this.props.network
|
|
||||||
const networkHasEnsSupport = getNetworkEnsSupport(network)
|
|
||||||
this.setState({ ensResolution: ZERO_ADDRESS })
|
|
||||||
|
|
||||||
if (networkHasEnsSupport) {
|
|
||||||
const provider = global.ethereumProvider
|
|
||||||
this.ens = new ENS({ provider, network })
|
|
||||||
this.checkName = debounce(this.lookupEnsName.bind(this), 200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsInput.prototype.lookupEnsName = function (recipient) {
|
|
||||||
const { ensResolution } = this.state
|
|
||||||
|
|
||||||
log.info(`ENS attempting to resolve name: ${recipient}`)
|
|
||||||
this.ens.lookup(recipient.trim())
|
|
||||||
.then((address) => {
|
|
||||||
if (address === ZERO_ADDRESS) throw new Error(this.context.t('noAddressForName'))
|
|
||||||
if (address !== ensResolution) {
|
|
||||||
this.setState({
|
|
||||||
loadingEns: false,
|
|
||||||
ensResolution: address,
|
|
||||||
nickname: recipient.trim(),
|
|
||||||
hoverText: address + '\n' + this.context.t('clickCopy'),
|
|
||||||
ensFailure: false,
|
|
||||||
toError: null,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((reason) => {
|
|
||||||
const setStateObj = {
|
|
||||||
loadingEns: false,
|
|
||||||
ensResolution: recipient,
|
|
||||||
ensFailure: true,
|
|
||||||
toError: null,
|
|
||||||
}
|
|
||||||
if (isValidENSAddress(recipient) && reason.message === 'ENS name not defined.') {
|
|
||||||
setStateObj.hoverText = this.context.t('ensNameNotFound')
|
|
||||||
setStateObj.toError = 'ensNameNotFound'
|
|
||||||
setStateObj.ensFailure = false
|
|
||||||
} else {
|
|
||||||
log.error(reason)
|
|
||||||
setStateObj.hoverText = reason.message
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.setState(setStateObj)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
|
|
||||||
const state = this.state || {}
|
|
||||||
const ensResolution = state.ensResolution
|
|
||||||
// If an address is sent without a nickname, meaning not from ENS or from
|
|
||||||
// the user's own accounts, a default of a one-space string is used.
|
|
||||||
const nickname = state.nickname || ' '
|
|
||||||
if (prevProps.network !== this.props.network) {
|
|
||||||
const provider = global.ethereumProvider
|
|
||||||
this.ens = new ENS({ provider, network: this.props.network })
|
|
||||||
this.onChange(ensResolution)
|
|
||||||
}
|
|
||||||
if (prevState && ensResolution && this.props.onChange &&
|
|
||||||
ensResolution !== prevState.ensResolution) {
|
|
||||||
this.props.onChange({ toAddress: ensResolution, nickname, toError: state.toError })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsInput.prototype.ensIcon = function (recipient) {
|
|
||||||
const { hoverText } = this.state || {}
|
|
||||||
return h('span.#ensIcon', {
|
|
||||||
title: hoverText,
|
|
||||||
style: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: '16px',
|
|
||||||
left: '-25px',
|
|
||||||
},
|
|
||||||
}, this.ensIconContents(recipient))
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsInput.prototype.ensIconContents = function (recipient) {
|
|
||||||
const { loadingEns, ensFailure, ensResolution, toError } = this.state || { ensResolution: ZERO_ADDRESS }
|
|
||||||
|
|
||||||
if (toError) return
|
|
||||||
|
|
||||||
if (loadingEns) {
|
|
||||||
return h('img', {
|
|
||||||
src: 'images/loading.svg',
|
|
||||||
style: {
|
|
||||||
width: '30px',
|
|
||||||
height: '30px',
|
|
||||||
transform: 'translateY(-6px)',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ensFailure) {
|
|
||||||
return h('i.fa.fa-warning.fa-lg.warning')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ensResolution && (ensResolution !== ZERO_ADDRESS)) {
|
|
||||||
return h('i.fa.fa-check-circle.fa-lg.cursor-pointer', {
|
|
||||||
style: { color: 'green' },
|
|
||||||
onClick: (event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
copyToClipboard(ensResolution)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNetworkEnsSupport (network) {
|
|
||||||
return Boolean(networkMap[network])
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
const { Component } = require('react')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const { inherits } = require('util')
|
|
||||||
const {
|
|
||||||
formatBalance,
|
|
||||||
generateBalanceObject,
|
|
||||||
} = require('../util')
|
|
||||||
const Tooltip = require('./tooltip.js')
|
|
||||||
const FiatValue = require('./fiat-value.js')
|
|
||||||
|
|
||||||
module.exports = EthBalanceComponent
|
|
||||||
|
|
||||||
inherits(EthBalanceComponent, Component)
|
|
||||||
function EthBalanceComponent () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
EthBalanceComponent.prototype.render = function () {
|
|
||||||
const props = this.props
|
|
||||||
const { value, style, width, needsParse = true } = props
|
|
||||||
|
|
||||||
const formattedValue = value ? formatBalance(value, 6, needsParse) : '...'
|
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
h('.ether-balance.ether-balance-amount', {
|
|
||||||
style,
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
display: 'inline',
|
|
||||||
width,
|
|
||||||
},
|
|
||||||
}, this.renderBalance(formattedValue)),
|
|
||||||
])
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
EthBalanceComponent.prototype.renderBalance = function (value) {
|
|
||||||
if (value === 'None') return value
|
|
||||||
if (value === '...') return value
|
|
||||||
|
|
||||||
const {
|
|
||||||
conversionRate,
|
|
||||||
shorten,
|
|
||||||
incoming,
|
|
||||||
currentCurrency,
|
|
||||||
hideTooltip,
|
|
||||||
styleOveride = {},
|
|
||||||
showFiat = true,
|
|
||||||
} = this.props
|
|
||||||
const { fontSize, color, fontFamily, lineHeight } = styleOveride
|
|
||||||
|
|
||||||
const { shortBalance, balance, label } = generateBalanceObject(value, shorten ? 1 : 3)
|
|
||||||
const balanceToRender = shorten ? shortBalance : balance
|
|
||||||
|
|
||||||
const [ethNumber, ethSuffix] = value.split(' ')
|
|
||||||
const containerProps = hideTooltip ? {} : {
|
|
||||||
position: 'bottom',
|
|
||||||
title: `${ethNumber} ${ethSuffix}`,
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
h(hideTooltip ? 'div' : Tooltip,
|
|
||||||
containerProps,
|
|
||||||
h('div.flex-column', [
|
|
||||||
h('.flex-row', {
|
|
||||||
style: {
|
|
||||||
alignItems: 'flex-end',
|
|
||||||
lineHeight: lineHeight || '13px',
|
|
||||||
fontFamily: fontFamily || 'Montserrat Light',
|
|
||||||
textRendering: 'geometricPrecision',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
width: '100%',
|
|
||||||
textAlign: 'right',
|
|
||||||
fontSize: fontSize || 'inherit',
|
|
||||||
color: color || 'inherit',
|
|
||||||
},
|
|
||||||
}, incoming ? `+${balanceToRender}` : balanceToRender),
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
color: color || '#AEAEAE',
|
|
||||||
fontSize: fontSize || '12px',
|
|
||||||
marginLeft: '5px',
|
|
||||||
},
|
|
||||||
}, label),
|
|
||||||
]),
|
|
||||||
|
|
||||||
showFiat ? h(FiatValue, { value: this.props.value, conversionRate, currentCurrency }) : null,
|
|
||||||
])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
const { Component } = require('react')
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const copyToClipboard = require('copy-to-clipboard')
|
|
||||||
const { exportAsFile } = require('../../util')
|
|
||||||
|
|
||||||
class ExportTextContainer extends Component {
|
|
||||||
render () {
|
|
||||||
const { text = '', filename = '' } = this.props
|
|
||||||
const { t } = this.context
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('.export-text-container', [
|
|
||||||
h('.export-text-container__text-container', [
|
|
||||||
h('.export-text-container__text', text),
|
|
||||||
]),
|
|
||||||
h('.export-text-container__buttons-container', [
|
|
||||||
h('.export-text-container__button.export-text-container__button--copy', {
|
|
||||||
onClick: () => copyToClipboard(text),
|
|
||||||
}, [
|
|
||||||
h('img', { src: 'images/copy-to-clipboard.svg' }),
|
|
||||||
h('.export-text-container__button-text', t('copyToClipboard')),
|
|
||||||
]),
|
|
||||||
h('.export-text-container__button', {
|
|
||||||
onClick: () => exportAsFile(filename, text),
|
|
||||||
}, [
|
|
||||||
h('img', { src: 'images/download.svg' }),
|
|
||||||
h('.export-text-container__button-text', t('saveAsCsvFile')),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExportTextContainer.propTypes = {
|
|
||||||
text: PropTypes.string,
|
|
||||||
filename: PropTypes.string,
|
|
||||||
}
|
|
||||||
|
|
||||||
ExportTextContainer.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ExportTextContainer
|
|
|
@ -1,2 +0,0 @@
|
||||||
const ExportTextContainer = require('./export-text-container.component')
|
|
||||||
module.exports = ExportTextContainer
|
|
|
@ -1,52 +0,0 @@
|
||||||
.export-text-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
border: 1px solid $alto;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
&__text-container {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: $alabaster;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
resize: none;
|
|
||||||
border: none;
|
|
||||||
background: $alabaster;
|
|
||||||
font-size: 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__buttons-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
border-top: 1px solid $alto;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__button {
|
|
||||||
padding: 10px;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: $curious-blue;
|
|
||||||
|
|
||||||
&--copy {
|
|
||||||
border-right: 1px solid $alto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__button-text {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
const Component = require('react').Component
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const formatBalance = require('../util').formatBalance
|
|
||||||
|
|
||||||
module.exports = FiatValue
|
|
||||||
|
|
||||||
inherits(FiatValue, Component)
|
|
||||||
function FiatValue () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
FiatValue.prototype.render = function () {
|
|
||||||
const props = this.props
|
|
||||||
const { conversionRate, currentCurrency, style } = props
|
|
||||||
const renderedCurrency = currentCurrency || ''
|
|
||||||
|
|
||||||
const value = formatBalance(props.value, 6)
|
|
||||||
|
|
||||||
if (value === 'None') return value
|
|
||||||
var fiatDisplayNumber, fiatTooltipNumber
|
|
||||||
var splitBalance = value.split(' ')
|
|
||||||
|
|
||||||
if (conversionRate !== 0) {
|
|
||||||
fiatTooltipNumber = Number(splitBalance[0]) * conversionRate
|
|
||||||
fiatDisplayNumber = fiatTooltipNumber.toFixed(2)
|
|
||||||
} else {
|
|
||||||
fiatDisplayNumber = 'N/A'
|
|
||||||
fiatTooltipNumber = 'Unknown'
|
|
||||||
}
|
|
||||||
|
|
||||||
return fiatDisplay(fiatDisplayNumber, renderedCurrency.toUpperCase(), style)
|
|
||||||
}
|
|
||||||
|
|
||||||
function fiatDisplay (fiatDisplayNumber, fiatSuffix, styleOveride = {}) {
|
|
||||||
const { fontSize, color, fontFamily, lineHeight } = styleOveride
|
|
||||||
|
|
||||||
if (fiatDisplayNumber !== 'N/A') {
|
|
||||||
return h('.flex-row', {
|
|
||||||
style: {
|
|
||||||
alignItems: 'flex-end',
|
|
||||||
lineHeight: lineHeight || '13px',
|
|
||||||
fontFamily: fontFamily || 'Montserrat Light',
|
|
||||||
textRendering: 'geometricPrecision',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
width: '100%',
|
|
||||||
textAlign: 'right',
|
|
||||||
fontSize: fontSize || '12px',
|
|
||||||
color: color || '#333333',
|
|
||||||
},
|
|
||||||
}, fiatDisplayNumber),
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
color: color || '#AEAEAE',
|
|
||||||
marginLeft: '5px',
|
|
||||||
fontSize: fontSize || '12px',
|
|
||||||
},
|
|
||||||
}, fiatSuffix),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
return h('div')
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
import InfoBox from './info-box.component'
|
|
||||||
module.exports = InfoBox
|
|
|
@ -1,24 +0,0 @@
|
||||||
.info-box {
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: $alabaster;
|
|
||||||
position: relative;
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
color: $mid-gray;
|
|
||||||
|
|
||||||
&__close::after {
|
|
||||||
content: '\00D7';
|
|
||||||
font-size: 29px;
|
|
||||||
font-weight: 200;
|
|
||||||
color: $dusty-gray;
|
|
||||||
position: absolute;
|
|
||||||
right: 12px;
|
|
||||||
top: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__description {
|
|
||||||
font-size: .75rem;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
import React, { Component } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
export default class InfoBox extends Component {
|
|
||||||
static contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
onClose: PropTypes.func,
|
|
||||||
title: PropTypes.string,
|
|
||||||
description: PropTypes.string,
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isShowing: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClose () {
|
|
||||||
const { onClose } = this.props
|
|
||||||
|
|
||||||
if (onClose) {
|
|
||||||
onClose()
|
|
||||||
} else {
|
|
||||||
this.setState({ isShowing: false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { title, description } = this.props
|
|
||||||
|
|
||||||
return !this.state.isShowing
|
|
||||||
? null
|
|
||||||
: (
|
|
||||||
<div className="info-box">
|
|
||||||
<div
|
|
||||||
className="info-box__close"
|
|
||||||
onClick={() => this.handleClose()}
|
|
||||||
/>
|
|
||||||
<div className="info-box__title">{ title }</div>
|
|
||||||
<div className="info-box__description">{ description }</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
const Component = require('react').Component
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const findDOMNode = require('react-dom').findDOMNode
|
|
||||||
import { CSSTransitionGroup } from 'react-transition-group'
|
|
||||||
|
|
||||||
module.exports = MenuDroppoComponent
|
|
||||||
|
|
||||||
|
|
||||||
inherits(MenuDroppoComponent, Component)
|
|
||||||
function MenuDroppoComponent () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuDroppoComponent.prototype.render = function () {
|
|
||||||
const { containerClassName = '' } = this.props
|
|
||||||
const speed = this.props.speed || '300ms'
|
|
||||||
const useCssTransition = this.props.useCssTransition
|
|
||||||
const zIndex = ('zIndex' in this.props) ? this.props.zIndex : 0
|
|
||||||
|
|
||||||
this.manageListeners()
|
|
||||||
|
|
||||||
const style = this.props.style || {}
|
|
||||||
if (!('position' in style)) {
|
|
||||||
style.position = 'fixed'
|
|
||||||
}
|
|
||||||
style.zIndex = zIndex
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('div', {
|
|
||||||
style,
|
|
||||||
className: `.menu-droppo-container ${containerClassName}`,
|
|
||||||
}, [
|
|
||||||
h('style', `
|
|
||||||
.menu-droppo-enter {
|
|
||||||
transition: transform ${speed} ease-in-out;
|
|
||||||
transform: translateY(-200%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-droppo-enter.menu-droppo-enter-active {
|
|
||||||
transition: transform ${speed} ease-in-out;
|
|
||||||
transform: translateY(0%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-droppo-leave {
|
|
||||||
transition: transform ${speed} ease-in-out;
|
|
||||||
transform: translateY(0%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-droppo-leave.menu-droppo-leave-active {
|
|
||||||
transition: transform ${speed} ease-in-out;
|
|
||||||
transform: translateY(-200%);
|
|
||||||
}
|
|
||||||
`),
|
|
||||||
|
|
||||||
useCssTransition
|
|
||||||
? h(CSSTransitionGroup, {
|
|
||||||
className: 'css-transition-group',
|
|
||||||
transitionName: 'menu-droppo',
|
|
||||||
transitionEnterTimeout: parseInt(speed),
|
|
||||||
transitionLeaveTimeout: parseInt(speed),
|
|
||||||
}, this.renderPrimary())
|
|
||||||
: this.renderPrimary(),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuDroppoComponent.prototype.renderPrimary = function () {
|
|
||||||
const isOpen = this.props.isOpen
|
|
||||||
if (!isOpen) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const innerStyle = this.props.innerStyle || {}
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('.menu-droppo', {
|
|
||||||
key: 'menu-droppo-drawer',
|
|
||||||
style: innerStyle,
|
|
||||||
},
|
|
||||||
[ this.props.children ])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuDroppoComponent.prototype.manageListeners = function () {
|
|
||||||
const isOpen = this.props.isOpen
|
|
||||||
const onClickOutside = this.props.onClickOutside
|
|
||||||
|
|
||||||
if (isOpen) {
|
|
||||||
this.outsideClickHandler = onClickOutside
|
|
||||||
} else if (isOpen) {
|
|
||||||
this.outsideClickHandler = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuDroppoComponent.prototype.componentDidMount = function () {
|
|
||||||
if (this && document.body) {
|
|
||||||
this.globalClickHandler = this.globalClickOccurred.bind(this)
|
|
||||||
document.body.addEventListener('click', this.globalClickHandler)
|
|
||||||
// eslint-disable-next-line react/no-find-dom-node
|
|
||||||
var container = findDOMNode(this)
|
|
||||||
this.container = container
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuDroppoComponent.prototype.componentWillUnmount = function () {
|
|
||||||
if (this && document.body) {
|
|
||||||
document.body.removeEventListener('click', this.globalClickHandler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuDroppoComponent.prototype.globalClickOccurred = function (event) {
|
|
||||||
const target = event.target
|
|
||||||
// eslint-disable-next-line react/no-find-dom-node
|
|
||||||
const container = findDOMNode(this)
|
|
||||||
|
|
||||||
if (target !== container &&
|
|
||||||
!isDescendant(this.container, event.target) &&
|
|
||||||
this.outsideClickHandler) {
|
|
||||||
this.outsideClickHandler(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDescendant (parent, child) {
|
|
||||||
var node = child.parentNode
|
|
||||||
while (node !== null) {
|
|
||||||
if (node === parent) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
node = node.parentNode
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,220 +0,0 @@
|
||||||
const Component = require('react').Component
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const actions = require('../../actions')
|
|
||||||
const { getNetworkDisplayName } = require('../../../../app/scripts/controllers/network/util')
|
|
||||||
const ShapeshiftForm = require('../shapeshift-form')
|
|
||||||
|
|
||||||
import Button from '../button'
|
|
||||||
|
|
||||||
let DIRECT_DEPOSIT_ROW_TITLE
|
|
||||||
let DIRECT_DEPOSIT_ROW_TEXT
|
|
||||||
let COINBASE_ROW_TITLE
|
|
||||||
let COINBASE_ROW_TEXT
|
|
||||||
let SHAPESHIFT_ROW_TITLE
|
|
||||||
let SHAPESHIFT_ROW_TEXT
|
|
||||||
let FAUCET_ROW_TITLE
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
return {
|
|
||||||
network: state.metamask.network,
|
|
||||||
address: state.metamask.selectedAddress,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
|
||||||
return {
|
|
||||||
toCoinbase: (address) => {
|
|
||||||
dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
|
|
||||||
},
|
|
||||||
hideModal: () => {
|
|
||||||
dispatch(actions.hideModal())
|
|
||||||
},
|
|
||||||
hideWarning: () => {
|
|
||||||
dispatch(actions.hideWarning())
|
|
||||||
},
|
|
||||||
showAccountDetailModal: () => {
|
|
||||||
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
|
|
||||||
},
|
|
||||||
toFaucet: network => dispatch(actions.buyEth({ network })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(DepositEtherModal, Component)
|
|
||||||
function DepositEtherModal (props, context) {
|
|
||||||
Component.call(this)
|
|
||||||
|
|
||||||
// need to set after i18n locale has loaded
|
|
||||||
DIRECT_DEPOSIT_ROW_TITLE = context.t('directDepositEther')
|
|
||||||
DIRECT_DEPOSIT_ROW_TEXT = context.t('directDepositEtherExplainer')
|
|
||||||
COINBASE_ROW_TITLE = context.t('buyCoinbase')
|
|
||||||
COINBASE_ROW_TEXT = context.t('buyCoinbaseExplainer')
|
|
||||||
SHAPESHIFT_ROW_TITLE = context.t('depositShapeShift')
|
|
||||||
SHAPESHIFT_ROW_TEXT = context.t('depositShapeShiftExplainer')
|
|
||||||
FAUCET_ROW_TITLE = context.t('testFaucet')
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
buyingWithShapeshift: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DepositEtherModal.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(DepositEtherModal)
|
|
||||||
|
|
||||||
|
|
||||||
DepositEtherModal.prototype.facuetRowText = function (networkName) {
|
|
||||||
return this.context.t('getEtherFromFaucet', [networkName])
|
|
||||||
}
|
|
||||||
|
|
||||||
DepositEtherModal.prototype.renderRow = function ({
|
|
||||||
logo,
|
|
||||||
title,
|
|
||||||
text,
|
|
||||||
buttonLabel,
|
|
||||||
onButtonClick,
|
|
||||||
hide,
|
|
||||||
className,
|
|
||||||
hideButton,
|
|
||||||
hideTitle,
|
|
||||||
onBackClick,
|
|
||||||
showBackButton,
|
|
||||||
}) {
|
|
||||||
if (hide) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return h('div', {
|
|
||||||
className: className || 'deposit-ether-modal__buy-row',
|
|
||||||
}, [
|
|
||||||
|
|
||||||
onBackClick && showBackButton && h('div.deposit-ether-modal__buy-row__back', {
|
|
||||||
onClick: onBackClick,
|
|
||||||
}, [
|
|
||||||
|
|
||||||
h('i.fa.fa-arrow-left.cursor-pointer'),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('div.deposit-ether-modal__buy-row__logo-container', [logo]),
|
|
||||||
|
|
||||||
h('div.deposit-ether-modal__buy-row__description', [
|
|
||||||
|
|
||||||
!hideTitle && h('div.deposit-ether-modal__buy-row__description__title', [title]),
|
|
||||||
|
|
||||||
h('div.deposit-ether-modal__buy-row__description__text', [text]),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
!hideButton && h('div.deposit-ether-modal__buy-row__button', [
|
|
||||||
h(Button, {
|
|
||||||
type: 'primary',
|
|
||||||
className: 'deposit-ether-modal__deposit-button',
|
|
||||||
large: true,
|
|
||||||
onClick: onButtonClick,
|
|
||||||
}, [buttonLabel]),
|
|
||||||
]),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
DepositEtherModal.prototype.render = function () {
|
|
||||||
const { network, toCoinbase, address, toFaucet } = this.props
|
|
||||||
const { buyingWithShapeshift } = this.state
|
|
||||||
|
|
||||||
const isTestNetwork = ['3', '4', '42'].find(n => n === network)
|
|
||||||
const networkName = getNetworkDisplayName(network)
|
|
||||||
|
|
||||||
return h('div.page-container.page-container--full-width.page-container--full-height', {}, [
|
|
||||||
|
|
||||||
h('div.page-container__header', [
|
|
||||||
|
|
||||||
h('div.page-container__title', [this.context.t('depositEther')]),
|
|
||||||
|
|
||||||
h('div.page-container__subtitle', [
|
|
||||||
this.context.t('needEtherInWallet'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('div.page-container__header-close', {
|
|
||||||
onClick: () => {
|
|
||||||
this.setState({ buyingWithShapeshift: false })
|
|
||||||
this.props.hideWarning()
|
|
||||||
this.props.hideModal()
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('.page-container__content', {}, [
|
|
||||||
|
|
||||||
h('div.deposit-ether-modal__buy-rows', [
|
|
||||||
|
|
||||||
this.renderRow({
|
|
||||||
logo: h('img.deposit-ether-modal__logo', {
|
|
||||||
src: './images/deposit-eth.svg',
|
|
||||||
}),
|
|
||||||
title: DIRECT_DEPOSIT_ROW_TITLE,
|
|
||||||
text: DIRECT_DEPOSIT_ROW_TEXT,
|
|
||||||
buttonLabel: this.context.t('viewAccount'),
|
|
||||||
onButtonClick: () => this.goToAccountDetailsModal(),
|
|
||||||
hide: buyingWithShapeshift,
|
|
||||||
}),
|
|
||||||
|
|
||||||
this.renderRow({
|
|
||||||
logo: h('i.fa.fa-tint.fa-2x'),
|
|
||||||
title: FAUCET_ROW_TITLE,
|
|
||||||
text: this.facuetRowText(networkName),
|
|
||||||
buttonLabel: this.context.t('getEther'),
|
|
||||||
onButtonClick: () => toFaucet(network),
|
|
||||||
hide: !isTestNetwork || buyingWithShapeshift,
|
|
||||||
}),
|
|
||||||
|
|
||||||
this.renderRow({
|
|
||||||
logo: h('div.deposit-ether-modal__logo', {
|
|
||||||
style: {
|
|
||||||
backgroundImage: 'url(\'./images/coinbase logo.png\')',
|
|
||||||
height: '40px',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
title: COINBASE_ROW_TITLE,
|
|
||||||
text: COINBASE_ROW_TEXT,
|
|
||||||
buttonLabel: this.context.t('continueToCoinbase'),
|
|
||||||
onButtonClick: () => toCoinbase(address),
|
|
||||||
hide: isTestNetwork || buyingWithShapeshift,
|
|
||||||
}),
|
|
||||||
|
|
||||||
this.renderRow({
|
|
||||||
logo: h('div.deposit-ether-modal__logo', {
|
|
||||||
style: {
|
|
||||||
backgroundImage: 'url(\'./images/shapeshift logo.png\')',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
title: SHAPESHIFT_ROW_TITLE,
|
|
||||||
text: SHAPESHIFT_ROW_TEXT,
|
|
||||||
buttonLabel: this.context.t('shapeshiftBuy'),
|
|
||||||
onButtonClick: () => this.setState({ buyingWithShapeshift: true }),
|
|
||||||
hide: isTestNetwork,
|
|
||||||
hideButton: buyingWithShapeshift,
|
|
||||||
hideTitle: buyingWithShapeshift,
|
|
||||||
onBackClick: () => this.setState({ buyingWithShapeshift: false }),
|
|
||||||
showBackButton: this.state.buyingWithShapeshift,
|
|
||||||
className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy',
|
|
||||||
}),
|
|
||||||
|
|
||||||
buyingWithShapeshift && h(ShapeshiftForm),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
DepositEtherModal.prototype.goToAccountDetailsModal = function () {
|
|
||||||
this.props.hideWarning()
|
|
||||||
this.props.hideModal()
|
|
||||||
this.props.showAccountDetailModal()
|
|
||||||
}
|
|
|
@ -10,7 +10,6 @@ const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums')
|
||||||
|
|
||||||
// Modal Components
|
// Modal Components
|
||||||
const BuyOptions = require('./buy-options-modal')
|
const BuyOptions = require('./buy-options-modal')
|
||||||
const DepositEtherModal = require('./deposit-ether-modal')
|
|
||||||
const EditAccountNameModal = require('./edit-account-name-modal')
|
const EditAccountNameModal = require('./edit-account-name-modal')
|
||||||
const ExportPrivateKeyModal = require('./export-private-key-modal')
|
const ExportPrivateKeyModal = require('./export-private-key-modal')
|
||||||
const NewAccountModal = require('./new-account-modal')
|
const NewAccountModal = require('./new-account-modal')
|
||||||
|
@ -72,40 +71,6 @@ const MODALS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
DEPOSIT_ETHER: {
|
|
||||||
contents: [
|
|
||||||
h(DepositEtherModal, {}, []),
|
|
||||||
],
|
|
||||||
onHide: (props) => props.hideWarning(),
|
|
||||||
mobileModalStyle: {
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
transform: 'none',
|
|
||||||
left: '0',
|
|
||||||
right: '0',
|
|
||||||
margin: '0 auto',
|
|
||||||
boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)',
|
|
||||||
top: '0',
|
|
||||||
display: 'flex',
|
|
||||||
},
|
|
||||||
laptopModalStyle: {
|
|
||||||
width: '850px',
|
|
||||||
top: 'calc(10% + 10px)',
|
|
||||||
left: '0',
|
|
||||||
right: '0',
|
|
||||||
margin: '0 auto',
|
|
||||||
boxShadow: '0 0 6px 0 rgba(0,0,0,0.3)',
|
|
||||||
borderRadius: '7px',
|
|
||||||
transform: 'none',
|
|
||||||
height: 'calc(80% - 20px)',
|
|
||||||
overflowY: 'hidden',
|
|
||||||
},
|
|
||||||
contentStyle: {
|
|
||||||
borderRadius: '7px',
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
EDIT_ACCOUNT_NAME: {
|
EDIT_ACCOUNT_NAME: {
|
||||||
contents: [
|
contents: [
|
||||||
h(EditAccountNameModal, {}, []),
|
h(EditAccountNameModal, {}, []),
|
||||||
|
|
|
@ -1,138 +0,0 @@
|
||||||
const inherits = require('util').inherits
|
|
||||||
const Component = require('react').Component
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const ReactMarkdown = require('react-markdown')
|
|
||||||
const linker = require('extension-link-enabler')
|
|
||||||
const findDOMNode = require('react-dom').findDOMNode
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
|
|
||||||
Notice.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect()(Notice)
|
|
||||||
|
|
||||||
|
|
||||||
inherits(Notice, Component)
|
|
||||||
function Notice () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
Notice.prototype.render = function () {
|
|
||||||
const { notice, onConfirm } = this.props
|
|
||||||
const { title, date, body } = notice
|
|
||||||
const state = this.state || { disclaimerDisabled: true }
|
|
||||||
const disabled = state.disclaimerDisabled
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('.flex-column.flex-center.flex-grow', {
|
|
||||||
style: {
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('h3.flex-center.text-transform-uppercase.terms-header', {
|
|
||||||
style: {
|
|
||||||
background: '#EBEBEB',
|
|
||||||
color: '#AEAEAE',
|
|
||||||
width: '100%',
|
|
||||||
fontSize: '20px',
|
|
||||||
textAlign: 'center',
|
|
||||||
padding: 6,
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
title,
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('h5.flex-center.text-transform-uppercase.terms-header', {
|
|
||||||
style: {
|
|
||||||
background: '#EBEBEB',
|
|
||||||
color: '#AEAEAE',
|
|
||||||
marginBottom: 24,
|
|
||||||
width: '100%',
|
|
||||||
fontSize: '20px',
|
|
||||||
textAlign: 'center',
|
|
||||||
padding: 6,
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
date,
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('style', `
|
|
||||||
|
|
||||||
.markdown {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown h1, .markdown h2, .markdown h3 {
|
|
||||||
margin: 10px 0;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.markdown em {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown a {
|
|
||||||
color: #df6b0e;
|
|
||||||
}
|
|
||||||
|
|
||||||
`),
|
|
||||||
|
|
||||||
h('div.markdown', {
|
|
||||||
onScroll: (e) => {
|
|
||||||
var object = e.currentTarget
|
|
||||||
if (object.offsetHeight + object.scrollTop + 100 >= object.scrollHeight) {
|
|
||||||
this.setState({disclaimerDisabled: false})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
background: 'rgb(235, 235, 235)',
|
|
||||||
height: '310px',
|
|
||||||
padding: '6px',
|
|
||||||
width: '90%',
|
|
||||||
overflowY: 'scroll',
|
|
||||||
scroll: 'auto',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h(ReactMarkdown, {
|
|
||||||
className: 'notice-box',
|
|
||||||
source: body,
|
|
||||||
skipHtml: true,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('button.primary', {
|
|
||||||
disabled,
|
|
||||||
onClick: () => {
|
|
||||||
this.setState({disclaimerDisabled: true}, () => onConfirm())
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
marginTop: '18px',
|
|
||||||
},
|
|
||||||
}, this.context.t('accept')),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Notice.prototype.componentDidMount = function () {
|
|
||||||
// eslint-disable-next-line react/no-find-dom-node
|
|
||||||
var node = findDOMNode(this)
|
|
||||||
linker.setupListener(node)
|
|
||||||
if (document.getElementsByClassName('notice-box')[0].clientHeight < 310) {
|
|
||||||
this.setState({disclaimerDisabled: false})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Notice.prototype.componentWillUnmount = function () {
|
|
||||||
// eslint-disable-next-line react/no-find-dom-node
|
|
||||||
var node = findDOMNode(this)
|
|
||||||
linker.teardownListener(node)
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
import React, { PureComponent } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import Media from 'react-media'
|
|
||||||
import { Redirect } from 'react-router-dom'
|
|
||||||
import WalletView from '../../wallet-view'
|
|
||||||
import TransactionView from '../../transaction-view'
|
|
||||||
import {
|
|
||||||
INITIALIZE_BACKUP_PHRASE_ROUTE,
|
|
||||||
RESTORE_VAULT_ROUTE,
|
|
||||||
CONFIRM_TRANSACTION_ROUTE,
|
|
||||||
NOTICE_ROUTE,
|
|
||||||
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
|
|
||||||
} from '../../../routes'
|
|
||||||
|
|
||||||
export default class Home extends PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
history: PropTypes.object,
|
|
||||||
noActiveNotices: PropTypes.bool,
|
|
||||||
lostAccounts: PropTypes.array,
|
|
||||||
forgottenPassword: PropTypes.bool,
|
|
||||||
seedWords: PropTypes.string,
|
|
||||||
suggestedTokens: PropTypes.object,
|
|
||||||
unconfirmedTransactionsCount: PropTypes.number,
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
const {
|
|
||||||
history,
|
|
||||||
suggestedTokens = {},
|
|
||||||
unconfirmedTransactionsCount = 0,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
// suggested new tokens
|
|
||||||
if (Object.keys(suggestedTokens).length > 0) {
|
|
||||||
history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unconfirmedTransactionsCount > 0) {
|
|
||||||
history.push(CONFIRM_TRANSACTION_ROUTE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const {
|
|
||||||
noActiveNotices,
|
|
||||||
lostAccounts,
|
|
||||||
forgottenPassword,
|
|
||||||
seedWords,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
// notices
|
|
||||||
if (!noActiveNotices || (lostAccounts && lostAccounts.length > 0)) {
|
|
||||||
return <Redirect to={{ pathname: NOTICE_ROUTE }} />
|
|
||||||
}
|
|
||||||
|
|
||||||
// seed words
|
|
||||||
if (seedWords) {
|
|
||||||
return <Redirect to={{ pathname: INITIALIZE_BACKUP_PHRASE_ROUTE }}/>
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forgottenPassword) {
|
|
||||||
return <Redirect to={{ pathname: RESTORE_VAULT_ROUTE }} />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="main-container">
|
|
||||||
<div className="account-and-transaction-details">
|
|
||||||
<Media
|
|
||||||
query="(min-width: 576px)"
|
|
||||||
render={() => <WalletView />}
|
|
||||||
/>
|
|
||||||
<TransactionView />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
import Home from './home.component'
|
|
||||||
import { compose } from 'recompose'
|
|
||||||
import { connect } from 'react-redux'
|
|
||||||
import { withRouter } from 'react-router-dom'
|
|
||||||
import { unconfirmedTransactionsCountSelector } from '../../../selectors/confirm-transaction'
|
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
|
||||||
const { metamask, appState } = state
|
|
||||||
const {
|
|
||||||
noActiveNotices,
|
|
||||||
lostAccounts,
|
|
||||||
seedWords,
|
|
||||||
suggestedTokens,
|
|
||||||
} = metamask
|
|
||||||
const { forgottenPassword } = appState
|
|
||||||
|
|
||||||
return {
|
|
||||||
noActiveNotices,
|
|
||||||
lostAccounts,
|
|
||||||
forgottenPassword,
|
|
||||||
seedWords,
|
|
||||||
suggestedTokens,
|
|
||||||
unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default compose(
|
|
||||||
withRouter,
|
|
||||||
connect(mapStateToProps)
|
|
||||||
)(Home)
|
|
|
@ -1 +0,0 @@
|
||||||
export { default } from './home.container'
|
|
|
@ -1,189 +0,0 @@
|
||||||
import React, { Component } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {connect} from 'react-redux'
|
|
||||||
import {
|
|
||||||
createNewVaultAndRestore,
|
|
||||||
unMarkPasswordForgotten,
|
|
||||||
} from '../../../actions'
|
|
||||||
import { DEFAULT_ROUTE } from '../../../routes'
|
|
||||||
import TextField from '../../text-field'
|
|
||||||
|
|
||||||
class RestoreVaultPage extends Component {
|
|
||||||
static contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
warning: PropTypes.string,
|
|
||||||
createNewVaultAndRestore: PropTypes.func.isRequired,
|
|
||||||
leaveImportSeedScreenState: PropTypes.func,
|
|
||||||
history: PropTypes.object,
|
|
||||||
isLoading: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
seedPhrase: '',
|
|
||||||
password: '',
|
|
||||||
confirmPassword: '',
|
|
||||||
seedPhraseError: null,
|
|
||||||
passwordError: null,
|
|
||||||
confirmPasswordError: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
parseSeedPhrase = (seedPhrase) => {
|
|
||||||
return seedPhrase
|
|
||||||
.match(/\w+/g)
|
|
||||||
.join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSeedPhraseChange (seedPhrase) {
|
|
||||||
let seedPhraseError = null
|
|
||||||
|
|
||||||
if (seedPhrase && this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) {
|
|
||||||
seedPhraseError = this.context.t('seedPhraseReq')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ seedPhrase, seedPhraseError })
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePasswordChange (password) {
|
|
||||||
const { confirmPassword } = this.state
|
|
||||||
let confirmPasswordError = null
|
|
||||||
let passwordError = null
|
|
||||||
|
|
||||||
if (password && password.length < 8) {
|
|
||||||
passwordError = this.context.t('passwordNotLongEnough')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (confirmPassword && password !== confirmPassword) {
|
|
||||||
confirmPasswordError = this.context.t('passwordsDontMatch')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ password, passwordError, confirmPasswordError })
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConfirmPasswordChange (confirmPassword) {
|
|
||||||
const { password } = this.state
|
|
||||||
let confirmPasswordError = null
|
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
|
||||||
confirmPasswordError = this.context.t('passwordsDontMatch')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ confirmPassword, confirmPasswordError })
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick = () => {
|
|
||||||
const { password, seedPhrase } = this.state
|
|
||||||
const {
|
|
||||||
createNewVaultAndRestore,
|
|
||||||
leaveImportSeedScreenState,
|
|
||||||
history,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
leaveImportSeedScreenState()
|
|
||||||
createNewVaultAndRestore(password, this.parseSeedPhrase(seedPhrase))
|
|
||||||
.then(() => history.push(DEFAULT_ROUTE))
|
|
||||||
}
|
|
||||||
|
|
||||||
hasError () {
|
|
||||||
const { passwordError, confirmPasswordError, seedPhraseError } = this.state
|
|
||||||
return passwordError || confirmPasswordError || seedPhraseError
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const {
|
|
||||||
seedPhrase,
|
|
||||||
password,
|
|
||||||
confirmPassword,
|
|
||||||
seedPhraseError,
|
|
||||||
passwordError,
|
|
||||||
confirmPasswordError,
|
|
||||||
} = this.state
|
|
||||||
const { t } = this.context
|
|
||||||
const { isLoading } = this.props
|
|
||||||
const disabled = !seedPhrase || !password || !confirmPassword || isLoading || this.hasError()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="first-view-main-wrapper">
|
|
||||||
<div className="first-view-main">
|
|
||||||
<div className="import-account">
|
|
||||||
<a
|
|
||||||
className="import-account__back-button"
|
|
||||||
onClick={e => {
|
|
||||||
e.preventDefault()
|
|
||||||
this.props.history.goBack()
|
|
||||||
}}
|
|
||||||
href="#"
|
|
||||||
>
|
|
||||||
{`< Back`}
|
|
||||||
</a>
|
|
||||||
<div className="import-account__title">
|
|
||||||
{ this.context.t('restoreAccountWithSeed') }
|
|
||||||
</div>
|
|
||||||
<div className="import-account__selector-label">
|
|
||||||
{ this.context.t('secretPhrase') }
|
|
||||||
</div>
|
|
||||||
<div className="import-account__input-wrapper">
|
|
||||||
<label className="import-account__input-label">Wallet Seed</label>
|
|
||||||
<textarea
|
|
||||||
className="import-account__secret-phrase"
|
|
||||||
onChange={e => this.handleSeedPhraseChange(e.target.value)}
|
|
||||||
value={this.state.seedPhrase}
|
|
||||||
placeholder={this.context.t('separateEachWord')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="error">
|
|
||||||
{ seedPhraseError }
|
|
||||||
</span>
|
|
||||||
<TextField
|
|
||||||
id="password"
|
|
||||||
label={t('newPassword')}
|
|
||||||
type="password"
|
|
||||||
className="first-time-flow__input"
|
|
||||||
value={this.state.password}
|
|
||||||
onChange={event => this.handlePasswordChange(event.target.value)}
|
|
||||||
error={passwordError}
|
|
||||||
autoComplete="new-password"
|
|
||||||
margin="normal"
|
|
||||||
largeLabel
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
id="confirm-password"
|
|
||||||
label={t('confirmPassword')}
|
|
||||||
type="password"
|
|
||||||
className="first-time-flow__input"
|
|
||||||
value={this.state.confirmPassword}
|
|
||||||
onChange={event => this.handleConfirmPasswordChange(event.target.value)}
|
|
||||||
error={confirmPasswordError}
|
|
||||||
autoComplete="confirm-password"
|
|
||||||
margin="normal"
|
|
||||||
largeLabel
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className="first-time-flow__button"
|
|
||||||
onClick={() => !disabled && this.onClick()}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
{this.context.t('restore')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RestoreVaultPage.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
({ appState: { warning, isLoading } }) => ({ warning, isLoading }),
|
|
||||||
dispatch => ({
|
|
||||||
leaveImportSeedScreenState: () => {
|
|
||||||
dispatch(unMarkPasswordForgotten())
|
|
||||||
},
|
|
||||||
createNewVaultAndRestore: (pw, seed) => dispatch(createNewVaultAndRestore(pw, seed)),
|
|
||||||
})
|
|
||||||
)(RestoreVaultPage)
|
|
|
@ -1,177 +0,0 @@
|
||||||
const { Component } = require('react')
|
|
||||||
const { connect } = require('react-redux')
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const classnames = require('classnames')
|
|
||||||
|
|
||||||
const { requestRevealSeedWords } = require('../../../actions')
|
|
||||||
const { DEFAULT_ROUTE } = require('../../../routes')
|
|
||||||
const ExportTextContainer = require('../../export-text-container')
|
|
||||||
|
|
||||||
import Button from '../../button'
|
|
||||||
|
|
||||||
const PASSWORD_PROMPT_SCREEN = 'PASSWORD_PROMPT_SCREEN'
|
|
||||||
const REVEAL_SEED_SCREEN = 'REVEAL_SEED_SCREEN'
|
|
||||||
|
|
||||||
class RevealSeedPage extends Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
screen: PASSWORD_PROMPT_SCREEN,
|
|
||||||
password: '',
|
|
||||||
seedWords: null,
|
|
||||||
error: null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
const passwordBox = document.getElementById('password-box')
|
|
||||||
if (passwordBox) {
|
|
||||||
passwordBox.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit (event) {
|
|
||||||
event.preventDefault()
|
|
||||||
this.setState({ seedWords: null, error: null })
|
|
||||||
this.props.requestRevealSeedWords(this.state.password)
|
|
||||||
.then(seedWords => this.setState({ seedWords, screen: REVEAL_SEED_SCREEN }))
|
|
||||||
.catch(error => this.setState({ error: error.message }))
|
|
||||||
}
|
|
||||||
|
|
||||||
renderWarning () {
|
|
||||||
return (
|
|
||||||
h('.page-container__warning-container', [
|
|
||||||
h('img.page-container__warning-icon', {
|
|
||||||
src: 'images/warning.svg',
|
|
||||||
}),
|
|
||||||
h('.page-container__warning-message', [
|
|
||||||
h('.page-container__warning-title', [this.context.t('revealSeedWordsWarningTitle')]),
|
|
||||||
h('div', [this.context.t('revealSeedWordsWarning')]),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderContent () {
|
|
||||||
return this.state.screen === PASSWORD_PROMPT_SCREEN
|
|
||||||
? this.renderPasswordPromptContent()
|
|
||||||
: this.renderRevealSeedContent()
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPasswordPromptContent () {
|
|
||||||
const { t } = this.context
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('form', {
|
|
||||||
onSubmit: event => this.handleSubmit(event),
|
|
||||||
}, [
|
|
||||||
h('label.input-label', {
|
|
||||||
htmlFor: 'password-box',
|
|
||||||
}, t('enterPasswordContinue')),
|
|
||||||
h('.input-group', [
|
|
||||||
h('input.form-control', {
|
|
||||||
type: 'password',
|
|
||||||
placeholder: t('password'),
|
|
||||||
id: 'password-box',
|
|
||||||
value: this.state.password,
|
|
||||||
onChange: event => this.setState({ password: event.target.value }),
|
|
||||||
className: classnames({ 'form-control--error': this.state.error }),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
this.state.error && h('.reveal-seed__error', this.state.error),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRevealSeedContent () {
|
|
||||||
const { t } = this.context
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('div', [
|
|
||||||
h('label.reveal-seed__label', t('yourPrivateSeedPhrase')),
|
|
||||||
h(ExportTextContainer, {
|
|
||||||
text: this.state.seedWords,
|
|
||||||
filename: t('metamaskSeedWords'),
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderFooter () {
|
|
||||||
return this.state.screen === PASSWORD_PROMPT_SCREEN
|
|
||||||
? this.renderPasswordPromptFooter()
|
|
||||||
: this.renderRevealSeedFooter()
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPasswordPromptFooter () {
|
|
||||||
return (
|
|
||||||
h('.page-container__footer', [
|
|
||||||
h('header', [
|
|
||||||
h(Button, {
|
|
||||||
type: 'default',
|
|
||||||
large: true,
|
|
||||||
className: 'page-container__footer-button',
|
|
||||||
onClick: () => this.props.history.push(DEFAULT_ROUTE),
|
|
||||||
}, this.context.t('cancel')),
|
|
||||||
h(Button, {
|
|
||||||
type: 'primary',
|
|
||||||
large: true,
|
|
||||||
className: 'page-container__footer-button',
|
|
||||||
onClick: event => this.handleSubmit(event),
|
|
||||||
disabled: this.state.password === '',
|
|
||||||
}, this.context.t('next')),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRevealSeedFooter () {
|
|
||||||
return (
|
|
||||||
h('.page-container__footer', [
|
|
||||||
h(Button, {
|
|
||||||
type: 'default',
|
|
||||||
large: true,
|
|
||||||
className: 'page-container__footer-button',
|
|
||||||
onClick: () => this.props.history.push(DEFAULT_ROUTE),
|
|
||||||
}, this.context.t('close')),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
h('.page-container', [
|
|
||||||
h('.page-container__header', [
|
|
||||||
h('.page-container__title', this.context.t('revealSeedWordsTitle')),
|
|
||||||
h('.page-container__subtitle', this.context.t('revealSeedWordsDescription')),
|
|
||||||
]),
|
|
||||||
h('.page-container__content', [
|
|
||||||
this.renderWarning(),
|
|
||||||
h('.reveal-seed__content', [
|
|
||||||
this.renderContent(),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
this.renderFooter(),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RevealSeedPage.propTypes = {
|
|
||||||
requestRevealSeedWords: PropTypes.func,
|
|
||||||
history: PropTypes.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
RevealSeedPage.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
|
||||||
return {
|
|
||||||
requestRevealSeedWords: password => dispatch(requestRevealSeedWords(password)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect(null, mapDispatchToProps)(RevealSeedPage)
|
|
|
@ -1 +0,0 @@
|
||||||
export { default } from './sender-to-recipient.component'
|
|
|
@ -1,120 +0,0 @@
|
||||||
.sender-to-recipient {
|
|
||||||
&--default {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
border-bottom: 1px solid $geyser;
|
|
||||||
position: relative;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
height: 42px;
|
|
||||||
|
|
||||||
.sender-to-recipient {
|
|
||||||
&__tooltip-wrapper {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__tooltip-container {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__party {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 16px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
&--sender {
|
|
||||||
padding-right: 30px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--recipient {
|
|
||||||
padding-left: 30px;
|
|
||||||
border-left: 1px solid $geyser;
|
|
||||||
|
|
||||||
&-with-address {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__arrow-container {
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__arrow-circle {
|
|
||||||
background: $white;
|
|
||||||
padding: 5px;
|
|
||||||
border: 1px solid $geyser;
|
|
||||||
border-radius: 20px;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
padding-left: 14px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: .875rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--cards {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
position: relative;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
|
|
||||||
.sender-to-recipient {
|
|
||||||
&__party {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex: 1;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
|
|
||||||
padding: 6px;
|
|
||||||
background: $white;
|
|
||||||
cursor: pointer;
|
|
||||||
min-width: 0;
|
|
||||||
color: $dusty-gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__tooltip-wrapper {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: .5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__arrow-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
import React, { PureComponent } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import classnames from 'classnames'
|
|
||||||
import Identicon from '../identicon'
|
|
||||||
import Tooltip from '../tooltip-v2'
|
|
||||||
import copyToClipboard from 'copy-to-clipboard'
|
|
||||||
import { DEFAULT_VARIANT, CARDS_VARIANT } from './sender-to-recipient.constants'
|
|
||||||
import { checksumAddress } from '../../util'
|
|
||||||
|
|
||||||
const variantHash = {
|
|
||||||
[DEFAULT_VARIANT]: 'sender-to-recipient--default',
|
|
||||||
[CARDS_VARIANT]: 'sender-to-recipient--cards',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class SenderToRecipient extends PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
senderName: PropTypes.string,
|
|
||||||
senderAddress: PropTypes.string,
|
|
||||||
recipientName: PropTypes.string,
|
|
||||||
recipientAddress: PropTypes.string,
|
|
||||||
t: PropTypes.func,
|
|
||||||
variant: PropTypes.oneOf([DEFAULT_VARIANT, CARDS_VARIANT]),
|
|
||||||
addressOnly: PropTypes.bool,
|
|
||||||
assetImage: PropTypes.string,
|
|
||||||
}
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
variant: DEFAULT_VARIANT,
|
|
||||||
}
|
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
senderAddressCopied: false,
|
|
||||||
recipientAddressCopied: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSenderIdenticon () {
|
|
||||||
return !this.props.addressOnly && (
|
|
||||||
<div className="sender-to-recipient__sender-icon">
|
|
||||||
<Identicon
|
|
||||||
address={checksumAddress(this.props.senderAddress)}
|
|
||||||
diameter={24}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSenderAddress () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { senderName, senderAddress, addressOnly } = this.props
|
|
||||||
const checksummedSenderAddress = checksumAddress(senderAddress)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
position="bottom"
|
|
||||||
title={this.state.senderAddressCopied ? t('copiedExclamation') : t('copyAddress')}
|
|
||||||
wrapperClassName="sender-to-recipient__tooltip-wrapper"
|
|
||||||
containerClassName="sender-to-recipient__tooltip-container"
|
|
||||||
onHidden={() => this.setState({ senderAddressCopied: false })}
|
|
||||||
>
|
|
||||||
<div className="sender-to-recipient__name">
|
|
||||||
{ addressOnly ? `${t('from')}: ${checksummedSenderAddress}` : senderName }
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRecipientIdenticon () {
|
|
||||||
const { recipientAddress, assetImage } = this.props
|
|
||||||
const checksummedRecipientAddress = checksumAddress(recipientAddress)
|
|
||||||
|
|
||||||
return !this.props.addressOnly && (
|
|
||||||
<div className="sender-to-recipient__sender-icon">
|
|
||||||
<Identicon
|
|
||||||
address={checksummedRecipientAddress}
|
|
||||||
diameter={24}
|
|
||||||
image={assetImage}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRecipientWithAddress () {
|
|
||||||
const { t } = this.context
|
|
||||||
const { recipientName, recipientAddress, addressOnly } = this.props
|
|
||||||
const checksummedRecipientAddress = checksumAddress(recipientAddress)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="sender-to-recipient__party sender-to-recipient__party--recipient sender-to-recipient__party--recipient-with-address"
|
|
||||||
onClick={() => {
|
|
||||||
this.setState({ recipientAddressCopied: true })
|
|
||||||
copyToClipboard(checksummedRecipientAddress)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ this.renderRecipientIdenticon() }
|
|
||||||
<Tooltip
|
|
||||||
position="bottom"
|
|
||||||
title={this.state.recipientAddressCopied ? t('copiedExclamation') : t('copyAddress')}
|
|
||||||
wrapperClassName="sender-to-recipient__tooltip-wrapper"
|
|
||||||
containerClassName="sender-to-recipient__tooltip-container"
|
|
||||||
onHidden={() => this.setState({ recipientAddressCopied: false })}
|
|
||||||
>
|
|
||||||
<div className="sender-to-recipient__name">
|
|
||||||
{
|
|
||||||
addressOnly
|
|
||||||
? `${t('to')}: ${checksummedRecipientAddress}`
|
|
||||||
: (recipientName || this.context.t('newContract'))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRecipientWithoutAddress () {
|
|
||||||
return (
|
|
||||||
<div className="sender-to-recipient__party sender-to-recipient__party--recipient">
|
|
||||||
{ !this.props.addressOnly && <i className="fa fa-file-text-o" /> }
|
|
||||||
<div className="sender-to-recipient__name">
|
|
||||||
{ this.context.t('newContract') }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderArrow () {
|
|
||||||
return this.props.variant === CARDS_VARIANT
|
|
||||||
? (
|
|
||||||
<div className="sender-to-recipient__arrow-container">
|
|
||||||
<img
|
|
||||||
height={20}
|
|
||||||
src="./images/caret-right.svg"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="sender-to-recipient__arrow-container">
|
|
||||||
<div className="sender-to-recipient__arrow-circle">
|
|
||||||
<img
|
|
||||||
height={15}
|
|
||||||
width={15}
|
|
||||||
src="./images/arrow-right.svg"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { senderAddress, recipientAddress, variant } = this.props
|
|
||||||
const checksummedSenderAddress = checksumAddress(senderAddress)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classnames(variantHash[variant])}>
|
|
||||||
<div
|
|
||||||
className={classnames('sender-to-recipient__party sender-to-recipient__party--sender')}
|
|
||||||
onClick={() => {
|
|
||||||
this.setState({ senderAddressCopied: true })
|
|
||||||
copyToClipboard(checksummedSenderAddress)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ this.renderSenderIdenticon() }
|
|
||||||
{ this.renderSenderAddress() }
|
|
||||||
</div>
|
|
||||||
{ this.renderArrow() }
|
|
||||||
{
|
|
||||||
recipientAddress
|
|
||||||
? this.renderRecipientWithAddress()
|
|
||||||
: this.renderRecipientWithoutAddress()
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
// Component design variants
|
|
||||||
export const DEFAULT_VARIANT = 'DEFAULT_VARIANT'
|
|
||||||
export const CARDS_VARIANT = 'CARDS_VARIANT'
|
|
|
@ -1,256 +0,0 @@
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const Component = require('react').Component
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const classnames = require('classnames')
|
|
||||||
const { qrcode } = require('qrcode-npm')
|
|
||||||
const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions')
|
|
||||||
const { isValidAddress } = require('../util')
|
|
||||||
const SimpleDropdown = require('./dropdowns/simple-dropdown')
|
|
||||||
|
|
||||||
import Button from './button'
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
const {
|
|
||||||
coinOptions,
|
|
||||||
tokenExchangeRates,
|
|
||||||
selectedAddress,
|
|
||||||
} = state.metamask
|
|
||||||
const { warning } = state.appState
|
|
||||||
|
|
||||||
return {
|
|
||||||
coinOptions,
|
|
||||||
tokenExchangeRates,
|
|
||||||
selectedAddress,
|
|
||||||
warning,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
|
||||||
return {
|
|
||||||
shapeShiftSubview: () => dispatch(shapeShiftSubview()),
|
|
||||||
pairUpdate: coin => dispatch(pairUpdate(coin)),
|
|
||||||
buyWithShapeShift: data => dispatch(buyWithShapeShift(data)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ShapeshiftForm)
|
|
||||||
|
|
||||||
|
|
||||||
inherits(ShapeshiftForm, Component)
|
|
||||||
function ShapeshiftForm () {
|
|
||||||
Component.call(this)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
depositCoin: 'btc',
|
|
||||||
refundAddress: '',
|
|
||||||
showQrCode: false,
|
|
||||||
depositAddress: '',
|
|
||||||
errorMessage: '',
|
|
||||||
isLoading: false,
|
|
||||||
bought: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.getCoinPair = function () {
|
|
||||||
return `${this.state.depositCoin.toUpperCase()}_ETH`
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.componentWillMount = function () {
|
|
||||||
this.props.shapeShiftSubview()
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.onCoinChange = function (coin) {
|
|
||||||
this.setState({
|
|
||||||
depositCoin: coin,
|
|
||||||
errorMessage: '',
|
|
||||||
})
|
|
||||||
this.props.pairUpdate(coin)
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.onBuyWithShapeShift = function () {
|
|
||||||
this.setState({
|
|
||||||
isLoading: true,
|
|
||||||
showQrCode: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
|
||||||
buyWithShapeShift,
|
|
||||||
selectedAddress: withdrawal,
|
|
||||||
} = this.props
|
|
||||||
const {
|
|
||||||
refundAddress: returnAddress,
|
|
||||||
depositCoin,
|
|
||||||
} = this.state
|
|
||||||
const pair = `${depositCoin}_eth`
|
|
||||||
const data = {
|
|
||||||
withdrawal,
|
|
||||||
pair,
|
|
||||||
returnAddress,
|
|
||||||
// Public api key
|
|
||||||
'apiKey': '5efdee9e7d3c99e7c7e8a0f788d6e52205bf00a0e24575fe59df86421f63c477d018840c94f6596cf8946990216073c68144394c384b0ddcbe782351d80d61d7',
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValidAddress(withdrawal)) {
|
|
||||||
buyWithShapeShift(data)
|
|
||||||
.then(d => this.setState({
|
|
||||||
showQrCode: true,
|
|
||||||
depositAddress: d.deposit,
|
|
||||||
isLoading: false,
|
|
||||||
}))
|
|
||||||
.catch(() => this.setState({
|
|
||||||
showQrCode: false,
|
|
||||||
errorMessage: this.context.t('invalidRequest'),
|
|
||||||
isLoading: false,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.renderMetadata = function (label, value) {
|
|
||||||
return h('div', {className: 'shapeshift-form__metadata-wrapper'}, [
|
|
||||||
|
|
||||||
h('div.shapeshift-form__metadata-label', {}, [
|
|
||||||
h('span', `${label}:`),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('div.shapeshift-form__metadata-value', {}, [
|
|
||||||
h('span', value),
|
|
||||||
]),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.renderMarketInfo = function () {
|
|
||||||
const { tokenExchangeRates } = this.props
|
|
||||||
const {
|
|
||||||
limit,
|
|
||||||
rate,
|
|
||||||
minimum,
|
|
||||||
} = tokenExchangeRates[this.getCoinPair()] || {}
|
|
||||||
|
|
||||||
return h('div.shapeshift-form__metadata', {}, [
|
|
||||||
|
|
||||||
this.renderMetadata(this.context.t('status'), limit ? this.context.t('available') : this.context.t('unavailable')),
|
|
||||||
this.renderMetadata(this.context.t('limit'), limit),
|
|
||||||
this.renderMetadata(this.context.t('exchangeRate'), rate),
|
|
||||||
this.renderMetadata(this.context.t('min'), minimum),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.renderQrCode = function () {
|
|
||||||
const { depositAddress, isLoading, depositCoin } = this.state
|
|
||||||
const qrImage = qrcode(4, 'M')
|
|
||||||
qrImage.addData(depositAddress)
|
|
||||||
qrImage.make()
|
|
||||||
|
|
||||||
return h('div.shapeshift-form', {}, [
|
|
||||||
|
|
||||||
h('div.shapeshift-form__deposit-instruction', [
|
|
||||||
this.context.t('depositCoin', [depositCoin.toUpperCase()]),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('div', depositAddress),
|
|
||||||
|
|
||||||
h('div.shapeshift-form__qr-code', [
|
|
||||||
isLoading
|
|
||||||
? h('img', {
|
|
||||||
src: 'images/loading.svg',
|
|
||||||
style: { width: '60px'},
|
|
||||||
})
|
|
||||||
: h('div', {
|
|
||||||
dangerouslySetInnerHTML: { __html: qrImage.createTableTag(4) },
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
this.renderMarketInfo(),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ShapeshiftForm.prototype.render = function () {
|
|
||||||
const { coinOptions, btnClass, warning } = this.props
|
|
||||||
const { errorMessage, showQrCode, depositAddress } = this.state
|
|
||||||
const { tokenExchangeRates } = this.props
|
|
||||||
const token = tokenExchangeRates[this.getCoinPair()]
|
|
||||||
|
|
||||||
return h('div.shapeshift-form-wrapper', [
|
|
||||||
showQrCode
|
|
||||||
? this.renderQrCode()
|
|
||||||
: h('div.modal-shapeshift-form', [
|
|
||||||
h('div.shapeshift-form__selectors', [
|
|
||||||
|
|
||||||
h('div.shapeshift-form__selector', [
|
|
||||||
|
|
||||||
h('div.shapeshift-form__selector-label', this.context.t('deposit')),
|
|
||||||
|
|
||||||
h(SimpleDropdown, {
|
|
||||||
selectedOption: this.state.depositCoin,
|
|
||||||
onSelect: (coin) => this.onCoinChange(coin),
|
|
||||||
options: Object.entries(coinOptions).map(([coin]) => ({
|
|
||||||
value: coin.toLowerCase(),
|
|
||||||
displayValue: coin,
|
|
||||||
})),
|
|
||||||
}),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('div.icon.shapeshift-form__caret', {
|
|
||||||
style: { backgroundImage: 'url(images/caret-right.svg)'},
|
|
||||||
}),
|
|
||||||
|
|
||||||
h('div.shapeshift-form__selector', [
|
|
||||||
|
|
||||||
h('div.shapeshift-form__selector-label', [
|
|
||||||
this.context.t('receive'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('div.shapeshift-form__selector-input', ['ETH']),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
warning && h('div.shapeshift-form__address-input-label', warning),
|
|
||||||
|
|
||||||
!warning && h('div', {
|
|
||||||
className: classnames('shapeshift-form__address-input-wrapper', {
|
|
||||||
'shapeshift-form__address-input-wrapper--error': errorMessage,
|
|
||||||
}),
|
|
||||||
}, [
|
|
||||||
|
|
||||||
h('div.shapeshift-form__address-input-label', [
|
|
||||||
this.context.t('refundAddress'),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('input.shapeshift-form__address-input', {
|
|
||||||
type: 'text',
|
|
||||||
onChange: e => this.setState({
|
|
||||||
refundAddress: e.target.value,
|
|
||||||
errorMessage: '',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
h('divshapeshift-form__address-input-error-message', [errorMessage]),
|
|
||||||
]),
|
|
||||||
|
|
||||||
!warning && this.renderMarketInfo(),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
!depositAddress && h(Button, {
|
|
||||||
type: 'primary',
|
|
||||||
large: true,
|
|
||||||
className: `${btnClass} shapeshift-form__shapeshift-buy-btn`,
|
|
||||||
disabled: !token,
|
|
||||||
onClick: () => this.onBuyWithShapeShift(),
|
|
||||||
}, [this.context.t('buy')]),
|
|
||||||
|
|
||||||
])
|
|
||||||
}
|
|
|
@ -1,210 +0,0 @@
|
||||||
const inherits = require('util').inherits
|
|
||||||
const Component = require('react').Component
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const vreme = new (require('vreme'))()
|
|
||||||
const ethNetProps = require('eth-net-props')
|
|
||||||
const actions = require('../actions')
|
|
||||||
const addressSummary = require('../util').addressSummary
|
|
||||||
|
|
||||||
const CopyButton = require('./copyButton')
|
|
||||||
const EthBalance = require('./eth-balance')
|
|
||||||
const Tooltip = require('./tooltip')
|
|
||||||
|
|
||||||
|
|
||||||
ShiftListItem.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(ShiftListItem)
|
|
||||||
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
return {
|
|
||||||
selectedAddress: state.metamask.selectedAddress,
|
|
||||||
conversionRate: state.metamask.conversionRate,
|
|
||||||
currentCurrency: state.metamask.currentCurrency,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(ShiftListItem, Component)
|
|
||||||
|
|
||||||
function ShiftListItem () {
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
ShiftListItem.prototype.render = function () {
|
|
||||||
return h('div.transaction-list-item.tx-list-clickable', {
|
|
||||||
style: {
|
|
||||||
paddingTop: '20px',
|
|
||||||
paddingBottom: '20px',
|
|
||||||
justifyContent: 'space-around',
|
|
||||||
alignItems: 'center',
|
|
||||||
flexDirection: 'row',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
width: '0px',
|
|
||||||
position: 'relative',
|
|
||||||
bottom: '19px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('img', {
|
|
||||||
src: './images/shapeshift-logo-only.png',
|
|
||||||
style: {
|
|
||||||
height: '35px',
|
|
||||||
width: '132px',
|
|
||||||
position: 'absolute',
|
|
||||||
clip: 'rect(0px,23px,34px,0px)',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
|
|
||||||
this.renderInfo(),
|
|
||||||
this.renderUtilComponents(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate (date) {
|
|
||||||
return vreme.format(new Date(date), 'March 16 2014 14:30')
|
|
||||||
}
|
|
||||||
|
|
||||||
ShiftListItem.prototype.renderUtilComponents = function () {
|
|
||||||
var props = this.props
|
|
||||||
const { conversionRate, currentCurrency } = props
|
|
||||||
|
|
||||||
switch (props.response.status) {
|
|
||||||
case 'no_deposits':
|
|
||||||
return h('.flex-row', [
|
|
||||||
h(CopyButton, {
|
|
||||||
value: this.props.depositAddress,
|
|
||||||
}),
|
|
||||||
h(Tooltip, {
|
|
||||||
title: this.context.t('qrCode'),
|
|
||||||
}, [
|
|
||||||
h('i.fa.fa-qrcode.pointer.pop-hover', {
|
|
||||||
onClick: () => props.dispatch(actions.reshowQrCode(props.depositAddress, props.depositType)),
|
|
||||||
style: {
|
|
||||||
margin: '5px',
|
|
||||||
marginLeft: '23px',
|
|
||||||
marginRight: '12px',
|
|
||||||
fontSize: '20px',
|
|
||||||
color: '#F7861C',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
case 'received':
|
|
||||||
return h('.flex-row')
|
|
||||||
|
|
||||||
case 'complete':
|
|
||||||
return h('.flex-row', [
|
|
||||||
h(CopyButton, {
|
|
||||||
value: this.props.response.transaction,
|
|
||||||
}),
|
|
||||||
h(EthBalance, {
|
|
||||||
value: `${props.response.outgoingCoin}`,
|
|
||||||
conversionRate,
|
|
||||||
currentCurrency,
|
|
||||||
width: '55px',
|
|
||||||
shorten: true,
|
|
||||||
needsParse: false,
|
|
||||||
incoming: true,
|
|
||||||
style: {
|
|
||||||
fontSize: '15px',
|
|
||||||
color: '#01888C',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
|
|
||||||
case 'failed':
|
|
||||||
return ''
|
|
||||||
default:
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShiftListItem.prototype.renderInfo = function () {
|
|
||||||
var props = this.props
|
|
||||||
switch (props.response.status) {
|
|
||||||
case 'no_deposits':
|
|
||||||
return h('.flex-column', {
|
|
||||||
style: {
|
|
||||||
width: '200px',
|
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
fontSize: 'x-small',
|
|
||||||
color: '#ABA9AA',
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
}, this.context.t('toETHviaShapeShift', [props.depositType])),
|
|
||||||
h('div', this.context.t('noDeposits')),
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
fontSize: 'x-small',
|
|
||||||
color: '#ABA9AA',
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
}, formatDate(props.time)),
|
|
||||||
])
|
|
||||||
case 'received':
|
|
||||||
return h('.flex-column', {
|
|
||||||
style: {
|
|
||||||
width: '200px',
|
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
fontSize: 'x-small',
|
|
||||||
color: '#ABA9AA',
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
}, this.context.t('toETHviaShapeShift', [props.depositType])),
|
|
||||||
h('div', this.context.t('conversionProgress')),
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
fontSize: 'x-small',
|
|
||||||
color: '#ABA9AA',
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
}, formatDate(props.time)),
|
|
||||||
])
|
|
||||||
case 'complete':
|
|
||||||
var url = ethNetProps.explorerLinks.getExplorerTxLinkFor(props.response.transaction, parseInt('1'))
|
|
||||||
|
|
||||||
return h('.flex-column.pointer', {
|
|
||||||
style: {
|
|
||||||
width: '200px',
|
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
|
||||||
onClick: () => global.platform.openWindow({ url }),
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
fontSize: 'x-small',
|
|
||||||
color: '#ABA9AA',
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
}, this.context.t('fromShapeShift')),
|
|
||||||
h('div', formatDate(props.time)),
|
|
||||||
h('div', {
|
|
||||||
style: {
|
|
||||||
fontSize: 'x-small',
|
|
||||||
color: '#ABA9AA',
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
}, addressSummary(props.response.transaction)),
|
|
||||||
])
|
|
||||||
|
|
||||||
case 'failed':
|
|
||||||
return h('span.error', '(' + this.context.t('failed') + ')')
|
|
||||||
default:
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
const { Component } = require('react')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const classnames = require('classnames')
|
|
||||||
|
|
||||||
class TabBar extends Component {
|
|
||||||
render () {
|
|
||||||
const { tabs = [], onSelect, isActive } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('.tab-bar', {}, [
|
|
||||||
tabs.map(({ key, content }) => {
|
|
||||||
return h('div', {
|
|
||||||
className: classnames('tab-bar__tab pointer', {
|
|
||||||
'tab-bar__tab--active': isActive(key, content),
|
|
||||||
}),
|
|
||||||
onClick: () => onSelect(key),
|
|
||||||
key,
|
|
||||||
}, content)
|
|
||||||
}),
|
|
||||||
h('div.tab-bar__tab.tab-bar__grow-tab'),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TabBar.propTypes = {
|
|
||||||
isActive: PropTypes.func.isRequired,
|
|
||||||
tabs: PropTypes.array,
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = TabBar
|
|
|
@ -1,160 +0,0 @@
|
||||||
const Component = require('react').Component
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const Identicon = require('./identicon')
|
|
||||||
const ethNetProps = require('eth-net-props')
|
|
||||||
const selectors = require('../selectors')
|
|
||||||
const actions = require('../actions')
|
|
||||||
const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
|
|
||||||
|
|
||||||
const TokenMenuDropdown = require('./dropdowns/token-menu-dropdown.js')
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
return {
|
|
||||||
network: state.metamask.network,
|
|
||||||
currentCurrency: state.metamask.currentCurrency,
|
|
||||||
selectedTokenAddress: state.metamask.selectedTokenAddress,
|
|
||||||
userAddress: selectors.getSelectedAddress(state),
|
|
||||||
contractExchangeRates: state.metamask.contractExchangeRates,
|
|
||||||
conversionRate: state.metamask.conversionRate,
|
|
||||||
sidebarOpen: state.appState.sidebar.isOpen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
|
||||||
return {
|
|
||||||
setSelectedToken: address => dispatch(actions.setSelectedToken(address)),
|
|
||||||
hideSidebar: () => dispatch(actions.hideSidebar()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(TokenCell)
|
|
||||||
|
|
||||||
inherits(TokenCell, Component)
|
|
||||||
function TokenCell () {
|
|
||||||
Component.call(this)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
tokenMenuOpen: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenCell.prototype.render = function () {
|
|
||||||
const { tokenMenuOpen } = this.state
|
|
||||||
const props = this.props
|
|
||||||
const {
|
|
||||||
address,
|
|
||||||
symbol,
|
|
||||||
string,
|
|
||||||
network,
|
|
||||||
setSelectedToken,
|
|
||||||
selectedTokenAddress,
|
|
||||||
contractExchangeRates,
|
|
||||||
conversionRate,
|
|
||||||
hideSidebar,
|
|
||||||
sidebarOpen,
|
|
||||||
currentCurrency,
|
|
||||||
// userAddress,
|
|
||||||
image,
|
|
||||||
} = props
|
|
||||||
let currentTokenToFiatRate
|
|
||||||
let currentTokenInFiat
|
|
||||||
let formattedFiat = ''
|
|
||||||
|
|
||||||
if (contractExchangeRates[address]) {
|
|
||||||
currentTokenToFiatRate = multiplyCurrencies(
|
|
||||||
contractExchangeRates[address],
|
|
||||||
conversionRate
|
|
||||||
)
|
|
||||||
currentTokenInFiat = conversionUtil(string, {
|
|
||||||
fromNumericBase: 'dec',
|
|
||||||
fromCurrency: symbol,
|
|
||||||
toCurrency: currentCurrency.toUpperCase(),
|
|
||||||
numberOfDecimals: 2,
|
|
||||||
conversionRate: currentTokenToFiatRate,
|
|
||||||
})
|
|
||||||
formattedFiat = currentTokenInFiat.toString() === '0'
|
|
||||||
? ''
|
|
||||||
: `${currentTokenInFiat} ${currentCurrency.toUpperCase()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const showFiat = Boolean(currentTokenInFiat) && currentCurrency.toUpperCase() !== symbol
|
|
||||||
|
|
||||||
return (
|
|
||||||
h('div.token-list-item', {
|
|
||||||
className: `token-list-item ${selectedTokenAddress === address ? 'token-list-item--active' : ''}`,
|
|
||||||
// style: { cursor: network === '1' ? 'pointer' : 'default' },
|
|
||||||
// onClick: this.view.bind(this, address, userAddress, network),
|
|
||||||
onClick: () => {
|
|
||||||
setSelectedToken(address)
|
|
||||||
selectedTokenAddress !== address && sidebarOpen && hideSidebar()
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
|
|
||||||
h(Identicon, {
|
|
||||||
className: 'token-list-item__identicon',
|
|
||||||
diameter: 50,
|
|
||||||
address,
|
|
||||||
network,
|
|
||||||
image,
|
|
||||||
}),
|
|
||||||
|
|
||||||
h('div.token-list-item__balance-ellipsis', null, [
|
|
||||||
h('div.token-list-item__balance-wrapper', null, [
|
|
||||||
h('div.token-list-item__token-balance', `${string || 0}`),
|
|
||||||
h('div.token-list-item__token-symbol', symbol),
|
|
||||||
showFiat && h('div.token-list-item__fiat-amount', {
|
|
||||||
style: {},
|
|
||||||
}, formattedFiat),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('i.fa.fa-ellipsis-h.fa-lg.token-list-item__ellipsis.cursor-pointer', {
|
|
||||||
onClick: (e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
this.setState({ tokenMenuOpen: true })
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
]),
|
|
||||||
|
|
||||||
|
|
||||||
tokenMenuOpen && h(TokenMenuDropdown, {
|
|
||||||
onClose: () => this.setState({ tokenMenuOpen: false }),
|
|
||||||
token: { symbol, address },
|
|
||||||
}),
|
|
||||||
|
|
||||||
/*
|
|
||||||
h('button', {
|
|
||||||
onClick: this.send.bind(this, address),
|
|
||||||
}, 'SEND'),
|
|
||||||
*/
|
|
||||||
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenCell.prototype.send = function (address, event) {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
const url = tokenFactoryFor(address)
|
|
||||||
if (url) {
|
|
||||||
navigateTo(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenCell.prototype.view = function (address, userAddress, network, event) {
|
|
||||||
const url = ethNetProps.explorerLinks.getExplorerTokenLinkFor(address, userAddress, network)
|
|
||||||
if (url) {
|
|
||||||
navigateTo(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigateTo (url) {
|
|
||||||
global.platform.openWindow({ url })
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenFactoryFor (tokenAddress) {
|
|
||||||
return `https://tokenfactory.surge.sh/#/token/${tokenAddress}`
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,188 +0,0 @@
|
||||||
const Component = require('react').Component
|
|
||||||
const PropTypes = require('prop-types')
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
const TokenTracker = require('eth-token-watcher')
|
|
||||||
const TokenCell = require('./token-cell.js')
|
|
||||||
const connect = require('react-redux').connect
|
|
||||||
const selectors = require('../selectors')
|
|
||||||
const log = require('loglevel')
|
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
|
||||||
return {
|
|
||||||
network: state.metamask.network,
|
|
||||||
tokens: state.metamask.tokens,
|
|
||||||
userAddress: selectors.getSelectedAddress(state),
|
|
||||||
assetImages: state.metamask.assetImages,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultTokens = []
|
|
||||||
const contracts = require('eth-contract-metadata')
|
|
||||||
for (const address in contracts) {
|
|
||||||
const contract = contracts[address]
|
|
||||||
if (contract.erc20) {
|
|
||||||
contract.address = address
|
|
||||||
defaultTokens.push(contract)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenList.contextTypes = {
|
|
||||||
t: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(TokenList)
|
|
||||||
|
|
||||||
|
|
||||||
inherits(TokenList, Component)
|
|
||||||
function TokenList () {
|
|
||||||
this.state = {
|
|
||||||
tokens: [],
|
|
||||||
isLoading: true,
|
|
||||||
network: null,
|
|
||||||
}
|
|
||||||
Component.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenList.prototype.render = function () {
|
|
||||||
const { userAddress, assetImages } = this.props
|
|
||||||
const state = this.state
|
|
||||||
const { tokens, isLoading, error } = state
|
|
||||||
if (isLoading) {
|
|
||||||
return this.message(this.context.t('loadingTokens'))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
log.error(error)
|
|
||||||
return h('.hotFix', {
|
|
||||||
style: {
|
|
||||||
padding: '80px',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
this.context.t('troubleTokenBalances'),
|
|
||||||
h('span.hotFix', {
|
|
||||||
style: {
|
|
||||||
color: 'rgba(247, 134, 28, 1)',
|
|
||||||
cursor: 'pointer',
|
|
||||||
},
|
|
||||||
onClick: () => {
|
|
||||||
global.platform.openWindow({
|
|
||||||
url: `https://ethplorer.io/address/${userAddress}`,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}, this.context.t('here')),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
return h('div', tokens.map((tokenData) => {
|
|
||||||
tokenData.image = assetImages[tokenData.address]
|
|
||||||
return h(TokenCell, tokenData)
|
|
||||||
}))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenList.prototype.message = function (body) {
|
|
||||||
return h('div', {
|
|
||||||
style: {
|
|
||||||
display: 'flex',
|
|
||||||
height: '250px',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: '30px',
|
|
||||||
},
|
|
||||||
}, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!global.ethereumProvider) return
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
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 })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
TokenList.prototype.componentDidUpdate = function (nextProps) {
|
|
||||||
const {
|
|
||||||
network: oldNet,
|
|
||||||
userAddress: oldAddress,
|
|
||||||
tokens,
|
|
||||||
} = this.props
|
|
||||||
const {
|
|
||||||
network: newNet,
|
|
||||||
userAddress: newAddress,
|
|
||||||
tokens: newTokens,
|
|
||||||
} = nextProps
|
|
||||||
|
|
||||||
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.updateBalances = function (tokens) {
|
|
||||||
if (!this.tracker.running) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// function uniqueMergeTokens (tokensA, tokensB = []) {
|
|
||||||
// const uniqueAddresses = []
|
|
||||||
// const result = []
|
|
||||||
// tokensA.concat(tokensB).forEach((token) => {
|
|
||||||
// const normal = normalizeAddress(token.address)
|
|
||||||
// if (!uniqueAddresses.includes(normal)) {
|
|
||||||
// uniqueAddresses.push(normal)
|
|
||||||
// result.push(token)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// return result
|
|
||||||
// }
|
|
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 289 KiB |
26
ui/css.js
|
@ -1,26 +0,0 @@
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
module.exports = bundleCss
|
|
||||||
|
|
||||||
var cssFiles = {
|
|
||||||
'index.css': fs.readFileSync(path.join(__dirname, '/app/css/output/index.css'), 'utf8'),
|
|
||||||
'first-time.css': fs.readFileSync(path.join(__dirname, '../mascara/src/app/first-time/index.css'), 'utf8'),
|
|
||||||
// 'react-tooltip.css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-tooltip', 'dist', 'style.css'), 'utf8'),
|
|
||||||
'react-css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-select', 'dist', 'react-select.css'), 'utf8'),
|
|
||||||
}
|
|
||||||
|
|
||||||
function bundleCss () {
|
|
||||||
var cssBundle = Object.keys(cssFiles).reduce(function (bundle, fileName) {
|
|
||||||
var fileContent = cssFiles[fileName]
|
|
||||||
var output = String()
|
|
||||||
|
|
||||||
output += '/*========== ' + fileName + ' ==========*/\n\n'
|
|
||||||
output += fileContent
|
|
||||||
output += '\n\n'
|
|
||||||
|
|
||||||
return bundle + output
|
|
||||||
}, String())
|
|
||||||
|
|
||||||
return cssBundle
|
|
||||||
}
|
|
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 168 KiB |
Before Width: | Height: | Size: 153 KiB |
Before Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 187 KiB |
Before Width: | Height: | Size: 158 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 197 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 285 KiB |
Before Width: | Height: | Size: 161 KiB |
Before Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 406 KiB |
Before Width: | Height: | Size: 357 KiB |
Before Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 292 KiB |