2019-11-05 16:13:48 +01:00
|
|
|
import React, { PureComponent } from 'react'
|
|
|
|
import PropTypes from 'prop-types'
|
2020-01-29 19:16:38 +01:00
|
|
|
import log from 'loglevel'
|
2020-08-18 21:18:25 +02:00
|
|
|
import classnames from 'classnames'
|
|
|
|
import BigNumber from 'bignumber.js'
|
2019-11-05 16:13:48 +01:00
|
|
|
import Modal from '../../modal'
|
|
|
|
import Identicon from '../../../ui/identicon'
|
|
|
|
import TextField from '../../../ui/text-field'
|
2020-01-29 19:16:38 +01:00
|
|
|
import { calcTokenAmount } from '../../../../helpers/utils/token-util'
|
2019-11-05 16:13:48 +01:00
|
|
|
|
2020-01-29 19:16:38 +01:00
|
|
|
const MAX_UNSIGNED_256_INT = new BigNumber(2).pow(256).minus(1).toString(10)
|
|
|
|
|
2019-11-05 16:13:48 +01:00
|
|
|
export default class EditApprovalPermission extends PureComponent {
|
|
|
|
static propTypes = {
|
2020-01-29 19:16:38 +01:00
|
|
|
decimals: PropTypes.number,
|
2019-11-05 16:13:48 +01:00
|
|
|
hideModal: PropTypes.func.isRequired,
|
|
|
|
selectedIdentity: PropTypes.object,
|
|
|
|
tokenAmount: PropTypes.string,
|
|
|
|
customTokenAmount: PropTypes.string,
|
|
|
|
tokenSymbol: PropTypes.string,
|
|
|
|
tokenBalance: PropTypes.string,
|
|
|
|
setCustomAmount: PropTypes.func,
|
2020-01-29 19:16:38 +01:00
|
|
|
origin: PropTypes.string.isRequired,
|
2020-10-06 20:28:38 +02:00
|
|
|
requiredMinimum: PropTypes.instanceOf(BigNumber),
|
2019-11-05 16:13:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static contextTypes = {
|
|
|
|
t: PropTypes.func,
|
|
|
|
}
|
|
|
|
|
|
|
|
state = {
|
2020-10-06 20:28:38 +02:00
|
|
|
// This is used as a TextField value, which should be a string.
|
|
|
|
customSpendLimit: this.props.customTokenAmount || '',
|
2019-11-05 16:13:48 +01:00
|
|
|
selectedOptionIsUnlimited: !this.props.customTokenAmount,
|
|
|
|
}
|
|
|
|
|
2020-01-29 19:16:38 +01:00
|
|
|
renderModalContent (error) {
|
2019-11-05 16:13:48 +01:00
|
|
|
const { t } = this.context
|
|
|
|
const {
|
|
|
|
hideModal,
|
|
|
|
selectedIdentity,
|
|
|
|
tokenAmount,
|
|
|
|
tokenSymbol,
|
|
|
|
tokenBalance,
|
|
|
|
customTokenAmount,
|
|
|
|
origin,
|
|
|
|
} = this.props
|
|
|
|
const { name, address } = selectedIdentity || {}
|
|
|
|
const { selectedOptionIsUnlimited } = this.state
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="edit-approval-permission">
|
|
|
|
<div className="edit-approval-permission__header">
|
|
|
|
<div className="edit-approval-permission__title">
|
|
|
|
{ t('editPermission') }
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
className="edit-approval-permission__header__close"
|
|
|
|
onClick={() => hideModal()}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__account-info">
|
|
|
|
<div className="edit-approval-permission__account-info__account">
|
|
|
|
<Identicon
|
|
|
|
address={address}
|
|
|
|
diameter={32}
|
|
|
|
/>
|
2020-10-06 20:28:38 +02:00
|
|
|
<div className="edit-approval-permission__name-and-balance-container">
|
|
|
|
<div className="edit-approval-permission__account-info__name">{ name }</div>
|
|
|
|
<div>{ t('balance') }</div>
|
|
|
|
</div>
|
2019-11-05 16:13:48 +01:00
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__account-info__balance">
|
2020-01-29 03:49:32 +01:00
|
|
|
{`${Number(tokenBalance).toPrecision(9)} ${tokenSymbol}`}
|
2019-11-05 16:13:48 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section">
|
|
|
|
<div className="edit-approval-permission__edit-section__title">
|
|
|
|
{ t('spendLimitPermission') }
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__description">
|
|
|
|
{ t('allowWithdrawAndSpend', [origin]) }
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__option">
|
|
|
|
<div
|
|
|
|
className="edit-approval-permission__edit-section__radio-button"
|
|
|
|
onClick={() => this.setState({ selectedOptionIsUnlimited: true })}
|
|
|
|
>
|
2019-12-03 17:35:44 +01:00
|
|
|
<div
|
|
|
|
className={classnames({
|
|
|
|
'edit-approval-permission__edit-section__radio-button-outline': !selectedOptionIsUnlimited,
|
|
|
|
'edit-approval-permission__edit-section__radio-button-outline--selected': selectedOptionIsUnlimited,
|
|
|
|
})}
|
|
|
|
/>
|
2019-11-05 16:13:48 +01:00
|
|
|
<div className="edit-approval-permission__edit-section__radio-button-fill" />
|
|
|
|
{ selectedOptionIsUnlimited && <div className="edit-approval-permission__edit-section__radio-button-dot" />}
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__option-text">
|
2019-12-03 17:35:44 +01:00
|
|
|
<div
|
|
|
|
className={classnames({
|
|
|
|
'edit-approval-permission__edit-section__option-label': !selectedOptionIsUnlimited,
|
|
|
|
'edit-approval-permission__edit-section__option-label--selected': selectedOptionIsUnlimited,
|
|
|
|
})}
|
|
|
|
>
|
2019-11-05 16:13:48 +01:00
|
|
|
{
|
2020-01-29 03:49:32 +01:00
|
|
|
(new BigNumber(tokenAmount)).lessThan(new BigNumber(tokenBalance))
|
2019-11-05 16:13:48 +01:00
|
|
|
? t('proposedApprovalLimit')
|
|
|
|
: t('unlimited')
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__option-description" >
|
|
|
|
{ t('spendLimitRequestedBy', [origin]) }
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__option-value" >
|
2020-01-29 03:49:32 +01:00
|
|
|
{`${Number(tokenAmount)} ${tokenSymbol}`}
|
2019-11-05 16:13:48 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__option">
|
|
|
|
<div
|
|
|
|
className="edit-approval-permission__edit-section__radio-button"
|
|
|
|
onClick={() => this.setState({ selectedOptionIsUnlimited: false })}
|
|
|
|
>
|
2019-12-03 17:35:44 +01:00
|
|
|
<div
|
|
|
|
className={classnames({
|
|
|
|
'edit-approval-permission__edit-section__radio-button-outline': selectedOptionIsUnlimited,
|
|
|
|
'edit-approval-permission__edit-section__radio-button-outline--selected': !selectedOptionIsUnlimited,
|
|
|
|
})}
|
|
|
|
/>
|
2019-11-05 16:13:48 +01:00
|
|
|
<div className="edit-approval-permission__edit-section__radio-button-fill" />
|
|
|
|
{ !selectedOptionIsUnlimited && <div className="edit-approval-permission__edit-section__radio-button-dot" />}
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__option-text">
|
2019-12-03 17:35:44 +01:00
|
|
|
<div
|
|
|
|
className={classnames({
|
|
|
|
'edit-approval-permission__edit-section__option-label': selectedOptionIsUnlimited,
|
|
|
|
'edit-approval-permission__edit-section__option-label--selected': !selectedOptionIsUnlimited,
|
|
|
|
})}
|
|
|
|
>
|
2019-11-05 16:13:48 +01:00
|
|
|
{ t('customSpendLimit') }
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__option-description" >
|
|
|
|
{ t('enterMaxSpendLimit') }
|
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__option-input" >
|
|
|
|
<TextField
|
|
|
|
type="number"
|
2020-01-29 03:49:32 +01:00
|
|
|
placeholder={ `${Number(customTokenAmount || tokenAmount)} ${tokenSymbol}` }
|
2019-11-05 16:13:48 +01:00
|
|
|
onChange={(event) => {
|
|
|
|
this.setState({ customSpendLimit: event.target.value })
|
|
|
|
if (selectedOptionIsUnlimited) {
|
|
|
|
this.setState({ selectedOptionIsUnlimited: false })
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
fullWidth
|
|
|
|
margin="dense"
|
|
|
|
value={ this.state.customSpendLimit }
|
2020-01-29 19:16:38 +01:00
|
|
|
error={error}
|
2019-11-05 16:13:48 +01:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-01-29 19:16:38 +01:00
|
|
|
validateSpendLimit () {
|
|
|
|
const { t } = this.context
|
2020-10-06 20:28:38 +02:00
|
|
|
const { decimals, requiredMinimum } = this.props
|
2020-01-29 19:16:38 +01:00
|
|
|
const { selectedOptionIsUnlimited, customSpendLimit } = this.state
|
|
|
|
|
|
|
|
if (selectedOptionIsUnlimited || !customSpendLimit) {
|
2020-08-12 21:06:57 +02:00
|
|
|
return undefined
|
2020-01-29 19:16:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let customSpendLimitNumber
|
|
|
|
try {
|
|
|
|
customSpendLimitNumber = new BigNumber(customSpendLimit)
|
|
|
|
} catch (error) {
|
|
|
|
log.debug(`Error converting '${customSpendLimit}' to BigNumber:`, error)
|
|
|
|
return t('spendLimitInvalid')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (customSpendLimitNumber.isNegative()) {
|
|
|
|
return t('spendLimitInvalid')
|
|
|
|
}
|
|
|
|
|
|
|
|
const maxTokenAmount = calcTokenAmount(MAX_UNSIGNED_256_INT, decimals)
|
|
|
|
if (customSpendLimitNumber.greaterThan(maxTokenAmount)) {
|
|
|
|
return t('spendLimitTooLarge')
|
|
|
|
}
|
2020-08-12 21:06:57 +02:00
|
|
|
|
2020-10-06 20:28:38 +02:00
|
|
|
if (
|
|
|
|
requiredMinimum !== undefined &&
|
|
|
|
customSpendLimitNumber.lessThan(requiredMinimum)
|
|
|
|
) {
|
|
|
|
return t('spendLimitInsufficient')
|
|
|
|
}
|
|
|
|
|
2020-08-12 21:06:57 +02:00
|
|
|
return undefined
|
2020-01-29 19:16:38 +01:00
|
|
|
}
|
|
|
|
|
2019-11-05 16:13:48 +01:00
|
|
|
render () {
|
|
|
|
const { t } = this.context
|
|
|
|
const { setCustomAmount, hideModal, customTokenAmount } = this.props
|
|
|
|
const { selectedOptionIsUnlimited, customSpendLimit } = this.state
|
2020-01-29 19:16:38 +01:00
|
|
|
|
|
|
|
const error = this.validateSpendLimit()
|
|
|
|
const disabled = Boolean(
|
|
|
|
(customSpendLimit === customTokenAmount && !selectedOptionIsUnlimited) ||
|
2020-07-14 17:20:41 +02:00
|
|
|
error,
|
2020-01-29 19:16:38 +01:00
|
|
|
)
|
|
|
|
|
2019-11-05 16:13:48 +01:00
|
|
|
return (
|
|
|
|
<Modal
|
|
|
|
onSubmit={() => {
|
2020-08-14 13:47:43 +02:00
|
|
|
setCustomAmount(selectedOptionIsUnlimited ? '' : customSpendLimit)
|
2019-11-05 16:13:48 +01:00
|
|
|
hideModal()
|
|
|
|
}}
|
|
|
|
submitText={t('save')}
|
|
|
|
submitType="primary"
|
|
|
|
contentClass="edit-approval-permission-modal-content"
|
|
|
|
containerClass="edit-approval-permission-modal-container"
|
2020-01-29 19:16:38 +01:00
|
|
|
submitDisabled={disabled}
|
2019-11-05 16:13:48 +01:00
|
|
|
>
|
2020-01-29 19:16:38 +01:00
|
|
|
{ this.renderModalContent(error) }
|
2019-11-05 16:13:48 +01:00
|
|
|
</Modal>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|