From 35703539d0f2b4ddb3b11d0de8c9634af59ab71f Mon Sep 17 00:00:00 2001 From: Hsuan Lee Date: Wed, 6 Mar 2019 17:46:50 +0800 Subject: Deploy @dexon-foundation/0x.js --- packages/website/ts/components/token_balances.tsx | 658 ---------------------- 1 file changed, 658 deletions(-) delete mode 100644 packages/website/ts/components/token_balances.tsx (limited to 'packages/website/ts/components/token_balances.tsx') diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx deleted file mode 100644 index e8f2a6461..000000000 --- a/packages/website/ts/components/token_balances.tsx +++ /dev/null @@ -1,658 +0,0 @@ -import { - constants as sharedConstants, - EtherscanLinkSuffixes, - Networks, - Styles, - utils as sharedUtils, -} from '@0x/react-shared'; -import { BigNumber, errorUtils, fetchAsync, logUtils } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as _ from 'lodash'; -import Dialog from 'material-ui/Dialog'; -import Divider from 'material-ui/Divider'; -import FlatButton from 'material-ui/FlatButton'; -import FloatingActionButton from 'material-ui/FloatingActionButton'; -import ContentAdd from 'material-ui/svg-icons/content/add'; -import ContentRemove from 'material-ui/svg-icons/content/remove'; -import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table'; -import * as React from 'react'; -import ReactTooltip from 'react-tooltip'; -import firstBy from 'thenby'; -import { Blockchain } from 'ts/blockchain'; -import { AssetPicker } from 'ts/components/generate_order/asset_picker'; -import { SendButton } from 'ts/components/send_button'; -import { HelpTooltip } from 'ts/components/ui/help_tooltip'; -import { LifeCycleRaisedButton } from 'ts/components/ui/lifecycle_raised_button'; -import { TokenIcon } from 'ts/components/ui/token_icon'; -import { AllowanceStateToggle } from 'ts/containers/inputs/allowance_state_toggle'; -import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { - BalanceErrs, - BlockchainCallErrs, - BlockchainErrs, - ScreenWidths, - Token, - TokenByAddress, - TokenStateByAddress, - TokenVisibility, -} from 'ts/types'; -import { configs } from 'ts/utils/configs'; -import { constants } from 'ts/utils/constants'; -import { errorReporter } from 'ts/utils/error_reporter'; -import { utils } from 'ts/utils/utils'; - -const ETHER_ICON_PATH = '/images/ether.png'; -const ETHER_TOKEN_SYMBOL = 'WETH'; -const ZRX_TOKEN_SYMBOL = 'ZRX'; - -const ICON_DIMENSION = 40; -const ARTIFICIAL_FAUCET_REQUEST_DELAY = 1000; -const TOKEN_TABLE_ROW_HEIGHT = 60; -const MAX_TOKEN_TABLE_HEIGHT = 420; -const TOKEN_COL_SPAN_LG = 2; -const TOKEN_COL_SPAN_SM = 1; - -const styles: Styles = { - bgColor: { - backgroundColor: 'transparent', - }, -}; - -interface TokenBalancesProps { - blockchain: Blockchain; - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - dispatcher: Dispatcher; - screenWidth: ScreenWidths; - tokenByAddress: TokenByAddress; - trackedTokens: Token[]; - userAddress: string; - userEtherBalanceInWei: BigNumber; - networkId: number; - lastForceTokenStateRefetch: number; - isFullWidth?: boolean; -} - -interface TokenBalancesState { - errorType: BalanceErrs; - trackedTokenStateByAddress: TokenStateByAddress; - isBalanceSpinnerVisible: boolean; - isZRXSpinnerVisible: boolean; - isTokenPickerOpen: boolean; - isAddingToken: boolean; -} - -export class TokenBalances extends React.Component { - public static defaultProps: Partial = { - userEtherBalanceInWei: new BigNumber(0), - isFullWidth: false, - }; - private _isUnmounted: boolean; - public constructor(props: TokenBalancesProps) { - super(props); - this._isUnmounted = false; - const initialTrackedTokenStateByAddress = this._getInitialTrackedTokenStateByAddress(props.trackedTokens); - this.state = { - errorType: undefined, - isBalanceSpinnerVisible: false, - isZRXSpinnerVisible: false, - isTokenPickerOpen: false, - isAddingToken: false, - trackedTokenStateByAddress: initialTrackedTokenStateByAddress, - }; - } - public componentWillMount(): void { - const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); - } - public componentWillUnmount(): void { - this._isUnmounted = true; - } - public componentWillReceiveProps(nextProps: TokenBalancesProps): void { - if (nextProps.userEtherBalanceInWei !== this.props.userEtherBalanceInWei) { - if (this.state.isBalanceSpinnerVisible) { - const receivedAmountInWei = nextProps.userEtherBalanceInWei.minus(this.props.userEtherBalanceInWei); - const receivedAmountInEth = Web3Wrapper.toUnitAmount(receivedAmountInWei, constants.DECIMAL_PLACES_ETH); - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - this.props.dispatcher.showFlashMessage( - `Received ${receivedAmountInEth.toString(10)} ${networkName} Ether`, - ); - } - this.setState({ - isBalanceSpinnerVisible: false, - }); - } - - if ( - nextProps.userAddress !== this.props.userAddress || - nextProps.networkId !== this.props.networkId || - nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch - ) { - const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); - } - - if (!_.isEqual(nextProps.trackedTokens, this.props.trackedTokens)) { - const newTokens = _.difference(nextProps.trackedTokens, this.props.trackedTokens); - const newTokenAddresses = _.map(newTokens, token => token.address); - // Add placeholder entry for this token to the state, since fetching the - // balance/allowance is asynchronous - const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; - for (const tokenAddress of newTokenAddresses) { - trackedTokenStateByAddress[tokenAddress] = { - balance: new BigNumber(0), - allowance: new BigNumber(0), - isLoaded: false, - }; - } - this.setState({ - trackedTokenStateByAddress, - }); - // Fetch the actual balance/allowance. - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(newTokenAddresses); - } - } - public componentDidMount(): void { - window.scrollTo(0, 0); - } - public render(): React.ReactNode { - const errorDialogActions = [ - , - ]; - const isTestNetwork = utils.isTestNetwork(this.props.networkId); - const stubColumnStyle = { - display: isTestNetwork ? 'none' : 'table-cell', - }; - const allTokenRowHeight = _.size(this.props.tokenByAddress) * TOKEN_TABLE_ROW_HEIGHT; - const tokenTableHeight = - allTokenRowHeight < MAX_TOKEN_TABLE_HEIGHT ? allTokenRowHeight : MAX_TOKEN_TABLE_HEIGHT; - const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; - const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG; - const allowanceExplanation = - '0x smart contracts require access to your
\ - token balances in order to execute trades.
\ - Toggling sets an allowance for the
\ - smart contract so you can start trading that token.'; - const userEtherBalanceInEth = Web3Wrapper.toUnitAmount( - this.props.userEtherBalanceInWei, - constants.DECIMAL_PLACES_ETH, - ); - const rootClassName = this.props.isFullWidth ? 'pb2' : 'lg-px4 md-px4 sm-px1 pb2'; - return ( -
-

{isTestNetwork ? 'Test ether' : 'Ether'}

- -
- {isTestNetwork - ? 'In order to try out the 0x Portal Dapp, request some test ether to pay for \ - gas costs. It might take a bit of time for the test ether to show up.' - : 'Ether must be converted to Ether Tokens in order to be tradable via 0x. \ - You can convert between Ether and Ether Tokens from the "Wrap ETH" tab.'} -
- - - - Currency - Balance - - {isTestNetwork && Action} - Send - - - - - - - - - {userEtherBalanceInEth.toFixed(configs.AMOUNT_DISPLAY_PRECSION)} ETH - {this.state.isBalanceSpinnerVisible && ( - - - - )} - - - {isTestNetwork && ( - - - - )} - - undefined} - /> - - - -
-
-
-

{isTestNetwork ? 'Test tokens' : 'Tokens'}

-
-
- - - -
-
- - - -
-
- -
- {isTestNetwork - ? "Mint some test tokens you'd like to use to generate or fill an order using 0x." - : "Set trading permissions for a token you'd like to start trading."} -
- - - - Token - Balance - -
Allowance
- -
- {isTestNetwork && Action} - {this.props.screenWidth !== ScreenWidths.Sm && Send} -
-
- {this._renderTokenTableRows()} -
- - {this._renderErrorDialogBody()} - - -
- ); - } - private _renderTokenTableRows(): React.ReactNode { - if (!this.props.blockchainIsLoaded || this.props.blockchainErr !== BlockchainErrs.NoError) { - return ''; - } - const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; - const tokenColSpan = isSmallScreen ? TOKEN_COL_SPAN_SM : TOKEN_COL_SPAN_LG; - const actionPaddingX = isSmallScreen ? 2 : 24; - const trackedTokens = this.props.trackedTokens; - const trackedTokensStartingWithEtherToken = trackedTokens.sort( - firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) - .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) - .thenBy('trackedTimestamp'), - ); - const tableRows = _.map( - trackedTokensStartingWithEtherToken, - this._renderTokenRow.bind(this, tokenColSpan, actionPaddingX), - ); - return tableRows; - } - private _renderTokenRow(tokenColSpan: number, actionPaddingX: number, token: Token): React.ReactNode { - const tokenState = this.state.trackedTokenStateByAddress[token.address]; - const tokenLink = sharedUtils.getEtherScanLinkIfExists( - token.address, - this.props.networkId, - EtherscanLinkSuffixes.Address, - ); - const isMintable = - (_.includes(configs.SYMBOLS_OF_MINTABLE_KOVAN_TOKENS, token.symbol) && - this.props.networkId === sharedConstants.NETWORK_ID_BY_NAME[Networks.Kovan]) || - (_.includes(configs.SYMBOLS_OF_MINTABLE_ROPSTEN_TOKENS, token.symbol) && - this.props.networkId === sharedConstants.NETWORK_ID_BY_NAME[Networks.Ropsten]); - return ( - - - {_.isUndefined(tokenLink) ? ( - this._renderTokenName(token) - ) : ( - - {this._renderTokenName(token)} - - )} - - - {tokenState.isLoaded ? ( - - {this._renderAmount(tokenState.balance, token.decimals)} {token.symbol} - {this.state.isZRXSpinnerVisible && token.symbol === ZRX_TOKEN_SYMBOL && ( - - - - )} - - ) : ( - - )} - - -
- -
-
- {utils.isTestNetwork(this.props.networkId) && ( - - {isMintable && ( - Minting...} - labelComplete="Minted!" - onClickAsyncFn={this._onMintTestTokensAsync.bind(this, token)} - /> - )} - - )} - {this.props.screenWidth !== ScreenWidths.Sm && ( - - - - )} -
- ); - } - private _onAssetTokenPicked(tokenAddress: string): void { - if (_.isEmpty(tokenAddress)) { - this.setState({ - isTokenPickerOpen: false, - }); - return; - } - const token = this.props.tokenByAddress[tokenAddress]; - const isDefaultTrackedToken = _.includes(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, token.symbol); - if (!this.state.isAddingToken && !isDefaultTrackedToken) { - if (token.isRegistered) { - // Remove the token from tracked tokens - const newToken: Token = { - ...token, - trackedTimestamp: undefined, - }; - this.props.dispatcher.updateTokenByAddress([newToken]); - } else { - this.props.dispatcher.removeTokenToTokenByAddress(token); - } - trackedTokenStorage.removeTrackedToken(this.props.userAddress, this.props.networkId, tokenAddress); - } else if (isDefaultTrackedToken) { - this.props.dispatcher.showFlashMessage(`Cannot remove ${token.name} because it's a default token`); - } - this.setState({ - isTokenPickerOpen: false, - }); - } - private _onSendFailed(): void { - this.setState({ - errorType: BalanceErrs.SendFailed, - }); - } - private _renderAmount(amount: BigNumber, decimals: number): React.ReactNode { - const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals); - return unitAmount.toNumber().toFixed(configs.AMOUNT_DISPLAY_PRECSION); - } - private _renderTokenName(token: Token): React.ReactNode { - const tooltipId = `tooltip-${token.address}`; - return ( -
- -
- {token.name} -
- {token.address} -
- ); - } - private _renderErrorDialogBody(): React.ReactNode { - switch (this.state.errorType) { - case BalanceErrs.IncorrectNetworkForFaucet: - return ( -
- Our faucet can only send test Ether to addresses on testnets. Please make sure you are connected - to a testnet and try requesting again. -
- ); - - case BalanceErrs.FaucetRequestFailed: - return ( -
- An unexpected error occurred while trying to request test Ether from our faucet. Please refresh - the page and try again. -
- ); - - case BalanceErrs.FaucetQueueIsFull: - return
Our test Ether faucet queue is full. Please try requesting test Ether again later.
; - - case BalanceErrs.MintingFailed: - return
Minting your test tokens failed unexpectedly. Please refresh the page and try again.
; - - case BalanceErrs.AllowanceSettingFailed: - return ( -
- An unexpected error occurred while trying to set your test token allowance. Please refresh the - page and try again. -
- ); - - case undefined: - return null; // No error to show - - default: - throw errorUtils.spawnSwitchErr('errorType', this.state.errorType); - } - } - private _onErrorOccurred(errorType: BalanceErrs): void { - this.setState({ - errorType, - }); - } - private async _onMintTestTokensAsync(token: Token): Promise { - try { - await this.props.blockchain.mintTestTokensAsync(token); - await this._refetchTokenStateAsync(token.address); - const amount = Web3Wrapper.toUnitAmount(constants.MINT_AMOUNT, token.decimals); - this.props.dispatcher.showFlashMessage(`Successfully minted ${amount.toString(10)} ${token.symbol}`); - return true; - } catch (err) { - const errMsg = `${err}`; - if (_.includes(errMsg, BlockchainCallErrs.UserHasNoAssociatedAddresses)) { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return false; - } - if (utils.didUserDenyWeb3Request(errMsg)) { - return false; - } - logUtils.log(`Unexpected error encountered: ${err}`); - logUtils.log(err.stack); - this.setState({ - errorType: BalanceErrs.MintingFailed, - }); - errorReporter.report(err); - return false; - } - } - private async _faucetRequestAsync(isEtherRequest: boolean): Promise { - if (this.props.userAddress === '') { - this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); - return false; - } - - // If on another network other then the testnet our faucet serves test ether - // from, we must show user an error message - if (!utils.isTestNetwork(this.props.blockchain.networkId)) { - this.setState({ - errorType: BalanceErrs.IncorrectNetworkForFaucet, - }); - return false; - } - - await utils.sleepAsync(ARTIFICIAL_FAUCET_REQUEST_DELAY); - - const segment = isEtherRequest ? 'ether' : 'zrx'; - const response = await fetchAsync( - `${constants.URL_TESTNET_FAUCET}/${segment}/${this.props.userAddress}?networkId=${this.props.networkId}`, - ); - const responseBody = await response.text(); - if (response.status !== constants.SUCCESS_STATUS) { - logUtils.log(`Unexpected status code: ${response.status} -> ${responseBody}`); - const errorType = - response.status === constants.UNAVAILABLE_STATUS - ? BalanceErrs.FaucetQueueIsFull - : BalanceErrs.FaucetRequestFailed; - this.setState({ - errorType, - }); - errorReporter.report(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`)); - return false; - } - - if (isEtherRequest) { - this.setState({ - isBalanceSpinnerVisible: true, - }); - } else { - this.setState({ - isZRXSpinnerVisible: true, - }); - // tslint:disable-next-line:no-floating-promises - this._startPollingZrxBalanceAsync(); - } - return true; - } - private _onErrorDialogToggle(_isOpen: boolean): void { - this.setState({ - errorType: undefined, - }); - } - private _onAddTokenClicked(): void { - this.setState({ - isTokenPickerOpen: true, - isAddingToken: true, - }); - } - private _onRemoveTokenClicked(): void { - this.setState({ - isTokenPickerOpen: true, - isAddingToken: false, - }); - } - private async _startPollingZrxBalanceAsync(): Promise { - const tokens = _.values(this.props.tokenByAddress); - const zrxToken = _.find(tokens, t => t.symbol === ZRX_TOKEN_SYMBOL); - - // tslint:disable-next-line:no-floating-promises - const balance = await this.props.blockchain.pollTokenBalanceAsync(zrxToken); - const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; - trackedTokenStateByAddress[zrxToken.address] = { - ...trackedTokenStateByAddress[zrxToken.address], - balance, - }; - this.setState({ - isZRXSpinnerVisible: false, - }); - } - private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]): Promise { - const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; - const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; - for (const tokenAddress of tokenAddresses) { - const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( - userAddressIfExists, - tokenAddress, - ); - trackedTokenStateByAddress[tokenAddress] = { - balance, - allowance, - isLoaded: true, - }; - } - if (!this._isUnmounted) { - this.setState({ - trackedTokenStateByAddress, - }); - } - } - private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]): TokenStateByAddress { - const trackedTokenStateByAddress: TokenStateByAddress = {}; - _.each(trackedTokens, token => { - trackedTokenStateByAddress[token.address] = { - balance: new BigNumber(0), - allowance: new BigNumber(0), - isLoaded: false, - }; - }); - return trackedTokenStateByAddress; - } - private async _refetchTokenStateAsync(tokenAddress: string): Promise { - const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; - const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( - userAddressIfExists, - tokenAddress, - ); - this.setState({ - trackedTokenStateByAddress: { - ...this.state.trackedTokenStateByAddress, - [tokenAddress]: { - balance, - allowance, - isLoaded: true, - }, - }, - }); - } -} // tslint:disable:max-file-line-count -- cgit v1.2.3