From f1354632a1a2915159f6d662f90b68fe8c3bab38 Mon Sep 17 00:00:00 2001
From: fragosti <francesco.agosti93@gmail.com>
Date: Thu, 29 Nov 2018 18:42:25 -0800
Subject: feat: implement available token fetching

---
 .../website/ts/pages/instant/config_generator.tsx  | 107 ++++++++++++++++++++-
 1 file changed, 105 insertions(+), 2 deletions(-)

(limited to 'packages/website/ts/pages/instant/config_generator.tsx')

diff --git a/packages/website/ts/pages/instant/config_generator.tsx b/packages/website/ts/pages/instant/config_generator.tsx
index 0dac0f9ec..cd215bc61 100644
--- a/packages/website/ts/pages/instant/config_generator.tsx
+++ b/packages/website/ts/pages/instant/config_generator.tsx
@@ -1,10 +1,19 @@
+import { StandardRelayerAPIOrderProvider } from '@0x/asset-buyer';
+import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses';
+import { assetDataUtils } from '@0x/order-utils';
+import { ObjectMap } from '@0x/types';
 import * as _ from 'lodash';
 import * as React from 'react';
 
 import { Container } from 'ts/components/ui/container';
+import { MultiSelect } from 'ts/components/ui/multi_select';
 import { Select, SelectItemConfig } from 'ts/components/ui/select';
+import { Spinner } from 'ts/components/ui/spinner';
 import { Text } from 'ts/components/ui/text';
 import { colors } from 'ts/style/colors';
+import { WebsiteBackendTokenInfo } from 'ts/types';
+import { backendClient } from 'ts/utils/backend_client';
+import { constants } from 'ts/utils/constants';
 
 import { ZeroExInstantBaseConfig } from '../../../../instant/src/types';
 
@@ -13,17 +22,41 @@ export interface ConfigGeneratorProps {
     onConfigChange: (config: ZeroExInstantBaseConfig) => void;
 }
 
+export interface ConfigGeneratorState {
+    isLoadingAvailableTokens: boolean;
+    // Address to token info
+    allKnownTokens: ObjectMap<WebsiteBackendTokenInfo>;
+    availableTokens?: WebsiteBackendTokenInfo[];
+}
+
 const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrelay.xyz/v2/'];
 
 export class ConfigGenerator extends React.Component<ConfigGeneratorProps> {
+    public state: ConfigGeneratorState = {
+        isLoadingAvailableTokens: true,
+        allKnownTokens: {},
+    };
+    public componentDidMount(): void {
+        this._setAllKnownTokens(this._setAvailableAssetsFromOrderProvider);
+    }
+    public componentDidUpdate(prevProps: ConfigGeneratorProps): void {
+        if (prevProps.value.orderSource !== this.props.value.orderSource) {
+            this._setAvailableAssetsFromOrderProvider();
+        }
+    }
     public render(): React.ReactNode {
         const { value } = this.props;
+        if (!_.isString(value.orderSource)) {
+            throw new Error('ConfigGenerator component only supports string values as an orderSource.');
+        }
         return (
             <Container>
                 <ConfigGeneratorSection title="Standard Relayer API Endpoint">
-                    <Select value={value.orderSource as string} items={this._generateItems()} />
+                    <Select value={value.orderSource} items={this._generateItems()} />
+                </ConfigGeneratorSection>
+                <ConfigGeneratorSection title="What tokens can users buy?">
+                    {this._renderTokenMultiSelectOrSpinner()}
                 </ConfigGeneratorSection>
-                <ConfigGeneratorSection title="What tokens can users buy?">BLAH</ConfigGeneratorSection>
             </Container>
         );
     }
@@ -40,6 +73,76 @@ export class ConfigGenerator extends React.Component<ConfigGeneratorProps> {
         };
         this.props.onConfigChange(newConfig);
     };
+    private readonly _handleTokenClick = (assetData: string) => {
+        const { value } = this.props;
+        let newAvailableAssetDatas = [];
+        if (_.includes(value.availableAssetDatas, assetData)) {
+            // Add it
+            newAvailableAssetDatas = [...value.availableAssetDatas, assetData];
+        } else {
+            // Remove it
+            newAvailableAssetDatas = _.remove(value.availableAssetDatas, assetData);
+        }
+        const newConfig = {
+            ...this.props.value,
+            availableAssetDatas: newAvailableAssetDatas,
+        };
+        this.props.onConfigChange(newConfig);
+    };
+    private _setAllKnownTokens = async (callback: () => void): Promise<void> => {
+        const tokenInfos = await backendClient.getTokenInfosAsync();
+        const allKnownTokens = _.reduce(
+            tokenInfos,
+            (acc, tokenInfo) => {
+                acc[tokenInfo.address] = tokenInfo;
+                return acc;
+            },
+            {} as ObjectMap<WebsiteBackendTokenInfo>,
+        );
+        this.setState({ allKnownTokens }, callback);
+    };
+    private _setAvailableAssetsFromOrderProvider = async (): Promise<void> => {
+        const { value } = this.props;
+        if (!_.isUndefined(value.orderSource) && _.isString(value.orderSource)) {
+            this.setState({ isLoadingAvailableTokens: true });
+            const networkId = constants.NETWORK_ID_MAINNET;
+            const sraOrderProvider = new StandardRelayerAPIOrderProvider(value.orderSource, networkId);
+            const etherTokenAddress = getContractAddressesForNetworkOrThrow(networkId).etherToken;
+            const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddress);
+            const assetDatas = await sraOrderProvider.getAvailableMakerAssetDatasAsync(etherTokenAssetData);
+            const availableTokens = _.compact(
+                _.map(assetDatas, assetData => {
+                    const address = assetDataUtils.decodeAssetDataOrThrow(assetData).tokenAddress;
+                    return this.state.allKnownTokens[address];
+                }),
+            );
+            this.setState({ availableTokens, isLoadingAvailableTokens: false });
+        }
+    };
+    private _renderTokenMultiSelectOrSpinner = (): React.ReactNode => {
+        const { value } = this.props;
+        const { availableTokens, isLoadingAvailableTokens } = this.state;
+        if (isLoadingAvailableTokens) {
+            return (
+                <Container className="flex items-center">
+                    <Spinner />
+                </Container>
+            );
+        }
+        const items = _.map(availableTokens, token => {
+            const assetData = assetDataUtils.encodeERC20AssetData(token.address);
+            return {
+                value: assetDataUtils.encodeERC20AssetData(token.address),
+                displayText: (
+                    <Text>
+                        <b>{token.symbol}</b> - {token.name}
+                    </Text>
+                ),
+                onClick: this._handleTokenClick.bind(this, assetData),
+            };
+        });
+        return <MultiSelect items={items} selectedValues={value.availableAssetDatas || []} />;
+    };
 }
 
 export interface ConfigGeneratorSectionProps {
-- 
cgit v1.2.3

