2021-02-04 19:15:23 +01:00
|
|
|
import React, { PureComponent } from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import log from 'loglevel';
|
|
|
|
import classnames from 'classnames';
|
|
|
|
import BigNumber from 'bignumber.js';
|
|
|
|
import Modal from '../../modal';
|
|
|
|
import Identicon from '../../../ui/identicon';
|
|
|
|
import TextField from '../../../ui/text-field';
|
|
|
|
import { calcTokenAmount } from '../../../../helpers/utils/token-util';
|
|
|
|
|
|
|
|
const MAX_UNSIGNED_256_INT = new BigNumber(2).pow(256).minus(1).toString(10);
|
2020-01-29 19:16:38 +01:00
|
|
|
|
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),
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|
2019-11-05 16:13:48 +01:00
|
|
|
|
|
|
|
static contextTypes = {
|
|
|
|
t: PropTypes.func,
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|
2019-11-05 16:13:48 +01:00
|
|
|
|
|
|
|
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,
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|
2019-11-05 16:13:48 +01:00
|
|
|
|
2020-11-03 00:41:28 +01:00
|
|
|
renderModalContent(error) {
|
2021-02-04 19:15:23 +01:00
|
|
|
const { t } = this.context;
|
2019-11-05 16:13:48 +01:00
|
|
|
const {
|
|
|
|
hideModal,
|
|
|
|
selectedIdentity,
|
|
|
|
tokenAmount,
|
|
|
|
tokenSymbol,
|
|
|
|
tokenBalance,
|
|
|
|
customTokenAmount,
|
|
|
|
origin,
|
2021-02-04 19:15:23 +01:00
|
|
|
} = this.props;
|
|
|
|
const { name, address } = selectedIdentity || {};
|
|
|
|
const { selectedOptionIsUnlimited } = this.state;
|
2019-11-05 16:13:48 +01:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="edit-approval-permission">
|
|
|
|
<div className="edit-approval-permission__header">
|
|
|
|
<div className="edit-approval-permission__title">
|
2020-11-03 00:41:28 +01:00
|
|
|
{t('editPermission')}
|
2019-11-05 16:13:48 +01:00
|
|
|
</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">
|
2020-11-03 00:41:28 +01:00
|
|
|
<Identicon address={address} diameter={32} />
|
2020-10-06 20:28:38 +02:00
|
|
|
<div className="edit-approval-permission__name-and-balance-container">
|
2020-11-03 00:41:28 +01:00
|
|
|
<div className="edit-approval-permission__account-info__name">
|
|
|
|
{name}
|
|
|
|
</div>
|
|
|
|
<div>{t('balance')}</div>
|
2020-10-06 20:28:38 +02:00
|
|
|
</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">
|
2020-11-03 00:41:28 +01:00
|
|
|
{t('spendLimitPermission')}
|
2019-11-05 16:13:48 +01:00
|
|
|
</div>
|
|
|
|
<div className="edit-approval-permission__edit-section__description">
|
2020-11-03 00:41:28 +01:00
|
|
|
{t('allowWithdrawAndSpend', [origin])}
|
2019-11-05 16:13:48 +01:00
|
|
|
</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" />
|
2020-11-03 00:41:28 +01:00
|
|
|
{selectedOptionIsUnlimited && (
|
|
|
|
<div className="edit-approval-permission__edit-section__radio-button-dot" />
|
|
|
|
)}
|
2019-11-05 16:13:48 +01:00
|
|
|
</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,
|
|
|
|
})}
|
|
|
|
>
|
2020-11-03 00:41:28 +01:00
|
|
|
{new BigNumber(tokenAmount).lessThan(
|
|
|
|
new BigNumber(tokenBalance),
|
|
|
|
)
|
|
|
|
? t('proposedApprovalLimit')
|
|
|
|
: t('unlimited')}
|
2019-11-05 16:13:48 +01:00
|
|
|
</div>
|
2020-11-03 00:41:28 +01:00
|
|
|
<div className="edit-approval-permission__edit-section__option-description">
|
|
|
|
{t('spendLimitRequestedBy', [origin])}
|
2019-11-05 16:13:48 +01:00
|
|
|
</div>
|
2020-11-03 00:41:28 +01:00
|
|
|
<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"
|
2020-11-03 00:41:28 +01:00
|
|
|
onClick={() =>
|
|
|
|
this.setState({ selectedOptionIsUnlimited: false })
|
|
|
|
}
|
2019-11-05 16:13:48 +01:00
|
|
|
>
|
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" />
|
2020-11-03 00:41:28 +01:00
|
|
|
{!selectedOptionIsUnlimited && (
|
|
|
|
<div className="edit-approval-permission__edit-section__radio-button-dot" />
|
|
|
|
)}
|
2019-11-05 16:13:48 +01:00
|
|
|
</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,
|
|
|
|
})}
|
|
|
|
>
|
2020-11-03 00:41:28 +01:00
|
|
|
{t('customSpendLimit')}
|
2019-11-05 16:13:48 +01:00
|
|
|
</div>
|
2020-11-03 00:41:28 +01:00
|
|
|
<div className="edit-approval-permission__edit-section__option-description">
|
|
|
|
{t('enterMaxSpendLimit')}
|
2019-11-05 16:13:48 +01:00
|
|
|
</div>
|
2020-11-03 00:41:28 +01:00
|
|
|
<div className="edit-approval-permission__edit-section__option-input">
|
2019-11-05 16:13:48 +01:00
|
|
|
<TextField
|
|
|
|
type="number"
|
2020-11-03 00:41:28 +01:00
|
|
|
placeholder={`${Number(
|
|
|
|
customTokenAmount || tokenAmount,
|
|
|
|
)} ${tokenSymbol}`}
|
2019-11-05 16:13:48 +01:00
|
|
|
onChange={(event) => {
|
2021-02-04 19:15:23 +01:00
|
|
|
this.setState({ customSpendLimit: event.target.value });
|
2019-11-05 16:13:48 +01:00
|
|
|
if (selectedOptionIsUnlimited) {
|
2021-02-04 19:15:23 +01:00
|
|
|
this.setState({ selectedOptionIsUnlimited: false });
|
2019-11-05 16:13:48 +01:00
|
|
|
}
|
|
|
|
}}
|
|
|
|
fullWidth
|
|
|
|
margin="dense"
|
2020-11-03 00:41:28 +01:00
|
|
|
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>
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
2019-11-05 16:13:48 +01:00
|
|
|
}
|
|
|
|
|
2020-11-03 00:41:28 +01:00
|
|
|
validateSpendLimit() {
|
2021-02-04 19:15:23 +01:00
|
|
|
const { t } = this.context;
|
|
|
|
const { decimals, requiredMinimum } = this.props;
|
|
|
|
const { selectedOptionIsUnlimited, customSpendLimit } = this.state;
|
2020-01-29 19:16:38 +01:00
|
|
|
|
|
|
|
if (selectedOptionIsUnlimited || !customSpendLimit) {
|
2021-02-04 19:15:23 +01:00
|
|
|
return undefined;
|
2020-01-29 19:16:38 +01:00
|
|
|
}
|
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
let customSpendLimitNumber;
|
2020-01-29 19:16:38 +01:00
|
|
|
try {
|
2021-02-04 19:15:23 +01:00
|
|
|
customSpendLimitNumber = new BigNumber(customSpendLimit);
|
2020-01-29 19:16:38 +01:00
|
|
|
} catch (error) {
|
2021-02-04 19:15:23 +01:00
|
|
|
log.debug(`Error converting '${customSpendLimit}' to BigNumber:`, error);
|
|
|
|
return t('spendLimitInvalid');
|
2020-01-29 19:16:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (customSpendLimitNumber.isNegative()) {
|
2021-02-04 19:15:23 +01:00
|
|
|
return t('spendLimitInvalid');
|
2020-01-29 19:16:38 +01:00
|
|
|
}
|
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
const maxTokenAmount = calcTokenAmount(MAX_UNSIGNED_256_INT, decimals);
|
2020-01-29 19:16:38 +01:00
|
|
|
if (customSpendLimitNumber.greaterThan(maxTokenAmount)) {
|
2021-02-04 19:15:23 +01:00
|
|
|
return t('spendLimitTooLarge');
|
2020-01-29 19:16:38 +01:00
|
|
|
}
|
2020-08-12 21:06:57 +02:00
|
|
|
|
2020-10-06 20:28:38 +02:00
|
|
|
if (
|
|
|
|
requiredMinimum !== undefined &&
|
|
|
|
customSpendLimitNumber.lessThan(requiredMinimum)
|
|
|
|
) {
|
2021-02-04 19:15:23 +01:00
|
|
|
return t('spendLimitInsufficient');
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
return undefined;
|
2020-01-29 19:16:38 +01:00
|
|
|
}
|
|
|
|
|
2020-11-03 00:41:28 +01:00
|
|
|
render() {
|
2021-02-04 19:15:23 +01:00
|
|
|
const { t } = this.context;
|
|
|
|
const { setCustomAmount, hideModal, customTokenAmount } = this.props;
|
|
|
|
const { selectedOptionIsUnlimited, customSpendLimit } = this.state;
|
2020-01-29 19:16:38 +01:00
|
|
|
|
2021-02-04 19:15:23 +01:00
|
|
|
const error = this.validateSpendLimit();
|
2020-01-29 19:16:38 +01:00
|
|
|
const disabled = Boolean(
|
|
|
|
(customSpendLimit === customTokenAmount && !selectedOptionIsUnlimited) ||
|
2020-11-03 00:41:28 +01:00
|
|
|
error,
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
2020-01-29 19:16:38 +01:00
|
|
|
|
2019-11-05 16:13:48 +01:00
|
|
|
return (
|
|
|
|
<Modal
|
|
|
|
onSubmit={() => {
|
2021-02-04 19:15:23 +01:00
|
|
|
setCustomAmount(selectedOptionIsUnlimited ? '' : customSpendLimit);
|
|
|
|
hideModal();
|
2019-11-05 16:13:48 +01:00
|
|
|
}}
|
|
|
|
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-11-03 00:41:28 +01:00
|
|
|
{this.renderModalContent(error)}
|
2019-11-05 16:13:48 +01:00
|
|
|
</Modal>
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
2019-11-05 16:13:48 +01:00
|
|
|
}
|
|
|
|
}
|