diff options
4 files changed, 118 insertions, 16 deletions
diff --git a/packages/website/ts/components/inputs/allowance_state_toggle.tsx b/packages/website/ts/components/inputs/allowance_state_toggle.tsx index bf661dee8..495efd806 100644 --- a/packages/website/ts/components/inputs/allowance_state_toggle.tsx +++ b/packages/website/ts/components/inputs/allowance_state_toggle.tsx @@ -1,36 +1,82 @@ -import { colors } from '@0xproject/react-shared'; +import { BigNumber, logUtils } from '@0xproject/utils'; +import * as _ from 'lodash'; import * as React from 'react'; -import { AllowanceStateView, AllowanceState } from 'ts/components/ui/allowance_state_view'; -import { Token, TokenState } from 'ts/types'; -import { Container } from 'ts/components/ui/container'; import ReactTooltip = require('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 { 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>; } export interface AllowanceStateToggleState { allowanceState: AllowanceState; + prevAllowance: BigNumber; } -const flip = () => Math.random() < 0.5; +const DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); export class AllowanceStateToggle extends React.Component<AllowanceStateToggleProps, AllowanceStateToggleState> { - public state = { - allowanceState: flip() ? AllowanceState.Loading : AllowanceState.Locked, + public static defaultProps = { + onErrorOccurred: _.noop.bind(_), }; + private static _getAllowanceStateFromAllowance(allowance?: BigNumber): AllowanceState { + if (_.isUndefined(allowance)) { + return AllowanceState.Loading; + } + if (allowance.gt(0)) { + return AllowanceState.Unlocked; + } + return AllowanceState.Locked; + } + constructor(props: AllowanceStateToggleProps) { + super(props); + const allowance = props.tokenState.allowance; + this.state = { + allowanceState: AllowanceState.Loading, + prevAllowance: allowance, + }; + } + public render(): React.ReactNode { const tooltipId = `tooltip-id-${this.props.token.symbol}`; return ( <Container cursor="pointer"> <ReactTooltip id={tooltipId}>{this._getTooltipContent()}</ReactTooltip> - <div data-tip={true} data-for={tooltipId} data-place="right" data-effect="solid"> + <div + data-tip={true} + data-for={tooltipId} + data-place="right" + data-effect="solid" + onClick={this._onToggleAllowanceAsync.bind(this)} + > <AllowanceStateView allowanceState={this.state.allowanceState} /> </div> </Container> ); } + public componentWillReceiveProps(nextProps: AllowanceStateToggleProps): void { + if (!nextProps.tokenState.allowance.eq(this.state.prevAllowance)) { + const allowance = nextProps.tokenState.allowance; + this.setState({ + prevAllowance: allowance, + allowanceState: AllowanceStateToggle._getAllowanceStateFromAllowance(allowance), + }); + } + } private _getTooltipContent(): React.ReactNode { const symbol = this.props.token.symbol; switch (this.state.allowanceState) { @@ -53,4 +99,44 @@ export class AllowanceStateToggle extends React.Component<AllowanceStateTogglePr return null; } } + private async _onToggleAllowanceAsync(): Promise<void> { + if (this.props.userAddress === '') { + this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); + return; + } + + this.setState({ + allowanceState: AllowanceState.Loading, + }); + + let newAllowanceAmountInBaseUnits = new BigNumber(0); + if (!this._isAllowanceSet()) { + newAllowanceAmountInBaseUnits = DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS; + } + 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._getAllowanceStateFromAllowance(this.state.prevAllowance), + }); + 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); + } } diff --git a/packages/website/ts/components/ui/allowance_state_view.tsx b/packages/website/ts/components/ui/allowance_state_view.tsx index eecf88c34..6b20d37eb 100644 --- a/packages/website/ts/components/ui/allowance_state_view.tsx +++ b/packages/website/ts/components/ui/allowance_state_view.tsx @@ -1,7 +1,5 @@ -import { colors } from '@0xproject/react-shared'; import CircularProgress from 'material-ui/CircularProgress'; import * as React from 'react'; -import { styled } from 'ts/style/theme'; export enum AllowanceState { Locked, diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 32c56ef14..2cba993c4 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -28,7 +28,6 @@ import { NullTokenRow } from 'ts/components/wallet/null_token_row'; import { PlaceHolder } from 'ts/components/wallet/placeholder'; import { StandardIconRow } from 'ts/components/wallet/standard_icon_row'; import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item'; -import { AllowanceToggle } from 'ts/containers/inputs/allowance_toggle'; import { AllowanceStateToggle } from 'ts/containers/inputs/allowance_state_toggle'; import { Dispatcher } from 'ts/redux/dispatcher'; import { colors } from 'ts/style/colors'; @@ -422,7 +421,14 @@ export class Wallet extends React.Component<WalletProps, WalletState> { // refetchTokenStateAsync={async () => this.props.refetchTokenStateAsync(config.token.address)} // /> // ); - return <AllowanceStateToggle token={config.token} tokenState={config.tokenState} />; + return ( + <AllowanceStateToggle + blockchain={this.props.blockchain} + token={config.token} + tokenState={config.tokenState} + refetchTokenStateAsync={async () => this.props.refetchTokenStateAsync(config.token.address)} + /> + ); } private _renderAmount( amount: BigNumber, diff --git a/packages/website/ts/containers/inputs/allowance_state_toggle.ts b/packages/website/ts/containers/inputs/allowance_state_toggle.ts index cdda5ecf0..d6ff11f84 100644 --- a/packages/website/ts/containers/inputs/allowance_state_toggle.ts +++ b/packages/website/ts/containers/inputs/allowance_state_toggle.ts @@ -9,21 +9,33 @@ import { AllowanceStateToggle as AllowanceStateToggleComponent } from 'ts/compon import { Dispatcher } from 'ts/redux/dispatcher'; interface AllowanceStateToggleProps { + blockchain: Blockchain; + onErrorOccurred?: (errType: BalanceErrs) => void; token: Token; tokenState: TokenState; + isDisabled?: boolean; + refetchTokenStateAsync: () => Promise<void>; } -interface ConnectedState {} +interface ConnectedState { + networkId: number; + userAddress: string; +} interface ConnectedDispatch { dispatcher: Dispatcher; } -const mapStateToProps = (state: State, _ownProps: AllowanceStateToggleProps): ConnectedState => ({}); +const mapStateToProps = (state: State, _ownProps: AllowanceStateToggleProps): ConnectedState => ({ + networkId: state.networkId, + userAddress: state.userAddress, +}); -// const mapDispatchTopProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({}); +const mapDispatchTopProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ + dispatcher: new Dispatcher(dispatch), +}); export const AllowanceStateToggle: React.ComponentClass<AllowanceStateToggleProps> = connect( mapStateToProps, - // mapDispatchTopProps, + mapDispatchTopProps, )(AllowanceStateToggleComponent); |