You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
8.7 KiB
238 lines
8.7 KiB
4 years ago
|
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);
|
||
5 years ago
|
|
||
5 years ago
|
export default class EditApprovalPermission extends PureComponent {
|
||
|
static propTypes = {
|
||
5 years ago
|
decimals: PropTypes.number,
|
||
5 years ago
|
hideModal: PropTypes.func.isRequired,
|
||
|
selectedIdentity: PropTypes.object,
|
||
|
tokenAmount: PropTypes.string,
|
||
|
customTokenAmount: PropTypes.string,
|
||
|
tokenSymbol: PropTypes.string,
|
||
|
tokenBalance: PropTypes.string,
|
||
|
setCustomAmount: PropTypes.func,
|
||
5 years ago
|
origin: PropTypes.string.isRequired,
|
||
4 years ago
|
requiredMinimum: PropTypes.instanceOf(BigNumber),
|
||
4 years ago
|
};
|
||
5 years ago
|
|
||
|
static contextTypes = {
|
||
|
t: PropTypes.func,
|
||
4 years ago
|
};
|
||
5 years ago
|
|
||
|
state = {
|
||
4 years ago
|
// This is used as a TextField value, which should be a string.
|
||
|
customSpendLimit: this.props.customTokenAmount || '',
|
||
5 years ago
|
selectedOptionIsUnlimited: !this.props.customTokenAmount,
|
||
4 years ago
|
};
|
||
5 years ago
|
|
||
4 years ago
|
renderModalContent(error) {
|
||
4 years ago
|
const { t } = this.context;
|
||
5 years ago
|
const {
|
||
|
hideModal,
|
||
|
selectedIdentity,
|
||
|
tokenAmount,
|
||
|
tokenSymbol,
|
||
|
tokenBalance,
|
||
|
customTokenAmount,
|
||
|
origin,
|
||
4 years ago
|
} = this.props;
|
||
|
const { name, address } = selectedIdentity || {};
|
||
|
const { selectedOptionIsUnlimited } = this.state;
|
||
5 years ago
|
|
||
|
return (
|
||
|
<div className="edit-approval-permission">
|
||
|
<div className="edit-approval-permission__header">
|
||
|
<div className="edit-approval-permission__title">
|
||
4 years ago
|
{t('editPermission')}
|
||
5 years ago
|
</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">
|
||
4 years ago
|
<Identicon address={address} diameter={32} />
|
||
4 years ago
|
<div className="edit-approval-permission__name-and-balance-container">
|
||
4 years ago
|
<div className="edit-approval-permission__account-info__name">
|
||
|
{name}
|
||
|
</div>
|
||
|
<div>{t('balance')}</div>
|
||
4 years ago
|
</div>
|
||
5 years ago
|
</div>
|
||
|
<div className="edit-approval-permission__account-info__balance">
|
||
5 years ago
|
{`${Number(tokenBalance).toPrecision(9)} ${tokenSymbol}`}
|
||
5 years ago
|
</div>
|
||
|
</div>
|
||
|
<div className="edit-approval-permission__edit-section">
|
||
|
<div className="edit-approval-permission__edit-section__title">
|
||
4 years ago
|
{t('spendLimitPermission')}
|
||
5 years ago
|
</div>
|
||
|
<div className="edit-approval-permission__edit-section__description">
|
||
4 years ago
|
{t('allowWithdrawAndSpend', [origin])}
|
||
5 years ago
|
</div>
|
||
|
<div className="edit-approval-permission__edit-section__option">
|
||
|
<div
|
||
|
className="edit-approval-permission__edit-section__radio-button"
|
||
|
onClick={() => this.setState({ selectedOptionIsUnlimited: true })}
|
||
|
>
|
||
5 years ago
|
<div
|
||
|
className={classnames({
|
||
|
'edit-approval-permission__edit-section__radio-button-outline': !selectedOptionIsUnlimited,
|
||
|
'edit-approval-permission__edit-section__radio-button-outline--selected': selectedOptionIsUnlimited,
|
||
|
})}
|
||
|
/>
|
||
5 years ago
|
<div className="edit-approval-permission__edit-section__radio-button-fill" />
|
||
4 years ago
|
{selectedOptionIsUnlimited && (
|
||
|
<div className="edit-approval-permission__edit-section__radio-button-dot" />
|
||
|
)}
|
||
5 years ago
|
</div>
|
||
|
<div className="edit-approval-permission__edit-section__option-text">
|
||
5 years ago
|
<div
|
||
|
className={classnames({
|
||
|
'edit-approval-permission__edit-section__option-label': !selectedOptionIsUnlimited,
|
||
|
'edit-approval-permission__edit-section__option-label--selected': selectedOptionIsUnlimited,
|
||
|
})}
|
||
|
>
|
||
4 years ago
|
{new BigNumber(tokenAmount).lessThan(
|
||
|
new BigNumber(tokenBalance),
|
||
|
)
|
||
|
? t('proposedApprovalLimit')
|
||
|
: t('unlimited')}
|
||
5 years ago
|
</div>
|
||
4 years ago
|
<div className="edit-approval-permission__edit-section__option-description">
|
||
|
{t('spendLimitRequestedBy', [origin])}
|
||
5 years ago
|
</div>
|
||
4 years ago
|
<div className="edit-approval-permission__edit-section__option-value">
|
||
5 years ago
|
{`${Number(tokenAmount)} ${tokenSymbol}`}
|
||
5 years ago
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div className="edit-approval-permission__edit-section__option">
|
||
|
<div
|
||
|
className="edit-approval-permission__edit-section__radio-button"
|
||
4 years ago
|
onClick={() =>
|
||
|
this.setState({ selectedOptionIsUnlimited: false })
|
||
|
}
|
||
5 years ago
|
>
|
||
5 years ago
|
<div
|
||
|
className={classnames({
|
||
|
'edit-approval-permission__edit-section__radio-button-outline': selectedOptionIsUnlimited,
|
||
|
'edit-approval-permission__edit-section__radio-button-outline--selected': !selectedOptionIsUnlimited,
|
||
|
})}
|
||
|
/>
|
||
5 years ago
|
<div className="edit-approval-permission__edit-section__radio-button-fill" />
|
||
4 years ago
|
{!selectedOptionIsUnlimited && (
|
||
|
<div className="edit-approval-permission__edit-section__radio-button-dot" />
|
||
|
)}
|
||
5 years ago
|
</div>
|
||
|
<div className="edit-approval-permission__edit-section__option-text">
|
||
5 years ago
|
<div
|
||
|
className={classnames({
|
||
|
'edit-approval-permission__edit-section__option-label': selectedOptionIsUnlimited,
|
||
|
'edit-approval-permission__edit-section__option-label--selected': !selectedOptionIsUnlimited,
|
||
|
})}
|
||
|
>
|
||
4 years ago
|
{t('customSpendLimit')}
|
||
5 years ago
|
</div>
|
||
4 years ago
|
<div className="edit-approval-permission__edit-section__option-description">
|
||
|
{t('enterMaxSpendLimit')}
|
||
5 years ago
|
</div>
|
||
4 years ago
|
<div className="edit-approval-permission__edit-section__option-input">
|
||
5 years ago
|
<TextField
|
||
|
type="number"
|
||
4 years ago
|
placeholder={`${Number(
|
||
|
customTokenAmount || tokenAmount,
|
||
|
)} ${tokenSymbol}`}
|
||
5 years ago
|
onChange={(event) => {
|
||
4 years ago
|
this.setState({ customSpendLimit: event.target.value });
|
||
5 years ago
|
if (selectedOptionIsUnlimited) {
|
||
4 years ago
|
this.setState({ selectedOptionIsUnlimited: false });
|
||
5 years ago
|
}
|
||
|
}}
|
||
|
fullWidth
|
||
|
margin="dense"
|
||
4 years ago
|
value={this.state.customSpendLimit}
|
||
5 years ago
|
error={error}
|
||
5 years ago
|
/>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
4 years ago
|
);
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
validateSpendLimit() {
|
||
4 years ago
|
const { t } = this.context;
|
||
|
const { decimals, requiredMinimum } = this.props;
|
||
|
const { selectedOptionIsUnlimited, customSpendLimit } = this.state;
|
||
5 years ago
|
|
||
|
if (selectedOptionIsUnlimited || !customSpendLimit) {
|
||
4 years ago
|
return undefined;
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
let customSpendLimitNumber;
|
||
5 years ago
|
try {
|
||
4 years ago
|
customSpendLimitNumber = new BigNumber(customSpendLimit);
|
||
5 years ago
|
} catch (error) {
|
||
4 years ago
|
log.debug(`Error converting '${customSpendLimit}' to BigNumber:`, error);
|
||
|
return t('spendLimitInvalid');
|
||
5 years ago
|
}
|
||
|
|
||
|
if (customSpendLimitNumber.isNegative()) {
|
||
4 years ago
|
return t('spendLimitInvalid');
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
const maxTokenAmount = calcTokenAmount(MAX_UNSIGNED_256_INT, decimals);
|
||
5 years ago
|
if (customSpendLimitNumber.greaterThan(maxTokenAmount)) {
|
||
4 years ago
|
return t('spendLimitTooLarge');
|
||
5 years ago
|
}
|
||
4 years ago
|
|
||
4 years ago
|
if (
|
||
|
requiredMinimum !== undefined &&
|
||
|
customSpendLimitNumber.lessThan(requiredMinimum)
|
||
|
) {
|
||
4 years ago
|
return t('spendLimitInsufficient');
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
return undefined;
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
render() {
|
||
4 years ago
|
const { t } = this.context;
|
||
|
const { setCustomAmount, hideModal, customTokenAmount } = this.props;
|
||
|
const { selectedOptionIsUnlimited, customSpendLimit } = this.state;
|
||
5 years ago
|
|
||
4 years ago
|
const error = this.validateSpendLimit();
|
||
5 years ago
|
const disabled = Boolean(
|
||
|
(customSpendLimit === customTokenAmount && !selectedOptionIsUnlimited) ||
|
||
4 years ago
|
error,
|
||
4 years ago
|
);
|
||
5 years ago
|
|
||
5 years ago
|
return (
|
||
|
<Modal
|
||
|
onSubmit={() => {
|
||
4 years ago
|
setCustomAmount(selectedOptionIsUnlimited ? '' : customSpendLimit);
|
||
|
hideModal();
|
||
5 years ago
|
}}
|
||
|
submitText={t('save')}
|
||
|
contentClass="edit-approval-permission-modal-content"
|
||
|
containerClass="edit-approval-permission-modal-container"
|
||
5 years ago
|
submitDisabled={disabled}
|
||
5 years ago
|
>
|
||
4 years ago
|
{this.renderModalContent(error)}
|
||
5 years ago
|
</Modal>
|
||
4 years ago
|
);
|
||
5 years ago
|
}
|
||
|
}
|