aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/website/ts')
-rw-r--r--packages/website/ts/blockchain.ts62
-rw-r--r--packages/website/ts/blockchain_watcher.ts6
-rw-r--r--packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx4
-rw-r--r--packages/website/ts/components/eth_weth_conversion_button.tsx4
-rw-r--r--packages/website/ts/components/fill_order.tsx22
-rw-r--r--packages/website/ts/components/fill_warning_dialog.tsx6
-rw-r--r--packages/website/ts/components/forms/subscribe_form.tsx4
-rw-r--r--packages/website/ts/components/generate_order/asset_picker.tsx2
-rw-r--r--packages/website/ts/components/generate_order/generate_order_form.tsx40
-rw-r--r--packages/website/ts/components/inputs/allowance_toggle.tsx16
-rw-r--r--packages/website/ts/components/inputs/balance_bounded_input.tsx4
-rw-r--r--packages/website/ts/components/inputs/expiration_input.tsx2
-rw-r--r--packages/website/ts/components/legacy_portal/legacy_portal.tsx339
-rw-r--r--packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx73
-rw-r--r--packages/website/ts/components/onboarding/portal_onboarding_flow.tsx17
-rw-r--r--packages/website/ts/components/order_json.tsx8
-rw-r--r--packages/website/ts/components/portal/portal.tsx13
-rw-r--r--packages/website/ts/components/relayer_index/relayer_grid_tile.tsx8
-rw-r--r--packages/website/ts/components/relayer_index/relayer_top_tokens.tsx13
-rw-r--r--packages/website/ts/components/send_button.tsx2
-rw-r--r--packages/website/ts/components/token_balances.tsx8
-rw-r--r--packages/website/ts/components/top_bar/top_bar.tsx23
-rw-r--r--packages/website/ts/components/ui/menu_item.tsx2
-rw-r--r--packages/website/ts/components/ui/overlay.tsx2
-rw-r--r--packages/website/ts/components/ui/simple_menu.tsx4
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx10
-rw-r--r--packages/website/ts/components/wallet/wrap_ether_item.tsx33
-rw-r--r--packages/website/ts/containers/legacy_portal.ts92
-rw-r--r--packages/website/ts/containers/zero_ex_js_documentation.ts26
-rw-r--r--packages/website/ts/index.tsx17
-rw-r--r--packages/website/ts/local_storage/local_storage.ts8
-rw-r--r--packages/website/ts/local_storage/tracked_token_storage.ts4
-rw-r--r--packages/website/ts/local_storage/trade_history_storage.tsx18
-rw-r--r--packages/website/ts/pages/about/about.tsx25
-rw-r--r--packages/website/ts/pages/jobs/jobs.tsx2
-rw-r--r--packages/website/ts/pages/landing/landing.tsx2
-rw-r--r--packages/website/ts/pages/wiki/wiki.tsx2
-rw-r--r--packages/website/ts/redux/analyticsMiddleware.ts36
-rw-r--r--packages/website/ts/redux/dispatcher.ts2
-rw-r--r--packages/website/ts/redux/store.ts7
-rw-r--r--packages/website/ts/types.ts9
-rw-r--r--packages/website/ts/utils/analytics.ts104
-rw-r--r--packages/website/ts/utils/backend_client.ts6
-rw-r--r--packages/website/ts/utils/configs.ts10
-rw-r--r--packages/website/ts/utils/constants.ts4
-rw-r--r--packages/website/ts/utils/doc_utils.ts6
-rw-r--r--packages/website/ts/utils/error_reporter.ts33
-rw-r--r--packages/website/ts/utils/fetch_utils.ts9
-rw-r--r--packages/website/ts/utils/utils.ts79
49 files changed, 425 insertions, 803 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index fde134b18..88461f8a9 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -15,8 +15,9 @@ import {
ledgerEthereumBrowserClientFactoryAsync,
LedgerSubprovider,
RedundantSubprovider,
+ RPCSubprovider,
SignerSubprovider,
- Subprovider,
+ Web3ProviderEngine,
} from '@0xproject/subproviders';
import {
BlockParam,
@@ -60,9 +61,7 @@ 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 ProviderEngine = require('web3-provider-engine');
import FilterSubprovider = require('web3-provider-engine/subproviders/filters');
-import RpcSubprovider = require('web3-provider-engine/subproviders/rpc');
import * as MintableArtifacts from '../contracts/Mintable.json';
@@ -84,11 +83,11 @@ export class Blockchain {
public networkId: number;
public nodeVersion: string;
private _contractWrappers: ContractWrappers;
- private _dispatcher: Dispatcher;
+ private readonly _dispatcher: Dispatcher;
private _web3Wrapper?: Web3Wrapper;
private _blockchainWatcher?: BlockchainWatcher;
private _injectedProviderObservable?: InjectedProviderObservable;
- private _injectedProviderUpdateHandler: (update: InjectedProviderUpdate) => Promise<void>;
+ private readonly _injectedProviderUpdateHandler: (update: InjectedProviderUpdate) => Promise<void>;
private _userAddressIfExists: string;
private _ledgerSubprovider: LedgerSubprovider;
private _defaultGasPrice: BigNumber;
@@ -126,7 +125,11 @@ export class Blockchain {
let networkIdIfExists: number;
if (!_.isUndefined(injectedWeb3IfExists)) {
try {
- networkIdIfExists = _.parseInt(await promisify<string>(injectedWeb3IfExists.version.getNetwork)());
+ networkIdIfExists = _.parseInt(
+ await promisify<string>(
+ injectedWeb3IfExists.version.getNetwork.bind(injectedWeb3IfExists.version),
+ )(),
+ );
} catch (err) {
// Ignore error and proceed with networkId undefined
}
@@ -148,7 +151,7 @@ export class Blockchain {
if (!isU2FSupported) {
throw new Error('Cannot update providerType to LEDGER without U2F support');
}
- const provider = new ProviderEngine();
+ const provider = new Web3ProviderEngine();
const ledgerWalletConfigs = {
networkId: networkIdIfExists,
ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync,
@@ -157,25 +160,21 @@ export class Blockchain {
provider.addProvider(ledgerSubprovider);
provider.addProvider(new FilterSubprovider());
const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists], publicNodeUrl => {
- return new RpcSubprovider({
- rpcUrl: publicNodeUrl,
- });
+ return new RPCSubprovider(publicNodeUrl);
});
- provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[]));
+ provider.addProvider(new RedundantSubprovider(rpcSubproviders));
provider.start();
return [provider, ledgerSubprovider];
} else if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) {
// We catch all requests involving a users account and send it to the injectedWeb3
// instance. All other requests go to the public hosted node.
- const provider = new ProviderEngine();
+ const provider = new Web3ProviderEngine();
provider.addProvider(new SignerSubprovider(injectedWeb3.currentProvider));
provider.addProvider(new FilterSubprovider());
const rpcSubproviders = _.map(publicNodeUrlsIfExistsForNetworkId, publicNodeUrl => {
- return new RpcSubprovider({
- rpcUrl: publicNodeUrl,
- });
+ return new RPCSubprovider(publicNodeUrl);
});
- provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[]));
+ provider.addProvider(new RedundantSubprovider(rpcSubproviders));
provider.start();
return [provider, undefined];
} else if (doesInjectedWeb3Exist) {
@@ -185,15 +184,13 @@ export class Blockchain {
// If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node
// We do this so that users can still browse the 0x Portal DApp even if they do not have web3
// injected into their browser.
- const provider = new ProviderEngine();
+ const provider = new Web3ProviderEngine();
provider.addProvider(new FilterSubprovider());
const networkId = constants.NETWORK_ID_MAINNET;
const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => {
- return new RpcSubprovider({
- rpcUrl: publicNodeUrl,
- });
+ return new RPCSubprovider(publicNodeUrl);
});
- provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[]));
+ provider.addProvider(new RedundantSubprovider(rpcSubproviders));
provider.start();
return [provider, undefined];
}
@@ -537,7 +534,9 @@ export class Blockchain {
}
public destroy(): void {
this._blockchainWatcher.destroy();
- this._injectedProviderObservable.unsubscribe(this._injectedProviderUpdateHandler);
+ if (this._injectedProviderObservable) {
+ this._injectedProviderObservable.unsubscribe(this._injectedProviderUpdateHandler);
+ }
this._stopWatchingExchangeLogFillEvents();
this._stopWatchingGasPrice();
}
@@ -569,7 +568,7 @@ export class Blockchain {
configs.DEFAULT_TRACKED_TOKEN_SYMBOLS,
)}) not found in tokenRegistry: ${JSON.stringify(tokenRegistryTokens)}`,
);
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
return;
}
if (_.isEmpty(trackedTokensByAddress)) {
@@ -680,8 +679,7 @@ export class Blockchain {
// Note: it's not entirely clear from the documentation which
// errors will be thrown by `watch`. For now, let's log the error
// to rollbar and stop watching when one occurs
- // tslint:disable-next-line:no-floating-promises
- errorReporter.reportAsync(err); // fire and forget
+ errorReporter.report(err); // fire and forget
return;
} else {
const decodedLog = decodedLogEvent.log;
@@ -767,9 +765,13 @@ export class Blockchain {
this._contractWrappers.exchange.unsubscribeAll();
}
private async _getTokenRegistryTokensByAddressAsync(): Promise<TokenByAddress> {
- utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.');
- const tokenRegistryTokens = await this._contractWrappers.tokenRegistry.getTokensAsync();
-
+ let tokenRegistryTokens;
+ if (this.networkId === constants.NETWORK_ID_MAINNET) {
+ tokenRegistryTokens = await backendClient.getTokenInfosAsync();
+ } else {
+ utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.');
+ tokenRegistryTokens = await this._contractWrappers.tokenRegistry.getTokensAsync();
+ }
const tokenByAddress: TokenByAddress = {};
_.each(tokenRegistryTokens, (t: ZeroExToken) => {
// HACK: For now we have a hard-coded list of iconUrls for the dummyTokens
@@ -789,7 +791,7 @@ export class Blockchain {
return tokenByAddress;
}
private async _onPageLoadInitFireAndForgetAsync(): Promise<void> {
- await utils.onPageLoadAsync(); // wait for page to load
+ await utils.onPageLoadPromise; // wait for page to load
const networkIdIfExists = await Blockchain._getInjectedWeb3ProviderNetworkIdIfExistsAsync();
this.networkId = !_.isUndefined(networkIdIfExists) ? networkIdIfExists : constants.NETWORK_ID_MAINNET;
const injectedWeb3IfExists = Blockchain._getInjectedWeb3();
@@ -909,7 +911,7 @@ export class Blockchain {
if (_.includes(errMsg, 'not been deployed to detected network')) {
throw new Error(BlockchainCallErrs.ContractDoesNotExist);
} else {
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
throw new Error(BlockchainCallErrs.UnhandledError);
}
}
diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts
index 4b23aa98a..39ed8b08b 100644
--- a/packages/website/ts/blockchain_watcher.ts
+++ b/packages/website/ts/blockchain_watcher.ts
@@ -4,9 +4,9 @@ import * as _ from 'lodash';
import { Dispatcher } from 'ts/redux/dispatcher';
export class BlockchainWatcher {
- private _dispatcher: Dispatcher;
- private _web3Wrapper: Web3Wrapper;
- private _shouldPollUserAddress: boolean;
+ private readonly _dispatcher: Dispatcher;
+ private readonly _web3Wrapper: Web3Wrapper;
+ private readonly _shouldPollUserAddress: boolean;
private _watchBalanceIntervalId: NodeJS.Timer;
private _prevUserEtherBalanceInWei?: BigNumber;
private _prevUserAddressIfExists: string;
diff --git a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx
index ce86df856..3ebab03ef 100644
--- a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx
+++ b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx
@@ -14,9 +14,9 @@ export const U2fNotSupportedDialog = (props: U2fNotSupportedDialogProps) => {
<Dialog
title="U2F Not Supported"
titleStyle={{ fontWeight: 100 }}
- actions={[<FlatButton key="u2fNo" label="Ok" onTouchTap={props.onToggleDialog.bind(this)} />]}
+ actions={[<FlatButton key="u2fNo" label="Ok" onTouchTap={props.onToggleDialog} />]}
open={props.isOpen}
- onRequestClose={props.onToggleDialog.bind(this)}
+ onRequestClose={props.onToggleDialog}
autoScrollBodyContent={true}
>
<div className="pt2" style={{ color: colors.grey700 }}>
diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx
index 4b91a2ebd..b0091a1c1 100644
--- a/packages/website/ts/components/eth_weth_conversion_button.tsx
+++ b/packages/website/ts/components/eth_weth_conversion_button.tsx
@@ -37,7 +37,7 @@ export class EthWethConversionButton extends React.Component<
> {
public static defaultProps: Partial<EthWethConversionButtonProps> = {
isDisabled: false,
- onConversionSuccessful: _.noop,
+ onConversionSuccessful: _.noop.bind(_),
};
public constructor(props: EthWethConversionButtonProps) {
super(props);
@@ -118,7 +118,7 @@ export class EthWethConversionButton extends React.Component<
? 'Failed to wrap your ETH. Please try again.'
: 'Failed to unwrap your WETH. Please try again.';
this.props.dispatcher.showFlashMessage(errorMsg);
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
}
}
this.setState({
diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx
index 03ba1183d..7da2e0870 100644
--- a/packages/website/ts/components/fill_order.tsx
+++ b/packages/website/ts/components/fill_order.tsx
@@ -1,5 +1,5 @@
import { getOrderHashHex, isValidSignature } from '@0xproject/order-utils';
-import { colors, constants as sharedConstants } from '@0xproject/react-shared';
+import { colors } from '@0xproject/react-shared';
import { Order as ZeroExOrder } from '@0xproject/types';
import { BigNumber, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
@@ -506,6 +506,10 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
await this._checkForUntrackedTokensAndAskToAddAsync();
}
+ private _trackOrderEvent(eventName: string): void {
+ const parsedOrder = this.state.parsedOrder;
+ analytics.trackOrderEvent(eventName, parsedOrder);
+ }
private async _onFillOrderClickFireAndForgetAsync(): Promise<void> {
if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) {
this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
@@ -552,14 +556,12 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
});
return;
}
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
- const eventLabel = `${parsedOrder.metadata.takerToken.symbol}-${networkName}`;
try {
const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync(
signedOrder,
this.props.orderFillAmount,
);
- analytics.logEvent('Portal', 'Fill Order Success', eventLabel, parsedOrder.signedOrder.takerTokenAmount);
+ this._trackOrderEvent('Fill Order Success');
// After fill completes, let's force fetch the token balances
this.props.dispatcher.forceTokenStateRefetch();
this.setState({
@@ -573,7 +575,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
this.setState({
isFilling: false,
});
- analytics.logEvent('Portal', 'Fill Order Failure', eventLabel, parsedOrder.signedOrder.takerTokenAmount);
+ this._trackOrderEvent('Fill Order Failure');
const errMsg = `${err}`;
if (utils.didUserDenyWeb3Request(errMsg)) {
return;
@@ -583,7 +585,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
this.setState({
globalErrMsg,
});
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
return;
}
}
@@ -628,8 +630,6 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
});
return;
}
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
- const eventLabel = `${parsedOrder.metadata.makerToken.symbol}-${networkName}`;
try {
await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount);
this.setState({
@@ -638,7 +638,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
globalErrMsg: '',
unavailableTakerAmount: takerTokenAmount,
});
- analytics.logEvent('Portal', 'Cancel Order Success', eventLabel, parsedOrder.signedOrder.makerTokenAmount);
+ this._trackOrderEvent('Cancel Order Success');
return;
} catch (err) {
this.setState({
@@ -648,13 +648,13 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
if (utils.didUserDenyWeb3Request(errMsg)) {
return;
}
- analytics.logEvent('Portal', 'Cancel Order Failure', eventLabel, parsedOrder.signedOrder.makerTokenAmount);
+ this._trackOrderEvent('Cancel Order Failure');
globalErrMsg = 'Failed to cancel order, please refresh and try again';
logUtils.log(`${err}`);
this.setState({
globalErrMsg,
});
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
return;
}
}
diff --git a/packages/website/ts/components/fill_warning_dialog.tsx b/packages/website/ts/components/fill_warning_dialog.tsx
index 83095b1d3..45c492221 100644
--- a/packages/website/ts/components/fill_warning_dialog.tsx
+++ b/packages/website/ts/components/fill_warning_dialog.tsx
@@ -18,16 +18,16 @@ export const FillWarningDialog = (props: FillWarningDialogProps) => {
<FlatButton
key="fillWarningCancel"
label="Cancel"
- onTouchTap={props.onToggleDialog.bind(this, didCancel)}
+ onTouchTap={() => props.onToggleDialog(didCancel)} // tslint:disable-line:jsx-no-lambda
/>,
<FlatButton
key="fillWarningContinue"
label="Fill Order"
- onTouchTap={props.onToggleDialog.bind(this, !didCancel)}
+ onTouchTap={() => props.onToggleDialog(!didCancel)} // tslint:disable-line:jsx-no-lambda
/>,
]}
open={props.isOpen}
- onRequestClose={props.onToggleDialog.bind(this)}
+ onRequestClose={() => props.onToggleDialog(didCancel)} // tslint:disable-line:jsx-no-lambda
autoScrollBodyContent={true}
modal={true}
>
diff --git a/packages/website/ts/components/forms/subscribe_form.tsx b/packages/website/ts/components/forms/subscribe_form.tsx
index 8ef58328e..761db7517 100644
--- a/packages/website/ts/components/forms/subscribe_form.tsx
+++ b/packages/website/ts/components/forms/subscribe_form.tsx
@@ -6,6 +6,7 @@ import { Button } from 'ts/components/ui/button';
import { Container } from 'ts/components/ui/container';
import { Input } from 'ts/components/ui/input';
import { Text } from 'ts/components/ui/text';
+import { analytics } from 'ts/utils/analytics';
import { backendClient } from 'ts/utils/backend_client';
export interface SubscribeFormProps {}
@@ -112,6 +113,9 @@ export class SubscribeForm extends React.Component<SubscribeFormProps, Subscribe
try {
const response = await backendClient.subscribeToNewsletterAsync(this.state.emailText);
const status = response.status === 200 ? SubscribeFormStatus.Success : SubscribeFormStatus.Error;
+ if (status === SubscribeFormStatus.Success) {
+ analytics.identify(this.state.emailText, 'email');
+ }
this.setState({ status, emailText: '' });
} catch (error) {
this._setStatus(SubscribeFormStatus.Error);
diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx
index 5eada37b6..2dca3483f 100644
--- a/packages/website/ts/components/generate_order/asset_picker.tsx
+++ b/packages/website/ts/components/generate_order/asset_picker.tsx
@@ -46,7 +46,7 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt
public static defaultProps: Partial<AssetPickerProps> = {
tokenVisibility: TokenVisibility.ALL,
};
- private _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs };
+ private readonly _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs };
constructor(props: AssetPickerProps) {
super(props);
this.state = {
diff --git a/packages/website/ts/components/generate_order/generate_order_form.tsx b/packages/website/ts/components/generate_order/generate_order_form.tsx
index d26b5c3fa..72efab033 100644
--- a/packages/website/ts/components/generate_order/generate_order_form.tsx
+++ b/packages/website/ts/components/generate_order/generate_order_form.tsx
@@ -1,6 +1,6 @@
import { generatePseudoRandomSalt, getOrderHashHex } from '@0xproject/order-utils';
-import { colors, constants as sharedConstants } from '@0xproject/react-shared';
-import { ECSignature, Order } from '@0xproject/types';
+import { colors } from '@0xproject/react-shared';
+import { ECSignature, Order as ZeroExOrder } from '@0xproject/types';
import { BigNumber, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import Dialog from 'material-ui/Dialog';
@@ -20,7 +20,7 @@ import { SwapIcon } from 'ts/components/ui/swap_icon';
import { Dispatcher } from 'ts/redux/dispatcher';
import { portalOrderSchema } from 'ts/schemas/portal_order_schema';
import { validator } from 'ts/schemas/validator';
-import { AlertTypes, BlockchainErrs, HashData, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types';
+import { AlertTypes, BlockchainErrs, HashData, Order, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types';
import { analytics } from 'ts/utils/analytics';
import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter';
@@ -254,7 +254,8 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
userAddressIfExists,
debitToken.address,
);
- const receiveAmount = this.props.sideToAssetToken[Side.Receive].amount;
+ const receiveToken = this.props.sideToAssetToken[Side.Receive];
+ const receiveAmount = receiveToken.amount;
if (
!_.isUndefined(debitToken.amount) &&
!_.isUndefined(receiveAmount) &&
@@ -264,24 +265,28 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
debitBalance.gte(debitToken.amount) &&
debitAllowance.gte(debitToken.amount)
) {
- const didSignSuccessfully = await this._signTransactionAsync();
- if (didSignSuccessfully) {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
- const eventLabel = `${this.props.tokenByAddress[debitToken.address].symbol}-${networkName}`;
- analytics.logEvent('Portal', 'Sign Order Success', eventLabel, debitToken.amount.toNumber());
+ const signedOrder = await this._signTransactionAsync();
+ const doesSignedOrderExist = !_.isUndefined(signedOrder);
+ if (doesSignedOrderExist) {
+ analytics.trackOrderEvent('Sign Order Success', signedOrder);
this.setState({
globalErrMsg: '',
shouldShowIncompleteErrs: false,
});
}
- return didSignSuccessfully;
+ return doesSignedOrderExist;
} else {
let globalErrMsg = 'You must fix the above errors in order to generate a valid order';
if (this.props.userAddress === '') {
globalErrMsg = 'You must enable wallet communication';
this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
}
- analytics.logEvent('Portal', 'Sign Order Failure', globalErrMsg);
+ analytics.track('Sign Order Failure', {
+ makerTokenAmount: debitToken.amount.toString(),
+ makerToken: this.props.tokenByAddress[debitToken.address].symbol,
+ takerTokenAmount: receiveToken.amount.toString(),
+ takerToken: this.props.tokenByAddress[receiveToken.address].symbol,
+ });
this.setState({
globalErrMsg,
shouldShowIncompleteErrs: true,
@@ -289,7 +294,7 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
return false;
}
}
- private async _signTransactionAsync(): Promise<boolean> {
+ private async _signTransactionAsync(): Promise<Order | undefined> {
this.setState({
signingState: SigningState.SIGNING,
});
@@ -299,11 +304,11 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
this.setState({
signingState: SigningState.UNSIGNED,
});
- return false;
+ return undefined;
}
const hashData = this.props.hashData;
- const zeroExOrder: Order = {
+ const zeroExOrder: ZeroExOrder = {
exchangeContractAddress: exchangeContractAddr,
expirationUnixTimestampSec: hashData.orderExpiryTimestamp,
feeRecipient: hashData.feeRecipientAddress,
@@ -320,9 +325,10 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
const orderHash = getOrderHashHex(zeroExOrder);
let globalErrMsg = '';
+ let order;
try {
const ecSignature = await this.props.blockchain.signOrderHashAsync(orderHash);
- const order = utils.generateOrder(
+ order = utils.generateOrder(
exchangeContractAddr,
this.props.sideToAssetToken,
hashData.orderExpiryTimestamp,
@@ -349,14 +355,14 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
globalErrMsg = 'An unexpected error occured. Please try refreshing the page';
logUtils.log(`Unexpected error occured: ${err}`);
logUtils.log(err.stack);
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
}
}
this.setState({
signingState: globalErrMsg === '' ? SigningState.SIGNED : SigningState.UNSIGNED,
globalErrMsg,
});
- return globalErrMsg === '';
+ return order;
}
private _updateOrderAddress(address?: string): void {
if (!_.isUndefined(address)) {
diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx
index 0d5995696..05dce134a 100644
--- a/packages/website/ts/components/inputs/allowance_toggle.tsx
+++ b/packages/website/ts/components/inputs/allowance_toggle.tsx
@@ -1,4 +1,4 @@
-import { constants as sharedConstants, Styles } from '@0xproject/react-shared';
+import { Styles } from '@0xproject/react-shared';
import { BigNumber, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import Toggle from 'material-ui/Toggle';
@@ -57,7 +57,7 @@ const styles: Styles = {
export class AllowanceToggle extends React.Component<AllowanceToggleProps, AllowanceToggleState> {
public static defaultProps = {
- onErrorOccurred: _.noop,
+ onErrorOccurred: _.noop.bind(_),
isDisabled: false,
};
constructor(props: AllowanceToggleProps) {
@@ -111,14 +111,16 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow
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}`;
+ const logData = {
+ tokenSymbol: this.props.token.symbol,
+ newAllowance: newAllowanceAmountInBaseUnits.toNumber(),
+ };
try {
await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits);
- analytics.logEvent('Portal', 'Set Allowance Success', eventLabel, newAllowanceAmountInBaseUnits.toNumber());
+ analytics.track('Set Allowances Success', logData);
await this.props.refetchTokenStateAsync();
} catch (err) {
- analytics.logEvent('Portal', 'Set Allowance Failure', eventLabel, newAllowanceAmountInBaseUnits.toNumber());
+ analytics.track('Set Allowance Failure', logData);
this.setState({
isSpinnerVisible: false,
});
@@ -129,7 +131,7 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow
logUtils.log(`Unexpected error encountered: ${err}`);
logUtils.log(err.stack);
this.props.onErrorOccurred(BalanceErrs.allowanceSettingFailed);
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
}
}
private _isAllowanceSet(): boolean {
diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx
index f23beb436..eb01e3ea6 100644
--- a/packages/website/ts/components/inputs/balance_bounded_input.tsx
+++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx
@@ -35,7 +35,7 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
isDisabled: false,
shouldShowErrs: true,
hintText: 'amount',
- onErrorMsgChange: _.noop,
+ onErrorMsgChange: _.noop.bind(_),
shouldShowUnderline: true,
};
constructor(props: BalanceBoundedInputProps) {
@@ -125,7 +125,7 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount);
return errMsg;
}
- private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop): void {
+ private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop.bind(_)): void {
const errorMsg = this._validate(amount, balance);
this.props.onErrorMsgChange(errorMsg);
this.setState(
diff --git a/packages/website/ts/components/inputs/expiration_input.tsx b/packages/website/ts/components/inputs/expiration_input.tsx
index 79dd2f568..5417ce715 100644
--- a/packages/website/ts/components/inputs/expiration_input.tsx
+++ b/packages/website/ts/components/inputs/expiration_input.tsx
@@ -17,7 +17,7 @@ interface ExpirationInputState {
}
export class ExpirationInput extends React.Component<ExpirationInputProps, ExpirationInputState> {
- private _earliestPickableMoment: moment.Moment;
+ private readonly _earliestPickableMoment: moment.Moment;
constructor(props: ExpirationInputProps) {
super(props);
// Set the earliest pickable date to today at 00:00, so users can only pick the current or later dates
diff --git a/packages/website/ts/components/legacy_portal/legacy_portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx
deleted file mode 100644
index c85d97207..000000000
--- a/packages/website/ts/components/legacy_portal/legacy_portal.tsx
+++ /dev/null
@@ -1,339 +0,0 @@
-import { colors } from '@0xproject/react-shared';
-import { BigNumber } from '@0xproject/utils';
-import * as _ from 'lodash';
-import CircularProgress from 'material-ui/CircularProgress';
-import Paper from 'material-ui/Paper';
-import * as React from 'react';
-import * as DocumentTitle from 'react-document-title';
-import { Route, Switch } from 'react-router-dom';
-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 { WrappedEthSectionNoticeDialog } from 'ts/components/dialogs/wrapped_eth_section_notice_dialog';
-import { EthWrappers } from 'ts/components/eth_wrappers';
-import { FillOrder } from 'ts/components/fill_order';
-import { Footer } from 'ts/components/footer';
-import { LegacyPortalMenu } from 'ts/components/legacy_portal/legacy_portal_menu';
-import { TokenBalances } from 'ts/components/token_balances';
-import { TopBar } from 'ts/components/top_bar/top_bar';
-import { TradeHistory } from 'ts/components/trade_history/trade_history';
-import { FlashMessage } from 'ts/components/ui/flash_message';
-import { GenerateOrderForm } from 'ts/containers/generate_order_form';
-import { localStorage } from 'ts/local_storage/local_storage';
-import { Dispatcher } from 'ts/redux/dispatcher';
-import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress, WebsitePaths } from 'ts/types';
-import { constants } from 'ts/utils/constants';
-import { orderParser } from 'ts/utils/order_parser';
-import { Translate } from 'ts/utils/translate';
-import { utils } from 'ts/utils/utils';
-
-const THROTTLE_TIMEOUT = 100;
-
-export interface LegacyPortalProps {
- 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 LegacyPortalState {
- prevNetworkId: number;
- prevNodeVersion: string;
- prevUserAddress: string;
- prevPathname: string;
- isDisclaimerDialogOpen: boolean;
- isWethNoticeDialogOpen: boolean;
- isLedgerDialogOpen: boolean;
-}
-
-export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPortalState> {
- private _blockchain: Blockchain;
- private _sharedOrderIfExists: Order;
- private _throttledScreenWidthUpdate: () => void;
- public static hasAlreadyDismissedWethNotice(): boolean {
- const didDismissWethNotice = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE);
- const hasAlreadyDismissedWethNotice = !_.isUndefined(didDismissWethNotice) && !_.isEmpty(didDismissWethNotice);
- return hasAlreadyDismissedWethNotice;
- }
- constructor(props: LegacyPortalProps) {
- super(props);
- this._sharedOrderIfExists = orderParser.parse(window.location.search);
- this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
-
- const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`);
- const hasAlreadyDismissedWethNotice = LegacyPortal.hasAlreadyDismissedWethNotice();
-
- 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,
- isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances,
- isLedgerDialogOpen: false,
- };
- }
- public componentDidMount(): void {
- window.addEventListener('resize', this._throttledScreenWidthUpdate);
- window.scrollTo(0, 0);
- }
- public componentWillMount(): void {
- this._blockchain = new Blockchain(this.props.dispatcher);
- }
- public componentWillUnmount(): void {
- 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: LegacyPortalProps): void {
- 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) {
- const isViewingBalances = _.includes(nextProps.location.pathname, `${WebsitePaths.Portal}/balances`);
- const hasAlreadyDismissedWethNotice = LegacyPortal.hasAlreadyDismissedWethNotice();
- this.setState({
- prevPathname: nextProps.location.pathname,
- isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances,
- });
- }
- }
- public render(): React.ReactNode {
- const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind(
- this.props.dispatcher,
- );
- const portalStyle: React.CSSProperties = {
- minHeight: '100vh',
- display: 'flex',
- flexDirection: 'column',
- justifyContent: 'space-between',
- };
- const portalMenuContainerStyle: React.CSSProperties = {
- overflow: 'hidden',
- backgroundColor: colors.darkestGrey,
- color: colors.white,
- };
- return (
- <div style={portalStyle}>
- <DocumentTitle title="0x Portal DApp" />
- <TopBar
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- injectedProviderName={this.props.injectedProviderName}
- onToggleLedgerDialog={this.onToggleLedgerDialog.bind(this)}
- dispatcher={this.props.dispatcher}
- providerType={this.props.providerType}
- blockchainIsLoaded={this.props.blockchainIsLoaded}
- location={this.props.location}
- blockchain={this._blockchain}
- translate={this.props.translate}
- />
- <div id="portal" className="mx-auto max-width-4" style={{ width: '100%' }}>
- <Paper className="mb3 mt2">
- <div className="mx-auto flex">
- <div className="col col-2 pr2 pt1 sm-hide xs-hide" style={portalMenuContainerStyle}>
- <LegacyPortalMenu menuItemStyle={{ color: colors.white }} />
- </div>
- <div className="col col-12 lg-col-10 md-col-10 sm-col sm-col-12">
- <div className="py2" style={{ backgroundColor: colors.grey50 }}>
- {this.props.blockchainIsLoaded ? (
- <Switch>
- <Route
- path={`${WebsitePaths.Portal}/weth`}
- render={this._renderEthWrapper.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/fill`}
- render={this._renderFillOrder.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/balances`}
- render={this._renderTokenBalances.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Portal}/trades`}
- render={this._renderTradeHistory.bind(this)}
- />
- <Route
- path={`${WebsitePaths.Home}`}
- render={this._renderGenerateOrderForm.bind(this)}
- />
- </Switch>
- ) : (
- <div className="pt4 sm-px2 sm-pt2 sm-m1" style={{ height: 500 }}>
- <div
- className="relative sm-px2 sm-pt2 sm-m1"
- style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }}
- >
- <div className="center pb2">
- <CircularProgress size={40} thickness={5} />
- </div>
- <div className="center pt2" style={{ paddingBottom: 11 }}>
- Loading Portal...
- </div>
- </div>
- </div>
- )}
- </div>
- </div>
- </div>
- </Paper>
- <BlockchainErrDialog
- blockchain={this._blockchain}
- blockchainErr={this.props.blockchainErr}
- isOpen={this.props.shouldBlockchainErrDialogBeOpen}
- userAddress={this.props.userAddress}
- toggleDialogFn={updateShouldBlockchainErrDialogBeOpen}
- networkId={this.props.networkId}
- />
- <WrappedEthSectionNoticeDialog
- isOpen={this.state.isWethNoticeDialogOpen}
- onToggleDialog={this._onWethNoticeAccepted.bind(this)}
- />
- <PortalDisclaimerDialog
- isOpen={this.state.isDisclaimerDialogOpen}
- onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)}
- />
- <FlashMessage dispatcher={this.props.dispatcher} flashMessage={this.props.flashMessage} />
- <LedgerConfigDialog
- providerType={this.props.providerType}
- networkId={this.props.networkId}
- blockchain={this._blockchain}
- dispatcher={this.props.dispatcher}
- toggleDialogFn={this.onToggleLedgerDialog.bind(this)}
- isOpen={this.state.isLedgerDialogOpen}
- />
- </div>
- <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
- </div>
- );
- }
- public onToggleLedgerDialog(): void {
- this.setState({
- isLedgerDialogOpen: !this.state.isLedgerDialogOpen,
- });
- }
- private _renderEthWrapper(): React.ReactNode {
- return (
- <EthWrappers
- networkId={this.props.networkId}
- blockchain={this._blockchain}
- dispatcher={this.props.dispatcher}
- tokenByAddress={this.props.tokenByAddress}
- userAddress={this.props.userAddress}
- userEtherBalanceInWei={this.props.userEtherBalanceInWei}
- lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
- />
- );
- }
- private _renderTradeHistory(): React.ReactNode {
- return (
- <TradeHistory
- tokenByAddress={this.props.tokenByAddress}
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- />
- );
- }
- private _renderTokenBalances(): React.ReactNode {
- const trackedTokens = utils.getTrackedTokens(this.props.tokenByAddress);
- return (
- <TokenBalances
- blockchain={this._blockchain}
- blockchainErr={this.props.blockchainErr}
- blockchainIsLoaded={this.props.blockchainIsLoaded}
- dispatcher={this.props.dispatcher}
- screenWidth={this.props.screenWidth}
- tokenByAddress={this.props.tokenByAddress}
- trackedTokens={trackedTokens}
- userAddress={this.props.userAddress}
- userEtherBalanceInWei={this.props.userEtherBalanceInWei}
- networkId={this.props.networkId}
- lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
- />
- );
- }
- private _renderFillOrder(_match: any, _location: Location, _history: History): React.ReactNode {
- const initialFillOrder = !_.isUndefined(this.props.userSuppliedOrderCache)
- ? this.props.userSuppliedOrderCache
- : this._sharedOrderIfExists;
- return (
- <FillOrder
- blockchain={this._blockchain}
- blockchainErr={this.props.blockchainErr}
- initialOrder={initialFillOrder}
- isOrderInUrl={!_.isUndefined(this._sharedOrderIfExists)}
- orderFillAmount={this.props.orderFillAmount}
- networkId={this.props.networkId}
- userAddress={this.props.userAddress}
- tokenByAddress={this.props.tokenByAddress}
- dispatcher={this.props.dispatcher}
- lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
- />
- );
- }
- private _renderGenerateOrderForm(_match: any, _location: Location, _history: History): React.ReactNode {
- return (
- <GenerateOrderForm
- blockchain={this._blockchain}
- hashData={this.props.hashData}
- dispatcher={this.props.dispatcher}
- />
- );
- }
- private _onPortalDisclaimerAccepted(): void {
- localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set');
- this.setState({
- isDisclaimerDialogOpen: false,
- });
- }
- private _onWethNoticeAccepted(): void {
- localStorage.setItem(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE, 'set');
- this.setState({
- isWethNoticeDialogOpen: false,
- });
- }
- private _updateScreenWidth(): void {
- const newScreenWidth = utils.getScreenWidth();
- this.props.dispatcher.updateScreenWidth(newScreenWidth);
- }
-}
diff --git a/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx b/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx
deleted file mode 100644
index 1dd164f8b..000000000
--- a/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import * as _ from 'lodash';
-import * as React from 'react';
-import { MenuItem } from 'ts/components/ui/menu_item';
-import { WebsitePaths } from 'ts/types';
-
-export interface LegacyPortalMenuProps {
- menuItemStyle: React.CSSProperties;
- onClick?: () => void;
-}
-
-interface LegacyPortalMenuState {}
-
-export class LegacyPortalMenu extends React.Component<LegacyPortalMenuProps, LegacyPortalMenuState> {
- public static defaultProps: Partial<LegacyPortalMenuProps> = {
- onClick: _.noop,
- };
- public render(): React.ReactNode {
- return (
- <div>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Generate order', 'zmdi-arrow-right-top')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/fill`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Fill order', 'zmdi-arrow-left-bottom')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/balances`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Balances', 'zmdi-balance-wallet')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/trades`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Trade history', 'zmdi-format-list-bulleted')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/weth`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')}
- </MenuItem>
- </div>
- );
- }
- private _renderMenuItemWithIcon(title: string, iconName: string): React.ReactNode {
- return (
- <div className="flex" style={{ fontWeight: 100 }}>
- <div className="pr1 pl2">
- <i style={{ fontSize: 20 }} className={`zmdi ${iconName}`} />
- </div>
- <div className="pl1">{title}</div>
- </div>
- );
- }
-}
diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
index 20a8f0a32..f395674a1 100644
--- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
+++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx
@@ -1,4 +1,3 @@
-import { constants as sharedConstants } from '@0xproject/react-shared';
import * as _ from 'lodash';
import * as React from 'react';
import { RouteComponentProps, withRouter } from 'react-router';
@@ -225,20 +224,24 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp
(this.props.stepIndex === 0 && !this.props.isRunning && this.props.blockchainIsLoaded) ||
(!this.props.isRunning && !this.props.hasBeenClosed && this.props.blockchainIsLoaded)
) {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
- analytics.logEvent('Portal', 'Onboarding Started - Automatic', networkName, this.props.stepIndex);
+ analytics.track('Onboarding Started', {
+ reason: 'automatic',
+ stepIndex: this.props.stepIndex,
+ });
this.props.updateIsRunning(true);
}
}
private _updateOnboardingStep(stepIndex: number): void {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
this.props.updateOnboardingStep(stepIndex);
- analytics.logEvent('Portal', 'Update Onboarding Step', networkName, stepIndex);
+ analytics.track('Update Onboarding Step', {
+ stepIndex,
+ });
}
private _closeOnboarding(): void {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
this.props.updateIsRunning(false);
- analytics.logEvent('Portal', 'Onboarding Closed', networkName, this.props.stepIndex);
+ analytics.track('Onboarding Closed', {
+ stepIndex: this.props.stepIndex,
+ });
}
private _renderZrxAllowanceToggle(): React.ReactNode {
const zrxToken = utils.getZrxToken(this.props.tokenByAddress);
diff --git a/packages/website/ts/components/order_json.tsx b/packages/website/ts/components/order_json.tsx
index 35188c024..cf06f10c8 100644
--- a/packages/website/ts/components/order_json.tsx
+++ b/packages/website/ts/components/order_json.tsx
@@ -1,5 +1,5 @@
import { ECSignature } from '@0xproject/types';
-import { BigNumber, logUtils } from '@0xproject/utils';
+import { BigNumber, fetchAsync, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import Paper from 'material-ui/Paper';
import TextField from 'material-ui/TextField';
@@ -127,7 +127,7 @@ export class OrderJSON extends React.Component<OrderJSONProps, OrderJSONState> {
href: this.state.shareLink,
method: 'share',
},
- _.noop,
+ _.noop.bind(_),
);
}
private _shareViaEmailAsync(): void {
@@ -148,13 +148,13 @@ You can see and fill it here: ${this.state.shareLink}`);
const bitlyRequestUrl = `${constants.URL_BITLY_API}/v3/shorten?access_token=${
configs.BITLY_ACCESS_TOKEN
}&longUrl=${longUrl}`;
- const response = await fetch(bitlyRequestUrl);
+ const response = await fetchAsync(bitlyRequestUrl);
const responseBody = await response.text();
const bodyObj = JSON.parse(responseBody);
if (response.status !== 200 || bodyObj.status_code !== 200) {
// TODO: Show error message in UI
logUtils.log(`Unexpected status code: ${response.status} -> ${responseBody}`);
- await errorReporter.reportAsync(new Error(`Bitly returned non-200: ${JSON.stringify(response)}`));
+ errorReporter.report(new Error(`Bitly returned non-200: ${JSON.stringify(response)}`));
return '';
}
return bodyObj.data.url;
diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx
index f983241fa..1790a9678 100644
--- a/packages/website/ts/components/portal/portal.tsx
+++ b/packages/website/ts/components/portal/portal.tsx
@@ -1,4 +1,4 @@
-import { colors, constants as sharedConstants } from '@0xproject/react-shared';
+import { colors } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import * as React from 'react';
@@ -110,8 +110,8 @@ const SIDE_PADDING = 20;
export class Portal extends React.Component<PortalProps, PortalState> {
private _blockchain: Blockchain;
- private _sharedOrderIfExists: Order;
- private _throttledScreenWidthUpdate: () => void;
+ private readonly _sharedOrderIfExists: Order;
+ private readonly _throttledScreenWidthUpdate: () => void;
constructor(props: PortalProps) {
super(props);
this._sharedOrderIfExists = orderParser.parse(window.location.search);
@@ -388,10 +388,11 @@ export class Portal extends React.Component<PortalProps, PortalState> {
startOnboarding
);
}
-
private _startOnboarding(): void {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
- analytics.logEvent('Portal', 'Onboarding Started - Manual', networkName, this.props.portalOnboardingStep);
+ analytics.track('Onboarding Started', {
+ reason: 'manual',
+ stepIndex: this.props.portalOnboardingStep,
+ });
this.props.dispatcher.updatePortalOnboardingShowing(true);
}
private _renderWalletSection(): React.ReactNode {
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 431cf145b..193dd237a 100644
--- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
+++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
@@ -1,4 +1,4 @@
-import { constants as sharedConstants, Styles } from '@0xproject/react-shared';
+import { Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
import { GridTile as PlainGridTile } from 'material-ui/GridList';
import * as React from 'react';
@@ -64,10 +64,10 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = (
const link = props.relayerInfo.appUrl || props.relayerInfo.url;
const topTokens = props.relayerInfo.topTokens;
const weeklyTxnVolume = props.relayerInfo.weeklyTxnVolume;
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[props.networkId];
- const eventLabel = `${props.relayerInfo.name}-${networkName}`;
const onClick = () => {
- analytics.logEvent('Portal', 'Relayer Click', eventLabel);
+ analytics.track('Relayer Click', {
+ name: props.relayerInfo.name,
+ });
utils.openUrl(link);
};
const headerImageUrl = props.relayerInfo.logoImgUrl;
diff --git a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx
index c48b672e9..f3787bd27 100644
--- a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx
+++ b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx
@@ -1,9 +1,4 @@
-import {
- colors,
- constants as sharedConstants,
- EtherscanLinkSuffixes,
- utils as sharedUtils,
-} from '@0xproject/react-shared';
+import { colors, EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared';
import * as _ from 'lodash';
import * as React from 'react';
@@ -46,11 +41,11 @@ class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> {
};
}
public render(): React.ReactNode {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
- const eventLabel = `${this.props.tokenInfo.symbol}-${networkName}`;
const onClick = (event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation();
- analytics.logEvent('Portal', 'Token Click', eventLabel);
+ analytics.track('Token Click', {
+ tokenSymbol: this.props.tokenInfo.symbol,
+ });
const tokenLink = this._tokenLinkFromToken(this.props.tokenInfo, this.props.networkId);
utils.openUrl(tokenLink);
};
diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx
index 8486dbd8b..ac55d430b 100644
--- a/packages/website/ts/components/send_button.tsx
+++ b/packages/website/ts/components/send_button.tsx
@@ -80,7 +80,7 @@ export class SendButton extends React.Component<SendButtonProps, SendButtonState
logUtils.log(`Unexpected error encountered: ${err}`);
logUtils.log(err.stack);
this.props.onError();
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
}
}
this.setState({
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx
index 3fae83c00..c8d80702b 100644
--- a/packages/website/ts/components/token_balances.tsx
+++ b/packages/website/ts/components/token_balances.tsx
@@ -5,7 +5,7 @@ import {
Styles,
utils as sharedUtils,
} from '@0xproject/react-shared';
-import { BigNumber, errorUtils, logUtils } from '@0xproject/utils';
+import { BigNumber, errorUtils, fetchAsync, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
import Dialog from 'material-ui/Dialog';
@@ -526,7 +526,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
this.setState({
errorType: BalanceErrs.mintingFailed,
});
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
return false;
}
}
@@ -548,7 +548,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
await utils.sleepAsync(ARTIFICIAL_FAUCET_REQUEST_DELAY);
const segment = isEtherRequest ? 'ether' : 'zrx';
- const response = await fetch(
+ const response = await fetchAsync(
`${constants.URL_TESTNET_FAUCET}/${segment}/${this.props.userAddress}?networkId=${this.props.networkId}`,
);
const responseBody = await response.text();
@@ -561,7 +561,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
this.setState({
errorType,
});
- await errorReporter.reportAsync(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`));
+ errorReporter.report(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`));
return false;
}
diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx
index 960e5a824..63ea3475a 100644
--- a/packages/website/ts/components/top_bar/top_bar.tsx
+++ b/packages/website/ts/components/top_bar/top_bar.tsx
@@ -7,7 +7,6 @@ import MenuItem from 'material-ui/MenuItem';
import * as React from 'react';
import { Link } from 'react-router-dom';
import { Blockchain } from 'ts/blockchain';
-import { LegacyPortalMenu } from 'ts/components/legacy_portal/legacy_portal_menu';
import { DrawerMenu } from 'ts/components/portal/drawer_menu';
import { ProviderDisplay } from 'ts/components/top_bar/provider_display';
import { TopBarMenuItem } from 'ts/components/top_bar/top_bar_menu_item';
@@ -17,7 +16,6 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import { Deco, Key, ProviderType, WebsiteLegacyPaths, WebsitePaths } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { Translate } from 'ts/utils/translate';
-import { utils } from 'ts/utils/utils';
export enum TopBarDisplayType {
Default,
@@ -213,8 +211,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</div>
);
const popoverContent = <Menu style={{ color: colors.darkGrey }}>{developerSectionMenuItems}</Menu>;
- // TODO : Remove this once we ship portal v2
- const shouldShowPortalV2Drawer = this._isViewingPortal() && utils.shouldShowPortalV2();
return (
<div
style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style, ...{ height } }}
@@ -294,11 +290,11 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</div>
</div>
</Container>
- {shouldShowPortalV2Drawer ? this._renderPortalV2Drawer() : this._renderDrawer()}
+ {this._isViewingPortal() ? this._renderPortalDrawer() : this._renderDrawer()}
</div>
);
}
- private _renderPortalV2Drawer(): React.ReactNode {
+ private _renderPortalDrawer(): React.ReactNode {
return (
<Drawer
open={this.state.isDrawerOpen}
@@ -326,7 +322,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
onRequestChange={this._onMenuButtonClick.bind(this)}
>
<div className="clearfix">
- {this._renderPortalMenu()}
{this._renderDocsMenu()}
{this._renderWiki()}
<div className="pl1 py1 mt3" style={{ backgroundColor: colors.lightGrey }}>
@@ -478,20 +473,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</div>
);
}
- private _renderPortalMenu(): React.ReactNode {
- if (!this._isViewingPortal()) {
- return undefined;
- }
-
- return (
- <div className="lg-hide md-hide">
- <div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
- {this.props.translate.get(Key.PortalDApp, Deco.CapWords)}
- </div>
- <LegacyPortalMenu menuItemStyle={{ color: 'black' }} onClick={this._onMenuButtonClick.bind(this)} />
- </div>
- );
- }
private _onMenuButtonClick(): void {
this.setState({
isDrawerOpen: !this.state.isDrawerOpen,
diff --git a/packages/website/ts/components/ui/menu_item.tsx b/packages/website/ts/components/ui/menu_item.tsx
index 64c0dc49d..0cb4b387c 100644
--- a/packages/website/ts/components/ui/menu_item.tsx
+++ b/packages/website/ts/components/ui/menu_item.tsx
@@ -15,7 +15,7 @@ interface MenuItemState {
export class MenuItem extends React.Component<MenuItemProps, MenuItemState> {
public static defaultProps: Partial<MenuItemProps> = {
- onClick: _.noop,
+ onClick: _.noop.bind(_),
className: '',
};
public constructor(props: MenuItemProps) {
diff --git a/packages/website/ts/components/ui/overlay.tsx b/packages/website/ts/components/ui/overlay.tsx
index da26317de..fc7507475 100644
--- a/packages/website/ts/components/ui/overlay.tsx
+++ b/packages/website/ts/components/ui/overlay.tsx
@@ -26,7 +26,7 @@ export const Overlay: React.StatelessComponent<OverlayProps> = props => (
Overlay.defaultProps = {
style: {},
- onClick: _.noop,
+ onClick: _.noop.bind(_),
};
Overlay.displayName = 'Overlay';
diff --git a/packages/website/ts/components/ui/simple_menu.tsx b/packages/website/ts/components/ui/simple_menu.tsx
index 74b8ef6ae..8a9349c6d 100644
--- a/packages/website/ts/components/ui/simple_menu.tsx
+++ b/packages/website/ts/components/ui/simple_menu.tsx
@@ -41,7 +41,7 @@ export const SimpleMenuItem: React.StatelessComponent<SimpleMenuItemProps> = ({
<Text
fontSize="14px"
fontColor={colors.darkGrey}
- onClick={onClick || _.noop}
+ onClick={onClick || _.noop.bind(_)}
hoverColor={colors.mediumBlue}
>
{displayText}
@@ -84,5 +84,5 @@ export interface DifferentWalletSimpleMenuItemProps {
export const DifferentWalletSimpleMenuItem: React.StatelessComponent<DifferentWalletSimpleMenuItemProps> = ({
onClick,
}) => {
- return <SimpleMenuItem displayText="Use a Different Wallet..." onClick={onClick} />;
+ return <SimpleMenuItem displayText="Use Ledger Wallet..." onClick={onClick} />;
};
diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx
index 6c1c495d7..40a8a23ea 100644
--- a/packages/website/ts/components/wallet/wallet.tsx
+++ b/packages/website/ts/components/wallet/wallet.tsx
@@ -1,4 +1,4 @@
-import { constants as sharedConstants, EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared';
+import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared';
import { BigNumber, errorUtils } from '@0xproject/utils';
import * as _ from 'lodash';
@@ -204,7 +204,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
<AccountConnection accountState={accountState} injectedProviderName={this.props.injectedProviderName} />
</div>
);
- const onClick = _.noop;
+ const onClick = _.noop.bind(_);
const accessory = (
<DropDown
activeNode={
@@ -488,19 +488,17 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
}
private _openWrappedEtherActionRow(wrappedEtherDirection: Side): void {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
const action =
wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Opened' : 'Wallet - Unwrap WETH Opened';
- analytics.logEvent('Portal', action, networkName);
+ analytics.track(action);
this.setState({
wrappedEtherDirection,
});
}
private _closeWrappedEtherActionRow(wrappedEtherDirection: Side): void {
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
const action =
wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Closed' : 'Wallet - Unwrap WETH Closed';
- analytics.logEvent('Portal', action, networkName);
+ analytics.track(action);
this.setState({
wrappedEtherDirection: undefined,
});
diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx
index 2b4cf93fe..fcab5d1dd 100644
--- a/packages/website/ts/components/wallet/wrap_ether_item.tsx
+++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx
@@ -1,4 +1,4 @@
-import { constants as sharedConstants, Styles } from '@0xproject/react-shared';
+import { Styles } from '@0xproject/react-shared';
import { BigNumber, logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
@@ -188,20 +188,23 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
this.setState({
isEthConversionHappening: true,
});
- const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
+ const etherToken = this.props.etherToken;
+ const amountToConvert = this.state.currentInputAmount;
+ const ethAmount = Web3Wrapper.toUnitAmount(amountToConvert, constants.DECIMAL_PLACES_ETH).toString();
+ const tokenAmount = Web3Wrapper.toUnitAmount(amountToConvert, etherToken.decimals).toString();
try {
- const etherToken = this.props.etherToken;
- const amountToConvert = this.state.currentInputAmount;
if (this.props.direction === Side.Deposit) {
await this.props.blockchain.convertEthToWrappedEthTokensAsync(etherToken.address, amountToConvert);
- const ethAmount = Web3Wrapper.toUnitAmount(amountToConvert, constants.DECIMAL_PLACES_ETH);
- this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`);
- analytics.logEvent('Portal', 'Wrap ETH Successfully', networkName);
+ this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount} ETH to WETH`);
+ analytics.track('Wrap ETH Success', {
+ amount: ethAmount,
+ });
} else {
await this.props.blockchain.convertWrappedEthTokensToEthAsync(etherToken.address, amountToConvert);
- const tokenAmount = Web3Wrapper.toUnitAmount(amountToConvert, etherToken.decimals);
- this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`);
- analytics.logEvent('Portal', 'Unwrap WETH Successfully', networkName);
+ this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount} WETH to ETH`);
+ analytics.track('Unwrap WETH Success', {
+ amount: tokenAmount,
+ });
}
await this.props.refetchEthTokenStateAsync();
this.props.onConversionSuccessful();
@@ -214,12 +217,16 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
logUtils.log(err.stack);
if (this.props.direction === Side.Deposit) {
this.props.dispatcher.showFlashMessage('Failed to wrap your ETH. Please try again.');
- analytics.logEvent('Portal', 'Wrap ETH Failed', networkName);
+ analytics.track('Wrap ETH Failure', {
+ amount: ethAmount,
+ });
} else {
this.props.dispatcher.showFlashMessage('Failed to unwrap your WETH. Please try again.');
- analytics.logEvent('Portal', 'Unwrap WETH Failed', networkName);
+ analytics.track('Unwrap WETH Failed', {
+ amount: tokenAmount,
+ });
}
- await errorReporter.reportAsync(err);
+ errorReporter.report(err);
}
}
this.setState({
diff --git a/packages/website/ts/containers/legacy_portal.ts b/packages/website/ts/containers/legacy_portal.ts
deleted file mode 100644
index e99f47fb7..000000000
--- a/packages/website/ts/containers/legacy_portal.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { BigNumber } from '@0xproject/utils';
-import * as _ from 'lodash';
-import * as React from 'react';
-import { connect } from 'react-redux';
-import { Dispatch } from 'redux';
-import {
- LegacyPortal as LegacyPortalComponent,
- LegacyPortalProps as LegacyPortalComponentProps,
-} from 'ts/components/legacy_portal/legacy_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: LegacyPortalComponentProps): 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<State>): ConnectedDispatch => ({
- dispatcher: new Dispatcher(dispatch),
-});
-
-export const LegacyPortal: React.ComponentClass<LegacyPortalComponentProps> = connect(
- mapStateToProps,
- mapDispatchToProps,
-)(LegacyPortalComponent);
diff --git a/packages/website/ts/containers/zero_ex_js_documentation.ts b/packages/website/ts/containers/zero_ex_js_documentation.ts
index b43a1f645..2fc08e923 100644
--- a/packages/website/ts/containers/zero_ex_js_documentation.ts
+++ b/packages/website/ts/containers/zero_ex_js_documentation.ts
@@ -183,6 +183,32 @@ const docsInfoConfig: DocsInfoConfig = {
'JSONRPCErrorCallback',
'LogEntryEvent',
'LogEntry',
+ 'ERC20AssetData',
+ 'ERC721AssetData',
+ 'AssetProxyId',
+ 'WETH9Events',
+ 'WETH9WithdrawalEventArgs',
+ 'WETH9ApprovalEventArgs',
+ 'WETH9EventArgs',
+ 'WETH9DepositEventArgs',
+ 'WETH9TransferEventArgs',
+ 'ERC20TokenTransferEventArgs',
+ 'ERC20TokenApprovalEventArgs',
+ 'ERC20TokenEvents',
+ 'ERC20TokenEventArgs',
+ 'ERC721TokenApprovalEventArgs',
+ 'ERC721TokenApprovalForAllEventArgs',
+ 'ERC721TokenTransferEventArgs',
+ 'ERC721TokenEvents',
+ 'ExchangeCancelUpToEventArgs',
+ 'ExchangeAssetProxyRegisteredEventArgs',
+ 'ExchangeFillEventArgs',
+ 'ExchangeCancelEventArgs',
+ 'ExchangeEventArgs',
+ 'ContractWrappersConfig',
+ 'MessagePrefixType',
+ 'MessagePrefixOpts',
+ 'OrderInfo',
],
typeNameToPrefix: {},
typeNameToExternalLink: {
diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx
index c7ccfdf1f..2a5c5e4f1 100644
--- a/packages/website/ts/index.tsx
+++ b/packages/website/ts/index.tsx
@@ -16,11 +16,9 @@ import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage';
import { tradeHistoryStorage } from 'ts/local_storage/trade_history_storage';
import { store } from 'ts/redux/store';
import { WebsiteLegacyPaths, WebsitePaths } from 'ts/types';
-import { analytics } from 'ts/utils/analytics';
import { muiTheme } from 'ts/utils/mui_theme';
import { utils } from 'ts/utils/utils';
// Polyfills
-import 'whatwg-fetch';
injectTapEventPlugin();
// Check if we've introduced an update that requires us to clear the tradeHistory local storage entries
@@ -35,14 +33,9 @@ import 'less/all.less';
// 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.
-// TODO: Remove this once we ship V2
-const LazyPortal = utils.shouldShowPortalV2()
- ? createLazyComponent('Portal', async () =>
- System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'),
- )
- : createLazyComponent('LegacyPortal', async () =>
- System.import<any>(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'),
- );
+const LazyPortal = createLazyComponent('Portal', async () =>
+ System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'),
+);
const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () =>
System.import<any>(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'),
);
@@ -74,10 +67,6 @@ const LazyEthereumTypesDocumentation = createLazyComponent('Documentation', asyn
System.import<any>(/* webpackChunkName: "ethereumTypesDocs" */ 'ts/containers/ethereum_types_documentation'),
);
-analytics.init();
-// tslint:disable-next-line:no-floating-promises
-analytics.logProviderAsync((window as any).web3);
-
render(
<Router>
<div>
diff --git a/packages/website/ts/local_storage/local_storage.ts b/packages/website/ts/local_storage/local_storage.ts
index 1e3258ce0..13d9ca401 100644
--- a/packages/website/ts/local_storage/local_storage.ts
+++ b/packages/website/ts/local_storage/local_storage.ts
@@ -5,7 +5,7 @@ export const localStorage = {
return !!window.localStorage;
},
getItemIfExists(key: string): string {
- if (!this.doesExist) {
+ if (!localStorage.doesExist) {
return undefined;
}
const item = window.localStorage.getItem(key);
@@ -15,13 +15,13 @@ export const localStorage = {
return item;
},
setItem(key: string, value: string): void {
- if (!this.doesExist || _.isUndefined(value)) {
+ if (!localStorage.doesExist || _.isUndefined(value)) {
return;
}
window.localStorage.setItem(key, value);
},
removeItem(key: string): void {
- if (!this.doesExist) {
+ if (!localStorage.doesExist) {
return;
}
window.localStorage.removeItem(key);
@@ -37,7 +37,7 @@ export const localStorage = {
localStorage.setItem(key, JSON.stringify(value));
},
getAllKeys(): string[] {
- if (!this.doesExist) {
+ if (!localStorage.doesExist) {
return [];
}
return _.keys(window.localStorage);
diff --git a/packages/website/ts/local_storage/tracked_token_storage.ts b/packages/website/ts/local_storage/tracked_token_storage.ts
index f865f8109..b1b579aef 100644
--- a/packages/website/ts/local_storage/tracked_token_storage.ts
+++ b/packages/website/ts/local_storage/tracked_token_storage.ts
@@ -17,7 +17,7 @@ export const trackedTokenStorage = {
localStorage.setItem(TRACKED_TOKENS_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE);
},
addTrackedTokenToUser(userAddress: string, networkId: number, token: Token): void {
- const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress();
+ const trackedTokensByUserAddress = trackedTokenStorage.getTrackedTokensByUserAddress();
let trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress];
if (_.isUndefined(trackedTokensByNetworkId)) {
trackedTokensByNetworkId = {};
@@ -57,7 +57,7 @@ export const trackedTokenStorage = {
return trackedTokensByAddress;
},
removeTrackedToken(userAddress: string, networkId: number, tokenAddress: string): void {
- const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress();
+ const trackedTokensByUserAddress = trackedTokenStorage.getTrackedTokensByUserAddress();
const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress];
const trackedTokens = trackedTokensByNetworkId[networkId];
const remainingTrackedTokens = _.filter(trackedTokens, (token: Token) => {
diff --git a/packages/website/ts/local_storage/trade_history_storage.tsx b/packages/website/ts/local_storage/trade_history_storage.tsx
index 2e2f4e64e..a9b042820 100644
--- a/packages/website/ts/local_storage/trade_history_storage.tsx
+++ b/packages/website/ts/local_storage/trade_history_storage.tsx
@@ -27,31 +27,31 @@ export const tradeHistoryStorage = {
localStorage.setItem(FILL_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE);
},
addFillToUser(userAddress: string, networkId: number, fill: Fill): void {
- const fillsByHash = this.getUserFillsByHash(userAddress, networkId);
- const fillHash = this._getFillHash(fill);
+ const fillsByHash = tradeHistoryStorage.getUserFillsByHash(userAddress, networkId);
+ const fillHash = tradeHistoryStorage._getFillHash(fill);
const doesFillExist = !_.isUndefined(fillsByHash[fillHash]);
if (doesFillExist) {
return; // noop
}
fillsByHash[fillHash] = fill;
const userFillsJSONString = JSON.stringify(fillsByHash);
- const userFillsKey = this._getUserFillsKey(userAddress, networkId);
+ const userFillsKey = tradeHistoryStorage._getUserFillsKey(userAddress, networkId);
localStorage.setItem(userFillsKey, userFillsJSONString);
},
removeFillFromUser(userAddress: string, networkId: number, fill: Fill): void {
- const fillsByHash = this.getUserFillsByHash(userAddress, networkId);
- const fillHash = this._getFillHash(fill);
+ const fillsByHash = tradeHistoryStorage.getUserFillsByHash(userAddress, networkId);
+ const fillHash = tradeHistoryStorage._getFillHash(fill);
const doesFillExist = !_.isUndefined(fillsByHash[fillHash]);
if (!doesFillExist) {
return; // noop
}
delete fillsByHash[fillHash];
const userFillsJSONString = JSON.stringify(fillsByHash);
- const userFillsKey = this._getUserFillsKey(userAddress, networkId);
+ const userFillsKey = tradeHistoryStorage._getUserFillsKey(userAddress, networkId);
localStorage.setItem(userFillsKey, userFillsJSONString);
},
getUserFillsByHash(userAddress: string, networkId: number): { [fillHash: string]: Fill } {
- const userFillsKey = this._getUserFillsKey(userAddress, networkId);
+ const userFillsKey = tradeHistoryStorage._getUserFillsKey(userAddress, networkId);
const userFillsJSONString = localStorage.getItemIfExists(userFillsKey);
if (_.isEmpty(userFillsJSONString)) {
return {};
@@ -66,7 +66,7 @@ export const tradeHistoryStorage = {
return userFillsByHash;
},
getFillsLatestBlock(userAddress: string, networkId: number): number {
- const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId);
+ const userFillsLatestBlockKey = tradeHistoryStorage._getFillsLatestBlockKey(userAddress, networkId);
const blockNumberStr = localStorage.getItemIfExists(userFillsLatestBlockKey);
if (_.isEmpty(blockNumberStr)) {
return constants.GENESIS_ORDER_BLOCK_BY_NETWORK_ID[networkId];
@@ -75,7 +75,7 @@ export const tradeHistoryStorage = {
return blockNumber;
},
setFillsLatestBlock(userAddress: string, networkId: number, blockNumber: number): void {
- const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId);
+ const userFillsLatestBlockKey = tradeHistoryStorage._getFillsLatestBlockKey(userAddress, networkId);
localStorage.setItem(userFillsLatestBlockKey, `${blockNumber}`);
},
_getUserFillsKey(userAddress: string, networkId: number): string {
diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx
index 5bb5d06a9..33581c938 100644
--- a/packages/website/ts/pages/about/about.tsx
+++ b/packages/website/ts/pages/about/about.tsx
@@ -99,7 +99,7 @@ const teamRow3: ProfileInfo[] = [
},
{
name: 'Jacob Evans',
- title: 'Blockchain Engineer',
+ title: 'Ecosystem Engineer',
description: `Previously software engineer at Qantas and RSA Security.`,
image: '/images/team/jacob.jpg',
linkedIn: 'https://www.linkedin.com/in/dekzter/',
@@ -174,14 +174,21 @@ const teamRow6: ProfileInfo[] = [
linkedIn: 'https://www.linkedin.com/in/stephenalexbrowne/',
github: 'http://github.com/albrow',
},
- // {
- // name: 'Chris Kalani',
- // title: 'Director of Design',
- // description: `Previously founded Wake (acquired by InVision). Early Facebook product designer.`,
- // image: 'images/team/chris.png',
- // linkedIn: 'https://www.linkedin.com/in/chriskalani/',
- // github: 'https://github.com/chriskalani',
- // },
+ {
+ name: 'Peter Zeitz',
+ title: 'Research Fellow',
+ description: `Researching decentralized governance. Previously Assistant Professor of Economics at National University of Singapore Business School. PhD in Economics at UCLA.`,
+ image: 'images/team/peter.jpg',
+ linkedIn: 'https://www.linkedin.com/in/peter-z-7b9595163/',
+ },
+ {
+ name: 'Chris Kalani',
+ title: 'Director of Design',
+ description: `Previously founded Wake (acquired by InVision). Early Facebook product designer.`,
+ image: 'images/team/chris.png',
+ linkedIn: 'https://www.linkedin.com/in/chriskalani/',
+ github: 'https://github.com/chriskalani',
+ },
];
const advisors: ProfileInfo[] = [
diff --git a/packages/website/ts/pages/jobs/jobs.tsx b/packages/website/ts/pages/jobs/jobs.tsx
index 314669ee9..38cefa832 100644
--- a/packages/website/ts/pages/jobs/jobs.tsx
+++ b/packages/website/ts/pages/jobs/jobs.tsx
@@ -33,7 +33,7 @@ export interface JobsState {}
export class Jobs extends React.Component<JobsProps, JobsState> {
// TODO: consolidate this small screen scaffolding into one place (its being used in portal and docs as well)
- private _throttledScreenWidthUpdate: () => void;
+ private readonly _throttledScreenWidthUpdate: () => void;
public constructor(props: JobsProps) {
super(props);
this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx
index b2cf4d979..2a51ee3c0 100644
--- a/packages/website/ts/pages/landing/landing.tsx
+++ b/packages/website/ts/pages/landing/landing.tsx
@@ -171,7 +171,7 @@ interface LandingState {
}
export class Landing extends React.Component<LandingProps, LandingState> {
- private _throttledScreenWidthUpdate: () => void;
+ private readonly _throttledScreenWidthUpdate: () => void;
constructor(props: LandingProps) {
super(props);
this.state = {
diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx
index 9659900be..55f532b11 100644
--- a/packages/website/ts/pages/wiki/wiki.tsx
+++ b/packages/website/ts/pages/wiki/wiki.tsx
@@ -205,7 +205,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
articlesBySection,
},
async () => {
- await utils.onPageLoadAsync();
+ await utils.onPageLoadPromise;
const hash = this.props.location.hash.slice(1);
sharedUtils.scrollToHash(hash, sharedConstants.SCROLL_CONTAINER_ID);
},
diff --git a/packages/website/ts/redux/analyticsMiddleware.ts b/packages/website/ts/redux/analyticsMiddleware.ts
new file mode 100644
index 000000000..51d39a5d7
--- /dev/null
+++ b/packages/website/ts/redux/analyticsMiddleware.ts
@@ -0,0 +1,36 @@
+import { Middleware } from 'redux';
+import { State } from 'ts/redux/reducer';
+import { ActionTypes } from 'ts/types';
+import { analytics } from 'ts/utils/analytics';
+
+export const analyticsMiddleware: Middleware = store => next => action => {
+ const nextAction = next(action);
+ const nextState = (store.getState() as any) as State;
+ switch (action.type) {
+ case ActionTypes.UpdateInjectedProviderName:
+ analytics.addEventProperties({
+ injectedProviderName: nextState.injectedProviderName,
+ });
+ break;
+ case ActionTypes.UpdateNetworkId:
+ analytics.addEventProperties({
+ networkId: nextState.networkId,
+ });
+ break;
+ case ActionTypes.UpdateUserAddress:
+ analytics.addUserProperties({
+ ethAddress: nextState.userAddress,
+ });
+ break;
+ case ActionTypes.UpdateUserEtherBalance:
+ if (nextState.userEtherBalanceInWei) {
+ analytics.addUserProperties({
+ ethBalance: nextState.userEtherBalanceInWei.toString(),
+ });
+ }
+ break;
+ default:
+ break;
+ }
+ return nextAction;
+};
diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts
index e0ce43ae5..db008d319 100644
--- a/packages/website/ts/redux/dispatcher.ts
+++ b/packages/website/ts/redux/dispatcher.ts
@@ -17,7 +17,7 @@ import {
} from 'ts/types';
export class Dispatcher {
- private _dispatch: Dispatch<State>;
+ private readonly _dispatch: Dispatch<State>;
constructor(dispatch: Dispatch<State>) {
this._dispatch = dispatch;
}
diff --git a/packages/website/ts/redux/store.ts b/packages/website/ts/redux/store.ts
index 2672e3f61..006241371 100644
--- a/packages/website/ts/redux/store.ts
+++ b/packages/website/ts/redux/store.ts
@@ -1,7 +1,8 @@
import * as _ from 'lodash';
-import { createStore, Store as ReduxStore } from 'redux';
-import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly';
+import { applyMiddleware, createStore, Store as ReduxStore } from 'redux';
+import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
import { stateStorage } from 'ts/local_storage/state_storage';
+import { analyticsMiddleware } from 'ts/redux/analyticsMiddleware';
import { reducer, State } from 'ts/redux/reducer';
const ONE_SECOND = 1000;
@@ -9,7 +10,7 @@ const ONE_SECOND = 1000;
export const store: ReduxStore<State> = createStore(
reducer,
stateStorage.getPersistedDefaultState(),
- devToolsEnhancer({ name: '0x Website Redux Store' }),
+ composeWithDevTools(applyMiddleware(analyticsMiddleware)),
);
store.subscribe(
_.throttle(() => {
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts
index e8dc694f6..4d0496f6c 100644
--- a/packages/website/ts/types.ts
+++ b/packages/website/ts/types.ts
@@ -244,7 +244,10 @@ export enum BlockchainCallErrs {
export enum Environments {
DEVELOPMENT = 'DEVELOPMENT',
+ DOGFOOD = 'DOGFOOD',
+ STAGING = 'STAGING',
PRODUCTION = 'PRODUCTION',
+ UNKNOWN = 'UNKNOWN',
}
export type ContractInstance = any; // TODO: add type definition for Contract
@@ -516,8 +519,10 @@ export interface OutdatedWrappedEtherByNetworkId {
};
}
-export interface ItemByAddress<T> {
- [address: string]: T;
+export type ItemByAddress<T> = ObjectMap<T>;
+
+export interface ObjectMap<T> {
+ [key: string]: T;
}
export type TokenStateByAddress = ItemByAddress<TokenState>;
diff --git a/packages/website/ts/utils/analytics.ts b/packages/website/ts/utils/analytics.ts
index f4bfa083f..e5a1ddfa4 100644
--- a/packages/website/ts/utils/analytics.ts
+++ b/packages/website/ts/utils/analytics.ts
@@ -1,27 +1,83 @@
import * as _ from 'lodash';
-import * as ReactGA from 'react-ga';
-import { InjectedWeb3 } from 'ts/types';
-import { configs } from 'ts/utils/configs';
+import { ObjectMap, Order } from 'ts/types';
import { utils } from 'ts/utils/utils';
-export const analytics = {
- init(): void {
- ReactGA.initialize(configs.GOOGLE_ANALYTICS_ID);
- },
- logEvent(category: string, action: string, label: string, value?: any): void {
- ReactGA.event({
- category,
- action,
- label,
- value,
- });
- },
- async logProviderAsync(web3IfExists: InjectedWeb3): Promise<void> {
- await utils.onPageLoadAsync();
- const providerType =
- !_.isUndefined(web3IfExists) && !_.isUndefined(web3IfExists.currentProvider)
- ? utils.getProviderType(web3IfExists.currentProvider)
- : 'NONE';
- ReactGA.ga('set', 'dimension1', providerType);
- },
-};
+export interface HeapAnalytics {
+ loaded: boolean;
+ identify(id: string, idType: string): void;
+ track(eventName: string, eventProperties?: ObjectMap<string | number>): void;
+ resetIdentity(): void;
+ addUserProperties(properties: ObjectMap<string | number>): void;
+ addEventProperties(properties: ObjectMap<string | number>): void;
+ removeEventProperty(property: string): void;
+ clearEventProperties(): void;
+}
+export class Analytics {
+ private _heap: HeapAnalytics;
+ public static init(): Analytics {
+ return new Analytics(Analytics.getHeap());
+ }
+ public static getHeap(): HeapAnalytics {
+ const heap = (window as any).heap;
+ if (!_.isUndefined(heap)) {
+ return heap;
+ } else {
+ throw new Error('Could not find the Heap SDK on the page.');
+ }
+ }
+ constructor(heap: HeapAnalytics) {
+ this._heap = heap;
+ }
+ // tslint:disable:no-floating-promises
+ // HeapAnalytics Wrappers
+ public identify(id: string, idType: string): void {
+ this._heapLoadedGuardAsync(() => this._heap.identify(id, idType));
+ }
+ public track(eventName: string, eventProperties?: ObjectMap<string | number>): void {
+ this._heapLoadedGuardAsync(() => this._heap.track(eventName, eventProperties));
+ }
+ public resetIdentity(): void {
+ this._heapLoadedGuardAsync(() => this._heap.resetIdentity());
+ }
+ public addUserProperties(properties: ObjectMap<string | number>): void {
+ this._heapLoadedGuardAsync(() => this._heap.addUserProperties(properties));
+ }
+ public addEventProperties(properties: ObjectMap<string | number>): void {
+ this._heapLoadedGuardAsync(() => this._heap.addEventProperties(properties));
+ }
+ public removeEventProperty(property: string): void {
+ this._heapLoadedGuardAsync(() => this._heap.removeEventProperty(property));
+ }
+ public clearEventProperties(): void {
+ this._heapLoadedGuardAsync(() => this._heap.clearEventProperties());
+ }
+ // tslint:enable:no-floating-promises
+ // Custom methods
+ public trackOrderEvent(eventName: string, order: Order): void {
+ const orderLoggingData = {
+ takerTokenAmount: order.signedOrder.takerTokenAmount,
+ makeTokenAmount: order.signedOrder.makerTokenAmount,
+ takerToken: order.metadata.takerToken.symbol,
+ makerToken: order.metadata.makerToken.symbol,
+ };
+ this.track(eventName, orderLoggingData);
+ }
+ /**
+ * Heap is not available as a UMD module, and additionally has the strange property of replacing itself with
+ * a different object once it's loaded.
+ * Instead of having an await call before every analytics use, we opt to have the awaiting logic in here by
+ * guarding every API call with the guard below.
+ */
+ private async _heapLoadedGuardAsync(callback: () => void): Promise<void> {
+ if (this._heap.loaded) {
+ callback();
+ return undefined;
+ }
+ await utils.onPageLoadPromise;
+ // HACK: Reset heap to loaded heap
+ this._heap = (window as any).heap;
+ callback();
+ }
+}
+
+export const analytics = Analytics.init();
diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts
index 835a6ef4d..5164211df 100644
--- a/packages/website/ts/utils/backend_client.ts
+++ b/packages/website/ts/utils/backend_client.ts
@@ -6,6 +6,7 @@ import {
WebsiteBackendJobInfo,
WebsiteBackendPriceInfo,
WebsiteBackendRelayerInfo,
+ WebsiteBackendTokenInfo,
} from 'ts/types';
import { fetchUtils } from 'ts/utils/fetch_utils';
import { utils } from 'ts/utils/utils';
@@ -14,6 +15,7 @@ const ETH_GAS_STATION_ENDPOINT = '/eth_gas_station';
const JOBS_ENDPOINT = '/jobs';
const PRICES_ENDPOINT = '/prices';
const RELAYERS_ENDPOINT = '/relayers';
+const TOKENS_ENDPOINT = '/tokens';
const WIKI_ENDPOINT = '/wiki';
const SUBSCRIBE_SUBSTACK_NEWSLETTER_ENDPOINT = '/newsletter_subscriber/substack';
@@ -41,6 +43,10 @@ export const backendClient = {
const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), RELAYERS_ENDPOINT);
return result;
},
+ async getTokenInfosAsync(): Promise<WebsiteBackendTokenInfo[]> {
+ const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), TOKENS_ENDPOINT);
+ return result;
+ },
async getWikiArticlesBySectionAsync(): Promise<ArticlesBySection> {
const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), WIKI_ENDPOINT);
return result;
diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts
index 97aabd13d..a1c64f9cb 100644
--- a/packages/website/ts/utils/configs.ts
+++ b/packages/website/ts/utils/configs.ts
@@ -1,11 +1,6 @@
-import * as _ from 'lodash';
-import { Environments, OutdatedWrappedEtherByNetworkId, PublicNodeUrlsByNetworkId } from 'ts/types';
+import { OutdatedWrappedEtherByNetworkId, PublicNodeUrlsByNetworkId } from 'ts/types';
const BASE_URL = window.location.origin;
-const isDevelopment = _.includes(
- ['https://0xproject.localhost:3572', 'https://localhost:3572', 'https://127.0.0.1'],
- BASE_URL,
-);
const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs';
export const configs = {
@@ -19,9 +14,8 @@ export const configs = {
DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'],
DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com',
DOMAIN_DOGFOOD: 'dogfood.0xproject.com',
- DOMAIN_DEVELOPMENT: '0xproject.localhost:3572',
+ DOMAINS_DEVELOPMENT: ['0xproject.localhost:3572', 'localhost:3572', '127.0.0.1'],
DOMAIN_PRODUCTION: '0xproject.com',
- ENVIRONMENT: isDevelopment ? Environments.DEVELOPMENT : Environments.PRODUCTION,
GOOGLE_ANALYTICS_ID: 'UA-98720122-1',
LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22',
LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2018-7-5',
diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts
index 4b3443d21..20441cd75 100644
--- a/packages/website/ts/utils/constants.ts
+++ b/packages/website/ts/utils/constants.ts
@@ -33,7 +33,7 @@ export const constants = {
PROVIDER_NAME_TOSHI: 'Toshi',
PROVIDER_NAME_GENERIC: 'Injected Web3',
PROVIDER_NAME_PUBLIC: '0x Public',
- ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65',
+ ROLLBAR_ACCESS_TOKEN: '32c39bfa4bb6440faedc1612a9c13d28',
S3_DOC_BUCKET_ROOT: 'https://s3.amazonaws.com/doc-jsons',
S3_STAGING_DOC_BUCKET_ROOT: 'https://s3.amazonaws.com/staging-doc-jsons',
SUCCESS_STATUS: 200,
@@ -58,7 +58,7 @@ export const constants = {
PROJECT_URL_MAKER: 'https://makerdao.com',
PROJECT_URL_ARAGON: 'https://aragon.one',
PROJECT_URL_BLOCKNET: 'https://blocknet.co',
- PROJECT_URL_0CEAN: 'http://the0cean.com',
+ PROJECT_URL_0CEAN: 'https://theocean.trade',
PROJECT_URL_IMTOKEN: 'https://tokenlon.token.im/',
PROJECT_URL_AUGUR: 'https://augur.net',
PROJECT_URL_AUCTUS: 'https://auctus.org',
diff --git a/packages/website/ts/utils/doc_utils.ts b/packages/website/ts/utils/doc_utils.ts
index 7768835fb..1627b9b0c 100644
--- a/packages/website/ts/utils/doc_utils.ts
+++ b/packages/website/ts/utils/doc_utils.ts
@@ -1,5 +1,5 @@
import { DoxityDocObj, TypeDocNode } from '@0xproject/react-docs';
-import { logUtils } from '@0xproject/utils';
+import { fetchAsync, logUtils } from '@0xproject/utils';
import findVersions = require('find-versions');
import * as _ from 'lodash';
import { S3FileObject, VersionToFilePath } from 'ts/types';
@@ -16,7 +16,7 @@ export const docUtils = {
return versionToFilePath;
},
async getVersionFileNamesAsync(s3DocJsonRoot: string, folderName: string): Promise<string[]> {
- const response = await fetch(s3DocJsonRoot);
+ const response = await fetchAsync(s3DocJsonRoot);
if (response.status !== 200) {
// TODO: Show the user an error message when the docs fail to load
const errMsg = await response.text();
@@ -73,7 +73,7 @@ export const docUtils = {
},
async getJSONDocFileAsync(filePath: string, s3DocJsonRoot: string): Promise<TypeDocNode | DoxityDocObj> {
const endpoint = `${s3DocJsonRoot}/${filePath}`;
- const response = await fetch(endpoint);
+ const response = await fetchAsync(endpoint);
if (response.status !== 200) {
// TODO: Show the user an error message when the docs fail to load
const errMsg = await response.text();
diff --git a/packages/website/ts/utils/error_reporter.ts b/packages/website/ts/utils/error_reporter.ts
index f875141fe..6008fffed 100644
--- a/packages/website/ts/utils/error_reporter.ts
+++ b/packages/website/ts/utils/error_reporter.ts
@@ -1,7 +1,7 @@
import { logUtils } from '@0xproject/utils';
-import { Environments } from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
+import { utils } from 'ts/utils/utils';
// Suggested way to include Rollbar with Webpack
// https://github.com/rollbar/rollbar.js/tree/master/examples/webpack
@@ -12,7 +12,15 @@ const rollbarConfig = {
itemsPerMinute: 10,
maxItems: 500,
payload: {
- environment: configs.ENVIRONMENT,
+ environment: utils.getEnvironment(),
+ client: {
+ javascript: {
+ source_map_enabled: true,
+ // This is only defined in production environments.
+ code_version: process.env.GIT_SHA,
+ guess_uncaught_frames: true,
+ },
+ },
},
uncaughtErrorLevel: 'error',
hostWhiteList: [configs.DOMAIN_PRODUCTION, configs.DOMAIN_STAGING],
@@ -28,25 +36,18 @@ const rollbarConfig = {
'SecurityError (DOM Exception 18)',
],
};
-import Rollbar = require('../../public/js/rollbar.umd.nojson.min.js');
+import Rollbar = require('../../public/js/rollbar.umd.min.js');
const rollbar = Rollbar.init(rollbarConfig);
export const errorReporter = {
- async reportAsync(err: Error): Promise<any> {
- if (configs.ENVIRONMENT === Environments.DEVELOPMENT) {
+ report(err: Error): void {
+ if (utils.isDevelopment()) {
return; // Let's not log development errors to rollbar
}
-
- return new Promise((resolve, _reject) => {
- rollbar.error(err, (rollbarErr: Error) => {
- if (rollbarErr) {
- logUtils.log(`Error reporting to rollbar, ignoring: ${rollbarErr}`);
- // We never want to reject and cause the app to throw because of rollbar
- resolve();
- } else {
- resolve();
- }
- });
+ rollbar.error(err, (rollbarErr: Error) => {
+ if (rollbarErr) {
+ logUtils.log(`Error reporting to rollbar, ignoring: ${rollbarErr}`);
+ }
});
},
};
diff --git a/packages/website/ts/utils/fetch_utils.ts b/packages/website/ts/utils/fetch_utils.ts
index 513f7e479..e9a88b6b3 100644
--- a/packages/website/ts/utils/fetch_utils.ts
+++ b/packages/website/ts/utils/fetch_utils.ts
@@ -1,4 +1,4 @@
-import { logUtils } from '@0xproject/utils';
+import { fetchAsync, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import * as queryString from 'query-string';
@@ -9,8 +9,7 @@ const logErrorIfPresent = (response: Response, requestedURL: string) => {
const errorText = `Error requesting url: ${requestedURL}, ${response.status}: ${response.statusText}`;
logUtils.log(errorText);
const error = Error(errorText);
- // tslint:disable-next-line:no-floating-promises
- errorReporter.reportAsync(error);
+ errorReporter.report(error);
throw error;
}
};
@@ -19,14 +18,14 @@ export const fetchUtils = {
async requestAsync(baseUrl: string, path: string, queryParams?: object): Promise<any> {
const query = queryStringFromQueryParams(queryParams);
const url = `${baseUrl}${path}${query}`;
- const response = await fetch(url);
+ const response = await fetchAsync(url);
logErrorIfPresent(response, url);
const result = await response.json();
return result;
},
async postAsync(baseUrl: string, path: string, body: object): Promise<Response> {
const url = `${baseUrl}${path}`;
- const response = await fetch(url, {
+ const response = await fetchAsync(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts
index 8c76a7592..e656d5963 100644
--- a/packages/website/ts/utils/utils.ts
+++ b/packages/website/ts/utils/utils.ts
@@ -30,8 +30,6 @@ import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import * as u2f from 'ts/vendor/u2f_api';
-const isDogfood = (): boolean => _.includes(window.location.href, configs.DOMAIN_DOGFOOD);
-
export const utils = {
assert(condition: boolean, message: string): void {
if (!condition) {
@@ -61,7 +59,7 @@ export const utils = {
return moment.unix(unixTimestampSec.toNumber());
},
convertToReadableDateTimeFromUnixTimestamp(unixTimestampSec: BigNumber): string {
- const m = this.convertToMomentFromUnixTimestamp(unixTimestampSec);
+ const m = utils.convertToMomentFromUnixTimestamp(unixTimestampSec);
const formattedDate: string = m.format('h:MMa MMMM D YYYY');
return formattedDate;
},
@@ -177,18 +175,6 @@ export const utils = {
_.includes(errMsg, ledgerDenialErrMsg);
return isUserDeniedErrMsg;
},
- getCurrentEnvironment(): string {
- switch (location.host) {
- case configs.DOMAIN_DEVELOPMENT:
- return 'development';
- case configs.DOMAIN_STAGING:
- return 'staging';
- case configs.DOMAIN_PRODUCTION:
- return 'production';
- default:
- return 'production';
- }
- },
getAddressBeginAndEnd(address: string): string {
const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287
return truncatedAddress;
@@ -313,14 +299,13 @@ export const utils = {
const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}`;
return baseUrl;
},
- async onPageLoadAsync(): Promise<void> {
+ onPageLoadPromise: new Promise<void>((resolve, _reject) => {
if (document.readyState === 'complete') {
- return; // Already loaded
+ resolve();
+ return;
}
- return new Promise<void>((resolve, _reject) => {
- window.onload = () => resolve();
- });
- },
+ window.onload = () => resolve();
+ }),
getProviderType(provider: Provider): Providers | string {
const constructorName = provider.constructor.name;
let parsedProviderName = constructorName;
@@ -346,10 +331,10 @@ export const utils = {
return parsedProviderName;
},
getBackendBaseUrl(): string {
- return isDogfood() ? configs.BACKEND_BASE_STAGING_URL : configs.BACKEND_BASE_PROD_URL;
+ return utils.isDogfood() ? configs.BACKEND_BASE_STAGING_URL : configs.BACKEND_BASE_PROD_URL;
},
isDevelopment(): boolean {
- return configs.ENVIRONMENT === Environments.DEVELOPMENT;
+ return _.includes(configs.DOMAINS_DEVELOPMENT, window.location.origin);
},
isStaging(): boolean {
return _.includes(window.location.href, configs.DOMAIN_STAGING);
@@ -357,14 +342,29 @@ export const utils = {
isExternallyInjected(providerType: ProviderType, injectedProviderName: string): boolean {
return providerType === ProviderType.Injected && injectedProviderName !== constants.PROVIDER_NAME_PUBLIC;
},
- isDogfood,
- shouldShowPortalV2(): boolean {
- // return this.isDevelopment() || this.isStaging() || this.isDogfood();
- // TODO: Remove this method entirely after launch.
- return true;
+ isDogfood(): boolean {
+ return _.includes(window.location.href, configs.DOMAIN_DOGFOOD);
+ },
+ isProduction(): boolean {
+ return _.includes(window.location.href, configs.DOMAIN_PRODUCTION);
+ },
+ getEnvironment(): Environments {
+ if (utils.isDogfood()) {
+ return Environments.DOGFOOD;
+ }
+ if (utils.isDevelopment()) {
+ return Environments.DEVELOPMENT;
+ }
+ if (utils.isStaging()) {
+ return Environments.STAGING;
+ }
+ if (utils.isProduction()) {
+ return Environments.PRODUCTION;
+ }
+ return Environments.UNKNOWN;
},
shouldShowJobsPage(): boolean {
- return this.isDevelopment() || this.isStaging() || this.isDogfood();
+ return utils.isDevelopment() || utils.isStaging() || utils.isDogfood();
},
getEthToken(tokenByAddress: TokenByAddress): Token {
return utils.getTokenBySymbol(constants.ETHER_TOKEN_SYMBOL, tokenByAddress);
@@ -379,27 +379,34 @@ export const utils = {
},
getTrackedTokens(tokenByAddress: TokenByAddress): Token[] {
const allTokens = _.values(tokenByAddress);
- const trackedTokens = _.filter(allTokens, t => this.isTokenTracked(t));
+ const trackedTokens = _.filter(allTokens, t => utils.isTokenTracked(t));
return trackedTokens;
},
getFormattedAmountFromToken(token: Token, tokenState: TokenState): string {
return utils.getFormattedAmount(tokenState.balance, token.decimals);
},
+ format(value: BigNumber, format: string): string {
+ const formattedAmount = numeral(value).format(format);
+ if (_.isNaN(formattedAmount)) {
+ // https://github.com/adamwdraper/Numeral-js/issues/596
+ return numeral(new BigNumber(0)).format(format);
+ }
+ return formattedAmount;
+ },
getFormattedAmount(amount: BigNumber, decimals: number): string {
const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
// if the unit amount is less than 1, show the natural number of decimal places with a max of 4
// if the unit amount is greater than or equal to 1, show only 2 decimal places
- const precision = unitAmount.lt(1)
- ? Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces())
- : 2;
+ const lessThanOnePrecision = Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces());
+ const greaterThanOnePrecision = 2;
+ const precision = unitAmount.lt(1) ? lessThanOnePrecision : greaterThanOnePrecision;
const format = `0,0.${_.repeat('0', precision)}`;
- const formattedAmount = numeral(unitAmount).format(format);
- return formattedAmount;
+ return utils.format(unitAmount, format);
},
getUsdValueFormattedAmount(amount: BigNumber, decimals: number, price: BigNumber): string {
const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals);
const value = unitAmount.mul(price);
- return numeral(value).format(constants.NUMERAL_USD_FORMAT);
+ return utils.format(value, constants.NUMERAL_USD_FORMAT);
},
openUrl(url: string): void {
window.open(url, '_blank');