161 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { colors } from '@0x/react-shared';
 | 
						|
import { BigNumber, logUtils } from '@0x/utils';
 | 
						|
import * as _ from 'lodash';
 | 
						|
import * as React from 'react';
 | 
						|
import ReactTooltip from 'react-tooltip';
 | 
						|
import { Blockchain } from 'ts/blockchain';
 | 
						|
import { AllowanceState, AllowanceStateView } from 'ts/components/ui/allowance_state_view';
 | 
						|
import { Container } from 'ts/components/ui/container';
 | 
						|
import { PointerDirection } from 'ts/components/ui/pointer';
 | 
						|
import { Text } from 'ts/components/ui/text';
 | 
						|
import { Dispatcher } from 'ts/redux/dispatcher';
 | 
						|
import { BalanceErrs, Token, TokenState } from 'ts/types';
 | 
						|
import { analytics } from 'ts/utils/analytics';
 | 
						|
import { errorReporter } from 'ts/utils/error_reporter';
 | 
						|
import { utils } from 'ts/utils/utils';
 | 
						|
 | 
						|
export interface AllowanceStateToggleProps {
 | 
						|
    networkId: number;
 | 
						|
    blockchain: Blockchain;
 | 
						|
    dispatcher: Dispatcher;
 | 
						|
    token: Token;
 | 
						|
    tokenState: TokenState;
 | 
						|
    userAddress: string;
 | 
						|
    onErrorOccurred?: (errType: BalanceErrs) => void;
 | 
						|
    refetchTokenStateAsync: () => Promise<void>;
 | 
						|
    tooltipDirection?: PointerDirection;
 | 
						|
}
 | 
						|
 | 
						|
export interface AllowanceStateToggleState {
 | 
						|
    allowanceState: AllowanceState;
 | 
						|
    prevTokenState: TokenState;
 | 
						|
    loadingMessage?: string;
 | 
						|
}
 | 
						|
 | 
						|
const DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1);
 | 
						|
 | 
						|
export class AllowanceStateToggle extends React.Component<AllowanceStateToggleProps, AllowanceStateToggleState> {
 | 
						|
    public static defaultProps = {
 | 
						|
        onErrorOccurred: _.noop.bind(_),
 | 
						|
        tooltipDirection: PointerDirection.Right,
 | 
						|
    };
 | 
						|
    private static _getAllowanceState(tokenState: TokenState): AllowanceState {
 | 
						|
        if (!tokenState.isLoaded) {
 | 
						|
            return AllowanceState.Loading;
 | 
						|
        }
 | 
						|
        if (tokenState.allowance.gt(0)) {
 | 
						|
            return AllowanceState.Unlocked;
 | 
						|
        }
 | 
						|
        return AllowanceState.Locked;
 | 
						|
    }
 | 
						|
    constructor(props: AllowanceStateToggleProps) {
 | 
						|
        super(props);
 | 
						|
        const tokenState = props.tokenState;
 | 
						|
        this.state = {
 | 
						|
            allowanceState: AllowanceStateToggle._getAllowanceState(tokenState),
 | 
						|
            prevTokenState: tokenState,
 | 
						|
        };
 | 
						|
    }
 | 
						|
 | 
						|
    public render(): React.ReactNode {
 | 
						|
        const tooltipId = `tooltip-id-${this.props.token.symbol}`;
 | 
						|
        return (
 | 
						|
            <Container cursor="pointer">
 | 
						|
                <ReactTooltip id={tooltipId} effect="solid" offset={{ top: 3 }}>
 | 
						|
                    {this._getTooltipContent()}
 | 
						|
                </ReactTooltip>
 | 
						|
                <div
 | 
						|
                    data-tip={true}
 | 
						|
                    data-for={tooltipId}
 | 
						|
                    data-place={this.props.tooltipDirection}
 | 
						|
                    onClick={this._onToggleAllowanceAsync.bind(this)}
 | 
						|
                >
 | 
						|
                    <AllowanceStateView allowanceState={this.state.allowanceState} />
 | 
						|
                </div>
 | 
						|
            </Container>
 | 
						|
        );
 | 
						|
    }
 | 
						|
    public componentWillReceiveProps(nextProps: AllowanceStateToggleProps): void {
 | 
						|
        const nextTokenState = nextProps.tokenState;
 | 
						|
        const prevTokenState = this.state.prevTokenState;
 | 
						|
        if (
 | 
						|
            !nextTokenState.allowance.eq(prevTokenState.allowance) ||
 | 
						|
            nextTokenState.isLoaded !== prevTokenState.isLoaded
 | 
						|
        ) {
 | 
						|
            const tokenState = nextProps.tokenState;
 | 
						|
            this.setState({
 | 
						|
                prevTokenState: tokenState,
 | 
						|
                allowanceState: AllowanceStateToggle._getAllowanceState(nextTokenState),
 | 
						|
            });
 | 
						|
        }
 | 
						|
    }
 | 
						|
    private _getTooltipContent(): React.ReactNode {
 | 
						|
        const symbol = this.props.token.symbol;
 | 
						|
        switch (this.state.allowanceState) {
 | 
						|
            case AllowanceState.Loading:
 | 
						|
                return (
 | 
						|
                    <Text noWrap={true} fontColor={colors.white}>
 | 
						|
                        {this.state.loadingMessage || 'Loading...'}
 | 
						|
                    </Text>
 | 
						|
                );
 | 
						|
            case AllowanceState.Locked:
 | 
						|
                return (
 | 
						|
                    <Text noWrap={true} fontColor={colors.white}>
 | 
						|
                        Click to enable <b>{symbol}</b> for trading
 | 
						|
                    </Text>
 | 
						|
                );
 | 
						|
            case AllowanceState.Unlocked:
 | 
						|
                return (
 | 
						|
                    <Text noWrap={true} fontColor={colors.white}>
 | 
						|
                        <b>{symbol}</b> is available for trading
 | 
						|
                    </Text>
 | 
						|
                );
 | 
						|
            default:
 | 
						|
                return null;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    private async _onToggleAllowanceAsync(): Promise<void> {
 | 
						|
        // Close all tooltips
 | 
						|
        ReactTooltip.hide();
 | 
						|
        if (this.props.userAddress === '') {
 | 
						|
            this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        let newAllowanceAmountInBaseUnits = new BigNumber(0);
 | 
						|
        if (!this._isAllowanceSet()) {
 | 
						|
            newAllowanceAmountInBaseUnits = DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS;
 | 
						|
        }
 | 
						|
        const isUnlockingToken = newAllowanceAmountInBaseUnits.gt(0);
 | 
						|
        this.setState({
 | 
						|
            allowanceState: AllowanceState.Loading,
 | 
						|
            loadingMessage: `${isUnlockingToken ? 'Unlocking' : 'Locking'} ${this.props.token.symbol}`,
 | 
						|
        });
 | 
						|
        const logData = {
 | 
						|
            tokenSymbol: this.props.token.symbol,
 | 
						|
            newAllowance: newAllowanceAmountInBaseUnits.toNumber(),
 | 
						|
        };
 | 
						|
        try {
 | 
						|
            await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits);
 | 
						|
            analytics.track('Set Allowances Success', logData);
 | 
						|
            await this.props.refetchTokenStateAsync();
 | 
						|
        } catch (err) {
 | 
						|
            analytics.track('Set Allowance Failure', logData);
 | 
						|
            this.setState({
 | 
						|
                allowanceState: AllowanceStateToggle._getAllowanceState(this.state.prevTokenState),
 | 
						|
            });
 | 
						|
            const errMsg = `${err}`;
 | 
						|
            if (utils.didUserDenyWeb3Request(errMsg)) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            logUtils.log(`Unexpected error encountered: ${err}`);
 | 
						|
            logUtils.log(err.stack);
 | 
						|
            this.props.onErrorOccurred(BalanceErrs.AllowanceSettingFailed);
 | 
						|
            errorReporter.report(err);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    private _isAllowanceSet(): boolean {
 | 
						|
        return !this.props.tokenState.allowance.eq(0);
 | 
						|
    }
 | 
						|
}
 |