import React, { useState, useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import BigNumber from 'bignumber.js'; import { I18nContext } from '../../../contexts/i18n'; import Box from '../../ui/box'; import FormField from '../../ui/form-field'; import Typography from '../../ui/typography'; import { ButtonLink, Icon, ICON_NAMES } from '../../component-library'; import { AlignItems, DISPLAY, FLEX_DIRECTION, TEXT_ALIGN, FONT_WEIGHT, TypographyVariant, JustifyContent, Size, BLOCK_SIZES, BackgroundColor, TextColor, } from '../../../helpers/constants/design-system'; import { getCustomTokenAmount } from '../../../selectors'; import { setCustomTokenAmount } from '../../../ducks/app/app'; import { calcTokenAmount } from '../../../../shared/lib/transactions-controller-utils'; import { MAX_TOKEN_ALLOWANCE_AMOUNT, NUM_W_OPT_DECIMAL_COMMA_OR_DOT_REGEX, DECIMAL_REGEX, } from '../../../../shared/constants/tokens'; import { Numeric } from '../../../../shared/modules/Numeric'; import { CustomSpendingCapTooltip } from './custom-spending-cap-tooltip'; export default function CustomSpendingCap({ tokenName, currentTokenBalance, dappProposedValue, siteOrigin, passTheErrorText, decimals, }) { const t = useContext(I18nContext); const dispatch = useDispatch(); const value = useSelector(getCustomTokenAmount); const [error, setError] = useState(''); const [showUseDefaultButton, setShowUseDefaultButton] = useState( value !== String(dappProposedValue) && true, ); const inputLogicEmptyStateText = t('inputLogicEmptyState'); const replaceCommaToDot = (inputValue) => { return inputValue.replace(/,/gu, '.'); }; const decConversionGreaterThan = (tokenValue, tokenBalance) => { return new Numeric(Number(replaceCommaToDot(tokenValue)), 10).greaterThan( Number(tokenBalance), 10, ); }; const getInputTextLogic = (inputNumber) => { if ( new Numeric(Number(replaceCommaToDot(inputNumber)), 10).lessThanOrEqualTo( new Numeric(Number(currentTokenBalance), 10), ) ) { return { className: 'custom-spending-cap__lowerValue', description: t('inputLogicEqualOrSmallerNumber', [ {replaceCommaToDot(inputNumber)} {tokenName} , ]), }; } else if (decConversionGreaterThan(inputNumber, currentTokenBalance)) { return { className: 'custom-spending-cap__higherValue', description: t('inputLogicHigherNumber'), }; } return { className: 'custom-spending-cap__emptyState', description: t('inputLogicEmptyState'), }; }; const [customSpendingCapText, setCustomSpendingCapText] = useState( getInputTextLogic(value).description, ); const handleChange = (valueInput) => { let spendingCapError = ''; const inputTextLogic = getInputTextLogic(valueInput); const inputTextLogicDescription = inputTextLogic.description; const match = DECIMAL_REGEX.exec(replaceCommaToDot(valueInput)); if (match?.[1]?.length > decimals) { return; } if (valueInput && !NUM_W_OPT_DECIMAL_COMMA_OR_DOT_REGEX.test(valueInput)) { spendingCapError = t('spendingCapError'); setCustomSpendingCapText(t('spendingCapErrorDescription', [siteOrigin])); setError(spendingCapError); } else { setCustomSpendingCapText(inputTextLogicDescription); setError(''); } const maxTokenAmount = calcTokenAmount( MAX_TOKEN_ALLOWANCE_AMOUNT, decimals, ); if (Number(valueInput.length) > 1 && Number(valueInput)) { const customSpendLimitNumber = new BigNumber(valueInput); if (customSpendLimitNumber.greaterThan(maxTokenAmount)) { spendingCapError = t('spendLimitTooLarge'); setError(spendingCapError); } } dispatch(setCustomTokenAmount(String(valueInput))); }; useEffect(() => { if (value !== String(dappProposedValue)) { setShowUseDefaultButton(true); } }, [value, dappProposedValue]); useEffect(() => { passTheErrorText(error); }, [error, passTheErrorText]); const chooseTooltipContentText = decConversionGreaterThan( value, currentTokenBalance, ) ? t('warningTooltipText', [ {t('beCareful')} , ]) : t('inputLogicEmptyState'); return ( <> ); } CustomSpendingCap.propTypes = { /** * Displayed the token name currently tracked in description related to the input state */ tokenName: PropTypes.string, /** * The current token balance of the token */ currentTokenBalance: PropTypes.string, /** * The dapp suggested amount */ dappProposedValue: PropTypes.string, /** * The origin of the site generally the URL */ siteOrigin: PropTypes.string, /** * Parent component's callback function passed in order to get the error text */ passTheErrorText: PropTypes.func, /** * Number of decimals */ decimals: PropTypes.string, };