From e51f9b3593c86c5ab4ec0ecb7e7ea8a9857a7c74 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sun, 22 Apr 2018 13:02:47 -0400 Subject: Grab price information from crypto compare api --- packages/website/ts/components/wallet/wallet.tsx | 45 +++++++++++++++++++----- packages/website/ts/utils/configs.ts | 1 + 2 files changed, 38 insertions(+), 8 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index d1ae38550..44bf69455 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -6,7 +6,7 @@ import { Styles, utils as sharedUtils, } from '@0xproject/react-shared'; -import { BigNumber } from '@0xproject/utils'; +import { BigNumber, logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import FlatButton from 'material-ui/FlatButton'; import { List, ListItem } from 'material-ui/List'; @@ -37,7 +37,9 @@ import { TokenStateByAddress, } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; +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'; import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles'; @@ -491,15 +493,42 @@ export class Wallet extends React.Component { if (_.isEmpty(tokenAddresses)) { return {}; } - try { - const websiteBackendPriceInfos = await backendClient.getPriceInfosAsync(tokenAddresses); - const addresses = _.map(websiteBackendPriceInfos, info => info.address); - const prices = _.map(websiteBackendPriceInfos, info => new BigNumber(info.price)); - const pricesByAddress = _.zipObject(addresses, prices); - return pricesByAddress; - } catch (err) { + // for each input token address, search for the corresponding symbol in this.props.tokenByAddress, if it exists + // create a mapping from existing symbols -> address + const tokenAddressBySymbol = _.fromPairs( + _.compact( + _.map(tokenAddresses, address => { + const tokenIfExists = _.get(this.props.tokenByAddress, address); + if (!_.isUndefined(tokenIfExists)) { + const symbol = tokenIfExists.symbol; + // the crypto compare api doesn't understand 'WETH' so we need to replace it with 'ETH' + const key = symbol === ETHER_TOKEN_SYMBOL ? ETHER_SYMBOL : symbol; + return [key, address]; + } else { + return undefined; + } + }), + ), + ); + const joinedTokenSymbols = _.keys(tokenAddressBySymbol).join(','); + const url = `${configs.CRYPTO_COMPARE_BASE_URL}/pricemulti?fsyms=${joinedTokenSymbols}&tsyms=USD`; + const response = await fetch(url); + if (response.status !== 200) { + const errorText = `Error requesting url: ${url}, ${response.status}: ${response.statusText}`; + logUtils.log(errorText); + const error = Error(errorText); + // tslint:disable-next-line:no-floating-promises + errorReporter.reportAsync(error); return {}; } + const priceInfoBySymbol = await response.json(); + const priceInfoByAddress = _.mapKeys(priceInfoBySymbol, (value, symbol) => _.get(tokenAddressBySymbol, symbol)); + const result = _.mapValues(priceInfoByAddress, priceInfo => { + const price = _.get(priceInfo, 'USD'); + const priceBigNumber = new BigNumber(price); + return priceBigNumber; + }); + return result; } private _openWrappedEtherActionRow(wrappedEtherDirection: Side) { this.setState({ diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index a54fc56a8..edfd04291 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -13,6 +13,7 @@ export const configs = { BACKEND_BASE_URL: 'https://website-api.0xproject.com', BASE_URL, BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', + CRYPTO_COMPARE_BASE_URL: 'https://min-api.cryptocompare.com/data', DEFAULT_DERIVATION_PATH: `44'/60'/0'`, // WARNING: ZRX & WETH MUST always be default trackedTokens DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'], -- cgit v1.2.3 From fb31c493176ec952f6c3621348e328e38ade2174 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sun, 22 Apr 2018 13:27:16 -0400 Subject: Refactor common fetch logic into fetch_utils --- packages/website/ts/components/wallet/wallet.tsx | 39 +++++++++++--------- packages/website/ts/utils/backend_client.ts | 46 ++++-------------------- packages/website/ts/utils/fetch_utils.ts | 33 +++++++++++++++++ 3 files changed, 62 insertions(+), 56 deletions(-) create mode 100644 packages/website/ts/utils/fetch_utils.ts (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 44bf69455..3f0a4fdca 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -40,6 +40,7 @@ import { backendClient } from 'ts/utils/backend_client'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; +import { fetchUtils } from 'ts/utils/fetch_utils'; import { utils } from 'ts/utils/utils'; import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles'; @@ -130,6 +131,7 @@ const FOOTER_ITEM_KEY = 'FOOTER'; const DISCONNECTED_ITEM_KEY = 'DISCONNECTED'; const ETHER_ITEM_KEY = 'ETHER'; const USD_DECIMAL_PLACES = 2; +const CRYPTO_COMPARE_MULTI_ENDPOINT = '/pricemulti'; export class Wallet extends React.Component { private _isUnmounted: boolean; @@ -511,24 +513,29 @@ export class Wallet extends React.Component { ), ); const joinedTokenSymbols = _.keys(tokenAddressBySymbol).join(','); - const url = `${configs.CRYPTO_COMPARE_BASE_URL}/pricemulti?fsyms=${joinedTokenSymbols}&tsyms=USD`; - const response = await fetch(url); - if (response.status !== 200) { - const errorText = `Error requesting url: ${url}, ${response.status}: ${response.statusText}`; - logUtils.log(errorText); - const error = Error(errorText); - // tslint:disable-next-line:no-floating-promises - errorReporter.reportAsync(error); + const baseCurrency = 'USD'; + const queryParams = { + fsyms: joinedTokenSymbols, + tsyms: baseCurrency, + }; + try { + const priceInfoBySymbol = await fetchUtils.requestAsync( + configs.CRYPTO_COMPARE_BASE_URL, + CRYPTO_COMPARE_MULTI_ENDPOINT, + queryParams, + ); + const priceInfoByAddress = _.mapKeys(priceInfoBySymbol, (value, symbol) => + _.get(tokenAddressBySymbol, symbol), + ); + const result = _.mapValues(priceInfoByAddress, priceInfo => { + const price = _.get(priceInfo, baseCurrency); + const priceBigNumber = new BigNumber(price); + return priceBigNumber; + }); + return result; + } catch (err) { return {}; } - const priceInfoBySymbol = await response.json(); - const priceInfoByAddress = _.mapKeys(priceInfoBySymbol, (value, symbol) => _.get(tokenAddressBySymbol, symbol)); - const result = _.mapValues(priceInfoByAddress, priceInfo => { - const price = _.get(priceInfo, 'USD'); - const priceBigNumber = new BigNumber(price); - return priceBigNumber; - }); - return result; } private _openWrappedEtherActionRow(wrappedEtherDirection: Side) { this.setState({ diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts index fdbb3e03a..ab0fb4f32 100644 --- a/packages/website/ts/utils/backend_client.ts +++ b/packages/website/ts/utils/backend_client.ts @@ -1,16 +1,8 @@ -import { BigNumber, logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; -import * as queryString from 'query-string'; -import { - ArticlesBySection, - ItemByAddress, - WebsiteBackendGasInfo, - WebsiteBackendPriceInfo, - WebsiteBackendRelayerInfo, -} from 'ts/types'; +import { ArticlesBySection, WebsiteBackendGasInfo, WebsiteBackendPriceInfo, WebsiteBackendRelayerInfo } from 'ts/types'; import { configs } from 'ts/utils/configs'; -import { errorReporter } from 'ts/utils/error_reporter'; +import { fetchUtils } from 'ts/utils/fetch_utils'; const ETH_GAS_STATION_ENDPOINT = '/eth_gas_station'; const PRICES_ENDPOINT = '/prices'; @@ -19,7 +11,7 @@ const WIKI_ENDPOINT = '/wiki'; export const backendClient = { async getGasInfoAsync(): Promise { - const result = await requestAsync(ETH_GAS_STATION_ENDPOINT); + const result = await fetchUtils.requestAsync(configs.BACKEND_BASE_URL, ETH_GAS_STATION_ENDPOINT); return result; }, async getPriceInfosAsync(tokenAddresses: string[]): Promise { @@ -30,41 +22,15 @@ export const backendClient = { const queryParams = { tokens: joinedTokenAddresses, }; - const result = await requestAsync(PRICES_ENDPOINT, queryParams); + const result = await fetchUtils.requestAsync(configs.BACKEND_BASE_URL, PRICES_ENDPOINT, queryParams); return result; }, async getRelayerInfosAsync(): Promise { - const result = await requestAsync(RELAYERS_ENDPOINT); + const result = await fetchUtils.requestAsync(configs.BACKEND_BASE_URL, RELAYERS_ENDPOINT); return result; }, async getWikiArticlesBySectionAsync(): Promise { - const result = await requestAsync(WIKI_ENDPOINT); + const result = await fetchUtils.requestAsync(configs.BACKEND_BASE_URL, WIKI_ENDPOINT); return result; }, }; - -async function requestAsync(endpoint: string, queryParams?: object): Promise { - const query = queryStringFromQueryParams(queryParams); - const url = `${configs.BACKEND_BASE_URL}${endpoint}${query}`; - const response = await fetch(url); - if (response.status !== 200) { - const errorText = `Error requesting url: ${url}, ${response.status}: ${response.statusText}`; - logUtils.log(errorText); - const error = Error(errorText); - // tslint:disable-next-line:no-floating-promises - errorReporter.reportAsync(error); - throw error; - } - const result = await response.json(); - return result; -} - -function queryStringFromQueryParams(queryParams?: object): string { - // if params are undefined or empty, return an empty string - if (_.isUndefined(queryParams) || _.isEmpty(queryParams)) { - return ''; - } - // stringify the formatted object - const stringifiedParams = queryString.stringify(queryParams); - return `?${stringifiedParams}`; -} diff --git a/packages/website/ts/utils/fetch_utils.ts b/packages/website/ts/utils/fetch_utils.ts new file mode 100644 index 000000000..d2e902db5 --- /dev/null +++ b/packages/website/ts/utils/fetch_utils.ts @@ -0,0 +1,33 @@ +import { logUtils } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as queryString from 'query-string'; + +import { errorReporter } from 'ts/utils/error_reporter'; + +export const fetchUtils = { + async requestAsync(baseUrl: string, path: string, queryParams?: object): Promise { + const query = queryStringFromQueryParams(queryParams); + const url = `${baseUrl}${path}${query}`; + const response = await fetch(url); + if (response.status !== 200) { + const errorText = `Error requesting url: ${url}, ${response.status}: ${response.statusText}`; + logUtils.log(errorText); + const error = Error(errorText); + // tslint:disable-next-line:no-floating-promises + errorReporter.reportAsync(error); + throw error; + } + const result = await response.json(); + return result; + }, +}; + +function queryStringFromQueryParams(queryParams?: object): string { + // if params are undefined or empty, return an empty string + if (_.isUndefined(queryParams) || _.isEmpty(queryParams)) { + return ''; + } + // stringify the formatted object + const stringifiedParams = queryString.stringify(queryParams); + return `?${stringifiedParams}`; +} -- cgit v1.2.3 From 121b6949a1e185c0cc9768c2b7042eb3daf124b7 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sun, 22 Apr 2018 14:05:03 -0400 Subject: Rate limit crypto compare calls --- packages/website/ts/components/wallet/wallet.tsx | 28 +++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 3f0a4fdca..231045bc5 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -132,9 +132,12 @@ const DISCONNECTED_ITEM_KEY = 'DISCONNECTED'; const ETHER_ITEM_KEY = 'ETHER'; const USD_DECIMAL_PLACES = 2; const CRYPTO_COMPARE_MULTI_ENDPOINT = '/pricemulti'; +// Crypto compare recommends requesting no more than once every 10s: https://www.cryptocompare.com/api/?javascript#requests +const CRYPTO_COMPARE_UPDATE_INTERVAL_MS = 10 * 1000; export class Wallet extends React.Component { private _isUnmounted: boolean; + private _cryptoCompareLastFetchTimestampMs?: number; constructor(props: WalletProps) { super(props); this._isUnmounted = false; @@ -465,16 +468,27 @@ export class Wallet extends React.Component { ); balanceAndAllowanceTupleByAddress[tokenAddress] = balanceAndAllowanceTuple; } - const pricesByAddress = await this._getPricesByAddressAsync(tokenAddresses); + // if we are allowed to fetch prices do so, if not, keep the old price state + const canFetchPrices = this._canGetPrice(); + let priceByAddress: ItemByAddress = {}; + if (canFetchPrices) { + priceByAddress = await this._getPricesByAddressAsync(tokenAddresses); + } else { + const cachedPricesByAddress = _.mapValues( + this.state.trackedTokenStateByAddress, + tokenState => tokenState.price, + ); + priceByAddress = cachedPricesByAddress; + } const trackedTokenStateByAddress = _.reduce( tokenAddresses, (acc, address) => { const [balance, allowance] = balanceAndAllowanceTupleByAddress[address]; - const price = pricesByAddress[address]; + const priceIfExists = _.get(priceByAddress, address); acc[address] = { balance, allowance, - price, + price: priceIfExists, isLoaded: true, }; return acc; @@ -519,6 +533,7 @@ export class Wallet extends React.Component { tsyms: baseCurrency, }; try { + this._cryptoCompareLastFetchTimestampMs = Date.now(); const priceInfoBySymbol = await fetchUtils.requestAsync( configs.CRYPTO_COMPARE_BASE_URL, CRYPTO_COMPARE_MULTI_ENDPOINT, @@ -537,6 +552,13 @@ export class Wallet extends React.Component { return {}; } } + private _canGetPrice() { + const currentTimeStamp = Date.now(); + const result = + _.isUndefined(this._cryptoCompareLastFetchTimestampMs) || + this._cryptoCompareLastFetchTimestampMs + CRYPTO_COMPARE_UPDATE_INTERVAL_MS < currentTimeStamp; + return result; + } private _openWrappedEtherActionRow(wrappedEtherDirection: Side) { this.setState({ wrappedEtherDirection, -- cgit v1.2.3 From 96568957263858c6832ae972f9df5f02913549c6 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sun, 22 Apr 2018 14:17:34 -0400 Subject: Remove some unused imports --- packages/website/ts/components/wallet/wallet.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 231045bc5..257ea8ac4 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -6,7 +6,7 @@ import { Styles, utils as sharedUtils, } from '@0xproject/react-shared'; -import { BigNumber, logUtils } from '@0xproject/utils'; +import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import FlatButton from 'material-ui/FlatButton'; import { List, ListItem } from 'material-ui/List'; @@ -39,7 +39,6 @@ import { import { backendClient } from 'ts/utils/backend_client'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; -import { errorReporter } from 'ts/utils/error_reporter'; import { fetchUtils } from 'ts/utils/fetch_utils'; import { utils } from 'ts/utils/utils'; import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles'; -- cgit v1.2.3 From 9b535e3cec4073205c1343306829bcdec3168002 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Sun, 22 Apr 2018 14:20:42 -0400 Subject: Rename baseCurrency to quoteCurrency --- packages/website/ts/components/wallet/wallet.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 257ea8ac4..2097c0eab 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -526,10 +526,10 @@ export class Wallet extends React.Component { ), ); const joinedTokenSymbols = _.keys(tokenAddressBySymbol).join(','); - const baseCurrency = 'USD'; + const quoteCurrency = 'USD'; const queryParams = { fsyms: joinedTokenSymbols, - tsyms: baseCurrency, + tsyms: quoteCurrency, }; try { this._cryptoCompareLastFetchTimestampMs = Date.now(); @@ -542,7 +542,7 @@ export class Wallet extends React.Component { _.get(tokenAddressBySymbol, symbol), ); const result = _.mapValues(priceInfoByAddress, priceInfo => { - const price = _.get(priceInfo, baseCurrency); + const price = _.get(priceInfo, quoteCurrency); const priceBigNumber = new BigNumber(price); return priceBigNumber; }); -- cgit v1.2.3 From feb7dfffa107211e76b87d2028b8c821e914f0d6 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 27 Apr 2018 14:59:22 -0700 Subject: Move quote currency string into config --- packages/website/ts/components/wallet/wallet.tsx | 5 ++--- packages/website/ts/utils/configs.ts | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 2097c0eab..33b8bbffb 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -526,10 +526,9 @@ export class Wallet extends React.Component { ), ); const joinedTokenSymbols = _.keys(tokenAddressBySymbol).join(','); - const quoteCurrency = 'USD'; const queryParams = { fsyms: joinedTokenSymbols, - tsyms: quoteCurrency, + tsyms: configs.FIAT_QUOTE_CURRENCY_SYMBOL, }; try { this._cryptoCompareLastFetchTimestampMs = Date.now(); @@ -542,7 +541,7 @@ export class Wallet extends React.Component { _.get(tokenAddressBySymbol, symbol), ); const result = _.mapValues(priceInfoByAddress, priceInfo => { - const price = _.get(priceInfo, quoteCurrency); + const price = _.get(priceInfo, configs.FIAT_QUOTE_CURRENCY_SYMBOL); const priceBigNumber = new BigNumber(price); return priceBigNumber; }); diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index edfd04291..a214593d4 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -14,6 +14,7 @@ export const configs = { BASE_URL, BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', CRYPTO_COMPARE_BASE_URL: 'https://min-api.cryptocompare.com/data', + FIAT_QUOTE_CURRENCY_SYMBOL: 'USD', DEFAULT_DERIVATION_PATH: `44'/60'/0'`, // WARNING: ZRX & WETH MUST always be default trackedTokens DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'], -- cgit v1.2.3 From 2403323463be66166dae9f8ca7903caab2546717 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 27 Apr 2018 15:01:19 -0700 Subject: Style suggestions --- packages/website/ts/components/wallet/wallet.tsx | 25 ++++++++++-------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 33b8bbffb..51399ae0b 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -510,21 +510,16 @@ export class Wallet extends React.Component { } // for each input token address, search for the corresponding symbol in this.props.tokenByAddress, if it exists // create a mapping from existing symbols -> address - const tokenAddressBySymbol = _.fromPairs( - _.compact( - _.map(tokenAddresses, address => { - const tokenIfExists = _.get(this.props.tokenByAddress, address); - if (!_.isUndefined(tokenIfExists)) { - const symbol = tokenIfExists.symbol; - // the crypto compare api doesn't understand 'WETH' so we need to replace it with 'ETH' - const key = symbol === ETHER_TOKEN_SYMBOL ? ETHER_SYMBOL : symbol; - return [key, address]; - } else { - return undefined; - } - }), - ), - ); + const tokenAddressBySymbol: { [symbol: string]: string } = {}; + _.each(tokenAddresses, address => { + const tokenIfExists = _.get(this.props.tokenByAddress, address); + if (!_.isUndefined(tokenIfExists)) { + const symbol = tokenIfExists.symbol; + // the crypto compare api doesn't understand 'WETH' so we need to replace it with 'ETH' + const key = symbol === ETHER_TOKEN_SYMBOL ? ETHER_SYMBOL : symbol; + tokenAddressBySymbol[key] = address; + } + }); const joinedTokenSymbols = _.keys(tokenAddressBySymbol).join(','); const queryParams = { fsyms: joinedTokenSymbols, -- cgit v1.2.3 From 1131d66b3db2bde04b5108d710801fab7eaf0ede Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 30 Apr 2018 16:36:42 -0700 Subject: Hit website backend for price information --- packages/website/ts/components/wallet/wallet.tsx | 46 ++++-------------------- packages/website/ts/types.ts | 3 +- packages/website/ts/utils/backend_client.ts | 10 +++--- packages/website/ts/utils/configs.ts | 2 -- 4 files changed, 12 insertions(+), 49 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 51399ae0b..9022eb1c9 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -37,9 +37,7 @@ import { TokenStateByAddress, } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; -import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; -import { fetchUtils } from 'ts/utils/fetch_utils'; import { utils } from 'ts/utils/utils'; import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles'; @@ -130,13 +128,9 @@ const FOOTER_ITEM_KEY = 'FOOTER'; const DISCONNECTED_ITEM_KEY = 'DISCONNECTED'; const ETHER_ITEM_KEY = 'ETHER'; const USD_DECIMAL_PLACES = 2; -const CRYPTO_COMPARE_MULTI_ENDPOINT = '/pricemulti'; -// Crypto compare recommends requesting no more than once every 10s: https://www.cryptocompare.com/api/?javascript#requests -const CRYPTO_COMPARE_UPDATE_INTERVAL_MS = 10 * 1000; export class Wallet extends React.Component { private _isUnmounted: boolean; - private _cryptoCompareLastFetchTimestampMs?: number; constructor(props: WalletProps) { super(props); this._isUnmounted = false; @@ -467,18 +461,7 @@ export class Wallet extends React.Component { ); balanceAndAllowanceTupleByAddress[tokenAddress] = balanceAndAllowanceTuple; } - // if we are allowed to fetch prices do so, if not, keep the old price state - const canFetchPrices = this._canGetPrice(); - let priceByAddress: ItemByAddress = {}; - if (canFetchPrices) { - priceByAddress = await this._getPricesByAddressAsync(tokenAddresses); - } else { - const cachedPricesByAddress = _.mapValues( - this.state.trackedTokenStateByAddress, - tokenState => tokenState.price, - ); - priceByAddress = cachedPricesByAddress; - } + const priceByAddress = await this._getPriceByAddressAsync(tokenAddresses); const trackedTokenStateByAddress = _.reduce( tokenAddresses, (acc, address) => { @@ -504,7 +487,7 @@ export class Wallet extends React.Component { private async _refetchTokenStateAsync(tokenAddress: string) { await this._fetchBalancesAndAllowancesAsync([tokenAddress]); } - private async _getPricesByAddressAsync(tokenAddresses: string[]): Promise> { + private async _getPriceByAddressAsync(tokenAddresses: string[]): Promise> { if (_.isEmpty(tokenAddresses)) { return {}; } @@ -520,23 +503,13 @@ export class Wallet extends React.Component { tokenAddressBySymbol[key] = address; } }); - const joinedTokenSymbols = _.keys(tokenAddressBySymbol).join(','); - const queryParams = { - fsyms: joinedTokenSymbols, - tsyms: configs.FIAT_QUOTE_CURRENCY_SYMBOL, - }; + const tokenSymbols = _.keys(tokenAddressBySymbol); try { - this._cryptoCompareLastFetchTimestampMs = Date.now(); - const priceInfoBySymbol = await fetchUtils.requestAsync( - configs.CRYPTO_COMPARE_BASE_URL, - CRYPTO_COMPARE_MULTI_ENDPOINT, - queryParams, - ); - const priceInfoByAddress = _.mapKeys(priceInfoBySymbol, (value, symbol) => + const priceBySymbol = await backendClient.getPriceInfoAsync(tokenSymbols); + const priceByAddress = _.mapKeys(priceBySymbol, (value, symbol) => _.get(tokenAddressBySymbol, symbol), ); - const result = _.mapValues(priceInfoByAddress, priceInfo => { - const price = _.get(priceInfo, configs.FIAT_QUOTE_CURRENCY_SYMBOL); + const result = _.mapValues(priceByAddress, price => { const priceBigNumber = new BigNumber(price); return priceBigNumber; }); @@ -545,13 +518,6 @@ export class Wallet extends React.Component { return {}; } } - private _canGetPrice() { - const currentTimeStamp = Date.now(); - const result = - _.isUndefined(this._cryptoCompareLastFetchTimestampMs) || - this._cryptoCompareLastFetchTimestampMs + CRYPTO_COMPARE_UPDATE_INTERVAL_MS < currentTimeStamp; - return result; - } private _openWrappedEtherActionRow(wrappedEtherDirection: Side) { this.setState({ wrappedEtherDirection, diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 2544e6735..f4fddb556 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -509,8 +509,7 @@ export interface WebsiteBackendRelayerInfo { } export interface WebsiteBackendPriceInfo { - price: string; - address: string; + [symbol: string]: string; } export interface WebsiteBackendGasInfo { diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts index ab0fb4f32..63e06fda7 100644 --- a/packages/website/ts/utils/backend_client.ts +++ b/packages/website/ts/utils/backend_client.ts @@ -14,13 +14,13 @@ export const backendClient = { const result = await fetchUtils.requestAsync(configs.BACKEND_BASE_URL, ETH_GAS_STATION_ENDPOINT); return result; }, - async getPriceInfosAsync(tokenAddresses: string[]): Promise { - if (_.isEmpty(tokenAddresses)) { - return []; + async getPriceInfoAsync(tokenSymbols: string[]): Promise { + if (_.isEmpty(tokenSymbols)) { + return {}; } - const joinedTokenAddresses = tokenAddresses.join(','); + const joinedTokenSymbols = tokenSymbols.join(','); const queryParams = { - tokens: joinedTokenAddresses, + tokens: joinedTokenSymbols, }; const result = await fetchUtils.requestAsync(configs.BACKEND_BASE_URL, PRICES_ENDPOINT, queryParams); return result; diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index a214593d4..a54fc56a8 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -13,8 +13,6 @@ export const configs = { BACKEND_BASE_URL: 'https://website-api.0xproject.com', BASE_URL, BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', - CRYPTO_COMPARE_BASE_URL: 'https://min-api.cryptocompare.com/data', - FIAT_QUOTE_CURRENCY_SYMBOL: 'USD', DEFAULT_DERIVATION_PATH: `44'/60'/0'`, // WARNING: ZRX & WETH MUST always be default trackedTokens DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'], -- cgit v1.2.3 From 3a1f9d01e8cdb40fbb293de10046fa654434dde4 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 30 Apr 2018 16:49:47 -0700 Subject: Remove WETH special case, website backend handles this now --- packages/website/ts/components/wallet/wallet.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 9022eb1c9..057c712e5 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -498,17 +498,13 @@ export class Wallet extends React.Component { const tokenIfExists = _.get(this.props.tokenByAddress, address); if (!_.isUndefined(tokenIfExists)) { const symbol = tokenIfExists.symbol; - // the crypto compare api doesn't understand 'WETH' so we need to replace it with 'ETH' - const key = symbol === ETHER_TOKEN_SYMBOL ? ETHER_SYMBOL : symbol; - tokenAddressBySymbol[key] = address; + tokenAddressBySymbol[symbol] = address; } }); const tokenSymbols = _.keys(tokenAddressBySymbol); try { const priceBySymbol = await backendClient.getPriceInfoAsync(tokenSymbols); - const priceByAddress = _.mapKeys(priceBySymbol, (value, symbol) => - _.get(tokenAddressBySymbol, symbol), - ); + const priceByAddress = _.mapKeys(priceBySymbol, (value, symbol) => _.get(tokenAddressBySymbol, symbol)); const result = _.mapValues(priceByAddress, price => { const priceBigNumber = new BigNumber(price); return priceBigNumber; -- cgit v1.2.3 From f08738e13379ea86a33ad9be9f7579e637e69acb Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 25 Apr 2018 15:41:30 -0700 Subject: Add Greg and Remco to the about page --- packages/website/ts/pages/about/about.tsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 97be59526..d78cca703 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -113,7 +113,7 @@ const teamRow4: ProfileInfo[] = [ { name: 'Blake Henderson', title: 'Operations Associate', - description: `Operations and Analytics. Previously analytics at LinkedIn. Economics at UC San Diego. `, + description: `Operations and Analytics. Previously analytics at LinkedIn. Economics at UC San Diego.`, image: '/images/team/blake.jpg', linkedIn: 'https://www.linkedin.com/in/blakerhenderson/', github: '', @@ -130,6 +130,27 @@ const teamRow4: ProfileInfo[] = [ }, ]; +const teamRow5: ProfileInfo[] = [ + { + name: 'Greg Hysen', + title: 'Blockchain Engineer', + description: `Smart contract R&D. Previously lead distributed systems engineer at Hivemapper. Computer Science at University of Waterloo.`, + image: '/images/team/greg.jpeg', + linkedIn: 'https://www.linkedin.com/in/gregory-hysen-71741463/', + github: 'https://github.com/hysz', + medium: '', + }, + { + name: 'Remco Bloemen', + title: 'Technical Fellow', + description: `Previously cofounder at Neufund and Coblue. Part III at Cambridge. PhD dropout at Twente Business School.`, + image: '/images/team/remco.jpeg', + linkedIn: 'https://www.linkedin.com/in/remcobloemen/', + github: 'http://github.com/recmo', + medium: '', + }, +]; + const advisors: ProfileInfo[] = [ { name: 'Fred Ehrsam', @@ -223,6 +244,7 @@ export class About extends React.Component {
{this._renderProfiles(teamRow2)}
{this._renderProfiles(teamRow3)}
{this._renderProfiles(teamRow4)}
+
{this._renderProfiles(teamRow5)}
Date: Tue, 24 Apr 2018 16:36:35 +0200 Subject: Move order utils to @0xproject/order-utils --- .../ts/containers/order_utils_documentation.ts | 99 ++++++++++++++++++++++ packages/website/ts/index.tsx | 7 ++ .../website/ts/pages/documentation/doc_page.tsx | 1 + packages/website/ts/types.ts | 2 + packages/website/ts/utils/utils.ts | 3 +- 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 packages/website/ts/containers/order_utils_documentation.ts (limited to 'packages/website/ts') diff --git a/packages/website/ts/containers/order_utils_documentation.ts b/packages/website/ts/containers/order_utils_documentation.ts new file mode 100644 index 000000000..64aa7300f --- /dev/null +++ b/packages/website/ts/containers/order_utils_documentation.ts @@ -0,0 +1,99 @@ +import { constants as docConstants, DocsInfo, DocsInfoConfig, SupportedDocJson } from '@0xproject/react-docs'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { State } from 'ts/redux/reducer'; +import { DocPackages, Environments, WebsitePaths } from 'ts/types'; +import { configs } from 'ts/utils/configs'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; + +/* tslint:disable:no-var-requires */ +const IntroMarkdown = require('md/docs/order_utils/introduction'); +const InstallationMarkdown = require('md/docs/order_utils/installation'); +/* tslint:enable:no-var-requires */ + +const docSections = { + introduction: 'introduction', + installation: 'installation', + usage: 'usage', + types: 'types', +}; + +const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.OrderUtils, + type: SupportedDocJson.TypeDoc, + displayName: 'Order utils', + packageUrl: 'https://github.com/0xProject/0x-monorepo', + menu: { + introduction: [docSections.introduction], + install: [docSections.installation], + usage: [docSections.usage], + types: [docSections.types], + }, + sectionNameToMarkdown: { + [docSections.introduction]: IntroMarkdown, + [docSections.installation]: InstallationMarkdown, + }, + sectionNameToModulePath: { + [docSections.usage]: [ + '"order-utils/src/order_hash"', + '"order-utils/src/signature_utils"', + '"order-utils/src/order_factory"', + '"order-utils/src/salt"', + '"order-utils/src/assert"', + '"order-utils/src/constants"', + ], + [docSections.types]: ['"order-utils/src/types"', '"types/src/index"'], + }, + menuSubsectionToVersionWhenIntroduced: {}, + sections: docSections, + visibleConstructors: [], + typeConfigs: { + // Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is + // currently no way to extract the re-exported types from index.ts via TypeDoc :( + publicTypes: [ + 'OrderError', + 'Order', + 'SignedOrder', + 'ECSignature', + 'Provider', + 'JSONRPCRequestPayload', + 'JSONRPCResponsePayload', + 'JSONRPCErrorCallback', + ], + typeNameToExternalLink: { + BigNumber: constants.URL_BIGNUMBERJS_GITHUB, + }, + }, +}; +const docsInfo = new DocsInfo(docsInfoConfig); + +interface ConnectedState { + docsVersion: string; + availableDocVersions: string[]; + docsInfo: DocsInfo; + translate: Translate; +} + +interface ConnectedDispatch { + dispatcher: Dispatcher; +} + +const mapStateToProps = (state: State, ownProps: DocPageProps): ConnectedState => ({ + docsVersion: state.docsVersion, + availableDocVersions: state.availableDocVersions, + translate: state.translate, + docsInfo, +}); + +const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ + dispatcher: new Dispatcher(dispatch), +}); + +export const Documentation: React.ComponentClass = connect(mapStateToProps, mapDispatchToProps)( + DocPageComponent, +); diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index d99187151..9535dd222 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -61,6 +61,9 @@ const LazySolCovDocumentation = createLazyComponent('Documentation', async () => const LazySubprovidersDocumentation = createLazyComponent('Documentation', async () => System.import(/* webpackChunkName: "subproviderDocs" */ 'ts/containers/subproviders_documentation'), ); +const LazyOrderUtilsDocumentation = createLazyComponent('Documentation', async () => + System.import(/* webpackChunkName: "orderUtilsDocs" */ 'ts/containers/order_utils_documentation'), +); analytics.init(); // tslint:disable-next-line:no-floating-promises @@ -93,6 +96,10 @@ render( path={`${WebsitePaths.Subproviders}/:version?`} component={LazySubprovidersDocumentation} /> + Date: Wed, 2 May 2018 17:03:36 -0700 Subject: Remove id property from WebsiteBackendRelayerInfo --- packages/website/ts/components/relayer_index/relayer_index.tsx | 2 +- packages/website/ts/types.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index 50760c32d..c1ab4227a 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -66,7 +66,7 @@ export class RelayerIndex extends React.Component {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo) => ( diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index fb4f22873..05adca460 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -504,7 +504,6 @@ export interface TokenState { // TODO: Add topTokens and headerUrl properties once available from backend export interface WebsiteBackendRelayerInfo { - id: string; name: string; dailyTxnVolume: string; url: string; -- cgit v1.2.3 From 528008b1a9d6d74434b34568bb4c3ba29a355b0c Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 3 May 2018 12:35:07 +0200 Subject: Add new section to homepage --- packages/website/ts/pages/landing/landing.tsx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'packages/website/ts') diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index f961220fd..a63c24fb7 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -37,6 +37,8 @@ interface Project { } const THROTTLE_TIMEOUT = 100; +const WHATS_NEW_TITLE = '18 ideas for 0x relayers in 2018'; +const WHATS_NEW_URL = 'https://blog.0xproject.com/18-ideas-for-0x-relayers-in-2018-80a1498b955f'; const relayersAndDappProjects: Project[] = [ { @@ -233,6 +235,7 @@ export class Landing extends React.Component { return (
+ {this._renderWhatsNew()}
@@ -302,6 +305,28 @@ export class Landing extends React.Component {
); } + private _renderWhatsNew() { + return ( + + ); + } private _renderProjects(projects: Project[], title: string, backgroundColor: string, isTitleCenter: boolean) { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const projectList = _.map(projects, (project: Project, i: number) => { -- cgit v1.2.3 From cf9555debc445f6645cfdf4e9835247abc084638 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 3 May 2018 12:35:21 +0200 Subject: Fix logo left padding on mobile --- packages/website/ts/components/top_bar/top_bar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 0c32f4c62..95e6276bf 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -193,7 +193,7 @@ export class TopBar extends React.Component { return (
-
+
-- cgit v1.2.3 From fe0f4ae25759eb6b6cad8c6608ebff526366c059 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 2 May 2018 20:42:17 -0700 Subject: Add header images to relayer grid tiles --- .../website/ts/components/relayer_index/relayer_grid_tile.tsx | 8 +++++--- packages/website/ts/types.ts | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 0c4b2841c..7d0551581 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -12,8 +12,7 @@ export interface RelayerGridTileProps { networkId: number; } -// TODO: Get top tokens and headerurl from remote -const headerUrl = '/images/og_image.png'; +// TODO: Get top tokens from remote const topTokens = [ { address: '0x1dad4783cf3fe3085c1426157ab175a6119a04ba', @@ -68,6 +67,9 @@ const styles: Styles = { borderBottomLeftRadius: 4, borderTopRightRadius: 4, borderTopLeftRadius: 4, + borderWidth: 1, + borderStyle: 'solid', + borderColor: colors.walletBorder, }, body: { paddingLeft: 6, @@ -95,7 +97,7 @@ export const RelayerGridTile: React.StatelessComponent = ( return (
- +
{props.relayerInfo.name} diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 05adca460..484f09e5d 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -502,11 +502,12 @@ export interface TokenState { price?: BigNumber; } -// TODO: Add topTokens and headerUrl properties once available from backend +// TODO: Add topTokens property once available from backend export interface WebsiteBackendRelayerInfo { name: string; dailyTxnVolume: string; url: string; + headerImgUrl: string; } export interface WebsiteBackendPriceInfo { -- cgit v1.2.3 From 6b92ef733c6f1cfd1772433abfdc9fd82227fa8b Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 3 May 2018 13:06:39 -0700 Subject: Open relayer app when clicking on grid tile --- .../components/relayer_index/relayer_grid_tile.tsx | 35 ++++++++++++---------- packages/website/ts/types.ts | 1 + 2 files changed, 20 insertions(+), 16 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 7d0551581..0b9b8165e 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -94,24 +94,27 @@ const styles: Styles = { }; export const RelayerGridTile: React.StatelessComponent = (props: RelayerGridTileProps) => { + const link = props.relayerInfo.appUrl || props.relayerInfo.url; return ( - -
- -
-
- {props.relayerInfo.name} -
-
{props.relayerInfo.dailyTxnVolume}
-
- Daily Trade Volume -
- - - + + ); }; diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 484f09e5d..693077ee6 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -507,6 +507,7 @@ export interface WebsiteBackendRelayerInfo { name: string; dailyTxnVolume: string; url: string; + appUrl?: string; headerImgUrl: string; } -- cgit v1.2.3 From 01dd84dced8d885f6b164cab714ecfde4d0a363e Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 4 May 2018 11:57:34 +0200 Subject: Add order utils docs to the menu --- packages/website/ts/components/top_bar/top_bar.tsx | 6 ++++++ packages/website/ts/types.ts | 1 + 2 files changed, 7 insertions(+) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 95e6276bf..8bea58b0a 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -139,6 +139,12 @@ export class TopBar extends React.Component { primaryText={this.props.translate.get(Key.Web3Wrapper, Deco.CapWords)} /> , + + + , Date: Mon, 23 Apr 2018 21:07:48 -0700 Subject: Lay out wallet and relayers --- packages/website/ts/components/portal/portal.tsx | 214 +++++++++++++++++++++++ packages/website/ts/containers/portal.ts | 88 ++++++++++ packages/website/ts/index.tsx | 11 +- 3 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 packages/website/ts/components/portal/portal.tsx create mode 100644 packages/website/ts/containers/portal.ts (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx new file mode 100644 index 000000000..8a9e89a72 --- /dev/null +++ b/packages/website/ts/components/portal/portal.tsx @@ -0,0 +1,214 @@ +import { colors, Styles } from '@0xproject/react-shared'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import * as DocumentTitle from 'react-document-title'; + +import { Blockchain } from 'ts/blockchain'; +import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; +import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; +import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; +import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; +import { TopBar } from 'ts/components/top_bar/top_bar'; +import { FlashMessage } from 'ts/components/ui/flash_message'; +import { Wallet } from 'ts/components/wallet/wallet'; +import { localStorage } from 'ts/local_storage/local_storage'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; +import { utils } from 'ts/utils/utils'; + +export interface PortalProps { + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + dispatcher: Dispatcher; + hashData: HashData; + injectedProviderName: string; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + providerType: ProviderType; + screenWidth: ScreenWidths; + tokenByAddress: TokenByAddress; + userEtherBalanceInWei: BigNumber; + userAddress: string; + shouldBlockchainErrDialogBeOpen: boolean; + userSuppliedOrderCache: Order; + location: Location; + flashMessage?: string | React.ReactNode; + lastForceTokenStateRefetch: number; + translate: Translate; +} + +interface PortalState { + prevNetworkId: number; + prevNodeVersion: string; + prevUserAddress: string; + prevPathname: string; + isDisclaimerDialogOpen: boolean; + isLedgerDialogOpen: boolean; +} + +const THROTTLE_TIMEOUT = 100; +const TOP_BAR_HEIGHT = 60; + +const styles: Styles = { + root: { + width: '100%', + height: '100%', + backgroundColor: colors.lightestGrey, + }, + body: { + height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + }, +}; + +export class Portal extends React.Component { + private _blockchain: Blockchain; + private _throttledScreenWidthUpdate: () => void; + constructor(props: PortalProps) { + super(props); + this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER); + const hasAcceptedDisclaimer = + !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer); + this.state = { + prevNetworkId: this.props.networkId, + prevNodeVersion: this.props.nodeVersion, + prevUserAddress: this.props.userAddress, + prevPathname: this.props.location.pathname, + isDisclaimerDialogOpen: !hasAcceptedDisclaimer, + isLedgerDialogOpen: false, + }; + } + public componentDidMount() { + window.addEventListener('resize', this._throttledScreenWidthUpdate); + window.scrollTo(0, 0); + } + public componentWillMount() { + this._blockchain = new Blockchain(this.props.dispatcher); + } + public componentWillUnmount() { + this._blockchain.destroy(); + window.removeEventListener('resize', this._throttledScreenWidthUpdate); + // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered + // the initialization process always occurs from the same base state. This helps avoid + // initialization inconsistencies (i.e While the portal was unrendered, the user might have + // become disconnected from their backing Ethereum node, changes user accounts, etc...) + this.props.dispatcher.resetState(); + } + public componentWillReceiveProps(nextProps: PortalProps) { + if (nextProps.networkId !== this.state.prevNetworkId) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId); + this.setState({ + prevNetworkId: nextProps.networkId, + }); + } + if (nextProps.userAddress !== this.state.prevUserAddress) { + const newUserAddress = _.isEmpty(nextProps.userAddress) ? undefined : nextProps.userAddress; + // tslint:disable-next-line:no-floating-promises + this._blockchain.userAddressUpdatedFireAndForgetAsync(newUserAddress); + this.setState({ + prevUserAddress: nextProps.userAddress, + }); + } + if (nextProps.nodeVersion !== this.state.prevNodeVersion) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion); + } + if (nextProps.location.pathname !== this.state.prevPathname) { + this.setState({ + prevPathname: nextProps.location.pathname, + }); + } + } + public render() { + const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( + this.props.dispatcher, + ); + const allTokens = _.values(this.props.tokenByAddress); + const trackedTokens = _.filter(allTokens, t => t.isTracked); + return ( +
+ + +
+
+
+ +
+
+ +
+
+ + + + {this.props.blockchainIsLoaded && ( + + )} +
+
+ ); + } + private _onToggleLedgerDialog() { + this.setState({ + isLedgerDialogOpen: !this.state.isLedgerDialogOpen, + }); + } + private _onPortalDisclaimerAccepted() { + localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set'); + this.setState({ + isDisclaimerDialogOpen: false, + }); + } + private _updateScreenWidth() { + const newScreenWidth = utils.getScreenWidth(); + this.props.dispatcher.updateScreenWidth(newScreenWidth); + } +} diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts new file mode 100644 index 000000000..3f0feb6e9 --- /dev/null +++ b/packages/website/ts/containers/portal.ts @@ -0,0 +1,88 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { Portal as PortalComponent, PortalProps as PortalComponentProps } from 'ts/components/portal/portal'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { State } from 'ts/redux/reducer'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; + +interface ConnectedState { + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + hashData: HashData; + injectedProviderName: string; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + providerType: ProviderType; + tokenByAddress: TokenByAddress; + lastForceTokenStateRefetch: number; + userEtherBalanceInWei: BigNumber; + screenWidth: ScreenWidths; + shouldBlockchainErrDialogBeOpen: boolean; + userAddress: string; + userSuppliedOrderCache: Order; + flashMessage?: string | React.ReactNode; + translate: Translate; +} + +interface ConnectedDispatch { + dispatcher: Dispatcher; +} + +const mapStateToProps = (state: State, ownProps: PortalComponentProps): ConnectedState => { + const receiveAssetToken = state.sideToAssetToken[Side.Receive]; + const depositAssetToken = state.sideToAssetToken[Side.Deposit]; + const receiveAddress = !_.isUndefined(receiveAssetToken.address) + ? receiveAssetToken.address + : constants.NULL_ADDRESS; + const depositAddress = !_.isUndefined(depositAssetToken.address) + ? depositAssetToken.address + : constants.NULL_ADDRESS; + const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0); + const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0); + const hashData = { + depositAmount, + depositTokenContractAddr: depositAddress, + feeRecipientAddress: constants.NULL_ADDRESS, + makerFee: constants.MAKER_FEE, + orderExpiryTimestamp: state.orderExpiryTimestamp, + orderMakerAddress: state.userAddress, + orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS, + receiveAmount, + receiveTokenContractAddr: receiveAddress, + takerFee: constants.TAKER_FEE, + orderSalt: state.orderSalt, + }; + return { + blockchainErr: state.blockchainErr, + blockchainIsLoaded: state.blockchainIsLoaded, + hashData, + injectedProviderName: state.injectedProviderName, + networkId: state.networkId, + nodeVersion: state.nodeVersion, + orderFillAmount: state.orderFillAmount, + providerType: state.providerType, + screenWidth: state.screenWidth, + shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, + tokenByAddress: state.tokenByAddress, + lastForceTokenStateRefetch: state.lastForceTokenStateRefetch, + userAddress: state.userAddress, + userEtherBalanceInWei: state.userEtherBalanceInWei, + userSuppliedOrderCache: state.userSuppliedOrderCache, + flashMessage: state.flashMessage, + translate: state.translate, + }; +}; + +const mapDispatchToProps = (dispatch: Dispatch): ConnectedDispatch => ({ + dispatcher: new Dispatcher(dispatch), +}); + +export const Portal: React.ComponentClass = connect(mapStateToProps, mapDispatchToProps)( + PortalComponent, +); diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index 9535dd222..1b1255214 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -34,9 +34,14 @@ import 'less/all.less'; // cause we only want to import the module when the user navigates to the page. // At the same time webpack statically parses for System.import() to determine bundle chunk split points // so each lazy import needs it's own `System.import()` declaration. -const LazyPortal = createLazyComponent('LegacyPortal', async () => - System.import(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), -); +const LazyPortal = + utils.isDevelopment() || utils.isStaging() + ? createLazyComponent('Portal', async () => + System.import(/* webpackChunkName: "portal" */ 'ts/containers/portal'), + ) + : createLazyComponent('LegacyPortal', async () => + System.import(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), + ); const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () => System.import(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'), ); -- cgit v1.2.3 From 14b29172b1713610c80ccf5b7390f7b445b0eb75 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 23 Apr 2018 21:45:05 -0700 Subject: Add scrolling to relayer index --- packages/website/ts/components/portal/portal.tsx | 8 +++++++- packages/website/ts/components/relayer_index/relayer_index.tsx | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 8a9e89a72..6939d8905 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -62,6 +62,12 @@ const styles: Styles = { body: { height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, }, + scrollContainer: { + overflowZ: 'hidden', + height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + WebkitOverflowScrolling: 'touch', + overflow: 'auto', + }, }; export class Portal extends React.Component { @@ -165,7 +171,7 @@ export class Portal extends React.Component { onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)} />
-
+
diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index c1ab4227a..e8775e1a5 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -57,7 +57,7 @@ export class RelayerIndex extends React.Component +
Date: Mon, 23 Apr 2018 22:17:26 -0700 Subject: Add headers to wallet and relayer index --- packages/website/ts/components/portal/portal.tsx | 12 +++++++++++- .../website/ts/components/relayer_index/relayer_index.tsx | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 6939d8905..507860ee6 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -68,6 +68,10 @@ const styles: Styles = { WebkitOverflowScrolling: 'touch', overflow: 'auto', }, + title: { + fontWeight: 'bold', + fontSize: 20, + }, }; export class Portal extends React.Component { @@ -154,7 +158,10 @@ export class Portal extends React.Component { />
-
+
+
+ Your Account +
{ />
+
+ Explore 0x Ecosystem +
diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index e8775e1a5..c1ab4227a 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -57,7 +57,7 @@ export class RelayerIndex extends React.Component +
Date: Sat, 5 May 2018 01:26:02 +0200 Subject: Fix type errors in CSS properties --- packages/website/ts/components/top_bar/top_bar.tsx | 2 +- packages/website/ts/components/ui/input_label.tsx | 2 +- packages/website/ts/pages/wiki/wiki.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 8bea58b0a..7fb476ba0 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -56,7 +56,7 @@ const styles: Styles = { width: 70, }, topBar: { - backgroundcolor: colors.white, + backgroundColor: colors.white, height: 59, width: '100%', position: 'relative', diff --git a/packages/website/ts/components/ui/input_label.tsx b/packages/website/ts/components/ui/input_label.tsx index 6a3f26155..8137c0db6 100644 --- a/packages/website/ts/components/ui/input_label.tsx +++ b/packages/website/ts/components/ui/input_label.tsx @@ -17,7 +17,7 @@ const styles = { userSelect: 'none', width: 240, zIndex: 1, - }, + } as React.CSSProperties, }; export const InputLabel = (props: InputLabelProps) => { diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index 7ed2b750d..edca5834d 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -47,7 +47,7 @@ const styles: Styles = { left: 0, bottom: 0, right: 0, - overflowZ: 'hidden', + overflow: 'hidden', height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, WebkitOverflowScrolling: 'touch', }, -- cgit v1.2.3