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
|
||||
const BuyOptions = require('./buy-options-modal')
|
||||
const DepositEtherModal = require('./deposit-ether-modal')
|
||||
const EditAccountNameModal = require('./edit-account-name-modal')
|
||||
const ExportPrivateKeyModal = require('./export-private-key-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: {
|
||||
contents: [
|
||||
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 |