aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts/components/inputs/allowance_toggle.tsx
blob: ff3a011369247fdd7a4583dd2d2f9a097fd03f0d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { constants as sharedConstants, Styles } from '@0xproject/react-shared';
import { BigNumber, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import Toggle from 'material-ui/Toggle';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { Dispatcher } from 'ts/redux/dispatcher';
import { colors } from 'ts/style/colors';
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';

const DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1);

interface AllowanceToggleProps {
    networkId: number;
    blockchain: Blockchain;
    dispatcher: Dispatcher;
    token: Token;
    tokenState: TokenState;
    userAddress: string;
    isDisabled?: boolean;
    onErrorOccurred?: (errType: BalanceErrs) => void;
    refetchTokenStateAsync: () => Promise<void>;
}

interface AllowanceToggleState {
    isSpinnerVisible: boolean;
    prevAllowance: BigNumber;
}

const styles: Styles = {
    baseThumbStyle: {
        height: 10,
        width: 10,
        top: 6,
        backgroundColor: colors.white,
        boxShadow: `0px 0px 0px ${colors.allowanceToggleShadow}`,
    },
    offThumbStyle: {
        left: 4,
    },
    onThumbStyle: {
        left: 25,
    },
    baseTrackStyle: {
        width: 25,
    },
    offTrackStyle: {
        backgroundColor: colors.grey300,
    },
    onTrackStyle: {
        backgroundColor: colors.mediumBlue,
    },
};

export class AllowanceToggle extends React.Component<AllowanceToggleProps, AllowanceToggleState> {
    public static defaultProps = {
        onErrorOccurred: _.noop,
        isDisabled: false,
    };
    constructor(props: AllowanceToggleProps) {
        super(props);
        this.state = {
            isSpinnerVisible: false,
            prevAllowance: props.tokenState.allowance,
        };
    }
    public componentWillReceiveProps(nextProps: AllowanceToggleProps): void {
        if (!nextProps.tokenState.allowance.eq(this.state.prevAllowance)) {
            this.setState({
                isSpinnerVisible: false,
                prevAllowance: nextProps.tokenState.allowance,
            });
        }
    }
    public render(): React.ReactNode {
        return (
            <div className="flex">
                <div>
                    <Toggle
                        disabled={this.state.isSpinnerVisible || this.props.isDisabled}
                        toggled={this._isAllowanceSet()}
                        onToggle={this._onToggleAllowanceAsync.bind(this)}
                        thumbStyle={{ ...styles.baseThumbStyle, ...styles.offThumbStyle }}
                        thumbSwitchedStyle={{ ...styles.baseThumbStyle, ...styles.onThumbStyle }}
                        trackStyle={{ ...styles.baseTrackStyle, ...styles.offTrackStyle }}
                        trackSwitchedStyle={{ ...styles.baseTrackStyle, ...styles.onTrackStyle }}
                    />
                </div>
                {this.state.isSpinnerVisible && (
                    <div className="pl1" style={{ paddingTop: 3 }}>
                        <i className="zmdi zmdi-spinner zmdi-hc-spin" />
                    </div>
                )}
            </div>
        );
    }
    private async _onToggleAllowanceAsync(): Promise<void> {
        if (this.props.userAddress === '') {
            this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
            return;
        }

        this.setState({
            isSpinnerVisible: true,
        });

        let newAllowanceAmountInBaseUnits = new BigNumber(0);
        if (!this._isAllowanceSet()) {
            newAllowanceAmountInBaseUnits = DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS;
        }
        const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
        const eventLabel = `${this.props.token.symbol}-${networkName}`;
        try {
            await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits);
            analytics.logEvent('Portal', 'Set Allowance Success', eventLabel, newAllowanceAmountInBaseUnits.toNumber());
            await this.props.refetchTokenStateAsync();
        } catch (err) {
            analytics.logEvent('Portal', 'Set Allowance Failure', eventLabel, newAllowanceAmountInBaseUnits.toNumber());
            this.setState({
                isSpinnerVisible: false,
            });
            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);
    }
}