import { colors, Link } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import * as React from 'react'; import { Blockchain } from 'ts/blockchain'; import { BalanceBoundedInput } from 'ts/components/inputs/balance_bounded_input'; import { Token, ValidatedBigNumberCallback, WebsitePaths } from 'ts/types'; interface TokenAmountInputProps { userAddress: string; networkId: number; blockchain: Blockchain; token: Token; label?: string; amount?: BigNumber; hintText?: string; shouldShowIncompleteErrs: boolean; shouldCheckBalance: boolean; shouldCheckAllowance: boolean; onChange: ValidatedBigNumberCallback; onErrorMsgChange?: (errorMsg: React.ReactNode) => void; lastForceTokenStateRefetch: number; shouldShowErrs?: boolean; shouldShowUnderline?: boolean; style?: React.CSSProperties; labelStyle?: React.CSSProperties; inputHintStyle?: React.CSSProperties; } interface TokenAmountInputState { balance: BigNumber; allowance: BigNumber; isBalanceAndAllowanceLoaded: boolean; } const HEIGHT_WITH_LABEL = 84; const HEIGHT_WITHOUT_LABEL = 62; export class TokenAmountInput extends React.Component { public static defaultProps: Partial = { shouldShowErrs: true, shouldShowUnderline: true, }; private _isUnmounted: boolean; constructor(props: TokenAmountInputProps) { super(props); this._isUnmounted = false; const defaultAmount = new BigNumber(0); this.state = { balance: defaultAmount, allowance: defaultAmount, isBalanceAndAllowanceLoaded: false, }; } public componentWillMount(): void { // tslint:disable-next-line:no-floating-promises this._fetchBalanceAndAllowanceAsync(this.props.token.address, this.props.userAddress); } public componentWillUnmount(): void { this._isUnmounted = true; } public componentWillReceiveProps(nextProps: TokenAmountInputProps): void { if ( nextProps.userAddress !== this.props.userAddress || nextProps.networkId !== this.props.networkId || nextProps.token.address !== this.props.token.address || nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch ) { // tslint:disable-next-line:no-floating-promises this._fetchBalanceAndAllowanceAsync(nextProps.token.address, nextProps.userAddress); } } public render(): React.ReactNode { const amount = this.props.amount ? Web3Wrapper.toUnitAmount(this.props.amount, this.props.token.decimals) : undefined; return (
{this.props.token.symbol}
); } private _onChange(isValid: boolean, amount?: BigNumber): void { let baseUnitAmount; if (!_.isUndefined(amount)) { baseUnitAmount = Web3Wrapper.toBaseUnitAmount(amount, this.props.token.decimals); } this.props.onChange(isValid, baseUnitAmount); } private _validate(amount: BigNumber): React.ReactNode { if (this.props.shouldCheckAllowance && amount.gt(this.state.allowance)) { return ( Insufficient allowance.{' '} Set allowance ); } else { return undefined; } } private async _fetchBalanceAndAllowanceAsync(tokenAddress: string, userAddress: string): Promise { this.setState({ isBalanceAndAllowanceLoaded: false, }); const userAddressIfExists = _.isEmpty(userAddress) ? undefined : userAddress; const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( userAddressIfExists, tokenAddress, ); if (!this._isUnmounted) { this.setState({ balance, allowance, isBalanceAndAllowanceLoaded: true, }); } } private _getStyle(): React.CSSProperties { const hasLabel = !_.isUndefined(this.props.label); return !_.isUndefined(this.props.style) ? this.props.style : { height: hasLabel ? HEIGHT_WITH_LABEL : HEIGHT_WITHOUT_LABEL }; } private _getLabelStyle(): React.CSSProperties { const hasLabel = !_.isUndefined(this.props.label); return this.props.labelStyle || { paddingTop: hasLabel ? 39 : 14 }; } }