aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/website/ts')
-rw-r--r--packages/website/ts/blockchain.ts57
-rw-r--r--packages/website/ts/blockchain_watcher.ts14
-rw-r--r--packages/website/ts/components/dialogs/blockchain_err_dialog.tsx16
-rw-r--r--packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx20
-rw-r--r--packages/website/ts/components/dialogs/ledger_config_dialog.tsx20
-rw-r--r--packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx4
-rw-r--r--packages/website/ts/components/dialogs/send_dialog.tsx14
-rw-r--r--packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx4
-rw-r--r--packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx4
-rw-r--r--packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx4
-rw-r--r--packages/website/ts/components/dropdowns/network_drop_down.tsx4
-rw-r--r--packages/website/ts/components/eth_weth_conversion_button.tsx6
-rw-r--r--packages/website/ts/components/eth_wrappers.tsx24
-rw-r--r--packages/website/ts/components/fill_order.tsx32
-rw-r--r--packages/website/ts/components/fill_order_json.tsx2
-rw-r--r--packages/website/ts/components/fill_warning_dialog.tsx4
-rw-r--r--packages/website/ts/components/flash_messages/token_send_completed.tsx2
-rw-r--r--packages/website/ts/components/flash_messages/transaction_submitted.tsx2
-rw-r--r--packages/website/ts/components/footer.tsx10
-rw-r--r--packages/website/ts/components/generate_order/asset_picker.tsx20
-rw-r--r--packages/website/ts/components/generate_order/generate_order_form.tsx8
-rw-r--r--packages/website/ts/components/generate_order/new_token_form.tsx18
-rw-r--r--packages/website/ts/components/inputs/address_input.tsx6
-rw-r--r--packages/website/ts/components/inputs/allowance_toggle.tsx6
-rw-r--r--packages/website/ts/components/inputs/balance_bounded_input.tsx55
-rw-r--r--packages/website/ts/components/inputs/eth_amount_input.tsx15
-rw-r--r--packages/website/ts/components/inputs/expiration_input.tsx8
-rw-r--r--packages/website/ts/components/inputs/hash_input.tsx4
-rw-r--r--packages/website/ts/components/inputs/identicon_address_input.tsx2
-rw-r--r--packages/website/ts/components/inputs/token_amount_input.tsx40
-rw-r--r--packages/website/ts/components/inputs/token_input.tsx8
-rw-r--r--packages/website/ts/components/legacy_portal/legacy_portal.tsx (renamed from packages/website/ts/components/portal.tsx)97
-rw-r--r--packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx (renamed from packages/website/ts/components/portal_menu.tsx)32
-rw-r--r--packages/website/ts/components/order_json.tsx12
-rw-r--r--packages/website/ts/components/portal/back_button.tsx41
-rw-r--r--packages/website/ts/components/portal/loading.tsx21
-rw-r--r--packages/website/ts/components/portal/menu.tsx88
-rw-r--r--packages/website/ts/components/portal/portal.tsx468
-rw-r--r--packages/website/ts/components/portal/section.tsx15
-rw-r--r--packages/website/ts/components/portal/text_header.tsx21
-rw-r--r--packages/website/ts/components/redirecter.tsx2
-rw-r--r--packages/website/ts/components/relayer_index/relayer_grid_tile.tsx83
-rw-r--r--packages/website/ts/components/relayer_index/relayer_index.tsx70
-rw-r--r--packages/website/ts/components/relayer_index/relayer_top_tokens.tsx63
-rw-r--r--packages/website/ts/components/send_button.tsx6
-rw-r--r--packages/website/ts/components/sidebar_header.tsx2
-rw-r--r--packages/website/ts/components/token_balances.tsx40
-rw-r--r--packages/website/ts/components/top_bar/provider_display.tsx40
-rw-r--r--packages/website/ts/components/top_bar/provider_picker.tsx8
-rw-r--r--packages/website/ts/components/top_bar/top_bar.tsx128
-rw-r--r--packages/website/ts/components/top_bar/top_bar_menu_item.tsx2
-rw-r--r--packages/website/ts/components/track_token_confirmation.tsx2
-rw-r--r--packages/website/ts/components/trade_history/trade_history.tsx20
-rw-r--r--packages/website/ts/components/trade_history/trade_history_item.tsx8
-rw-r--r--packages/website/ts/components/ui/alert.tsx4
-rw-r--r--packages/website/ts/components/ui/copy_icon.tsx12
-rw-r--r--packages/website/ts/components/ui/drop_down.tsx18
-rw-r--r--packages/website/ts/components/ui/etherscan_icon.tsx2
-rw-r--r--packages/website/ts/components/ui/fake_text_field.tsx4
-rw-r--r--packages/website/ts/components/ui/flash_message.tsx4
-rw-r--r--packages/website/ts/components/ui/identicon.tsx2
-rw-r--r--packages/website/ts/components/ui/input_label.tsx2
-rw-r--r--packages/website/ts/components/ui/lifecycle_raised_button.tsx6
-rw-r--r--packages/website/ts/components/ui/menu_item.tsx4
-rw-r--r--packages/website/ts/components/ui/party.tsx2
-rw-r--r--packages/website/ts/components/ui/swap_icon.tsx4
-rw-r--r--packages/website/ts/components/ui/token_icon.tsx2
-rw-r--r--packages/website/ts/components/visual_order.tsx4
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx209
-rw-r--r--packages/website/ts/components/wallet/wrap_ether_item.tsx66
-rw-r--r--packages/website/ts/containers/legacy_portal.ts92
-rw-r--r--packages/website/ts/containers/order_utils_documentation.ts99
-rw-r--r--packages/website/ts/containers/portal.ts6
-rw-r--r--packages/website/ts/containers/sol_compiler_documentation.ts (renamed from packages/website/ts/containers/deployer_documentation.ts)38
-rw-r--r--packages/website/ts/containers/subproviders_documentation.ts2
-rw-r--r--packages/website/ts/containers/zero_ex_js_documentation.ts10
-rw-r--r--packages/website/ts/index.tsx31
-rw-r--r--packages/website/ts/lazy_component.tsx8
-rw-r--r--packages/website/ts/local_storage/local_storage.ts6
-rw-r--r--packages/website/ts/local_storage/trade_history_storage.tsx12
-rw-r--r--packages/website/ts/pages/about/about.tsx42
-rw-r--r--packages/website/ts/pages/about/profile.tsx8
-rw-r--r--packages/website/ts/pages/documentation/doc_page.tsx13
-rw-r--r--packages/website/ts/pages/faq/faq.tsx8
-rw-r--r--packages/website/ts/pages/faq/question.tsx4
-rw-r--r--packages/website/ts/pages/fullscreen_message.tsx30
-rw-r--r--packages/website/ts/pages/landing/landing.tsx62
-rw-r--r--packages/website/ts/pages/not_found.tsx43
-rw-r--r--packages/website/ts/pages/wiki/wiki.tsx20
-rw-r--r--packages/website/ts/redux/dispatcher.ts60
-rw-r--r--packages/website/ts/redux/reducer.ts2
-rw-r--r--packages/website/ts/types.ts26
-rw-r--r--packages/website/ts/utils/analytics.ts6
-rw-r--r--packages/website/ts/utils/backend_client.ts58
-rw-r--r--packages/website/ts/utils/configs.ts5
-rw-r--r--packages/website/ts/utils/fetch_utils.ts33
-rw-r--r--packages/website/ts/utils/translate.ts8
-rw-r--r--packages/website/ts/utils/utils.ts80
98 files changed, 2003 insertions, 856 deletions
diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts
index b7ba2241c..32acb9d43 100644
--- a/packages/website/ts/blockchain.ts
+++ b/packages/website/ts/blockchain.ts
@@ -39,6 +39,7 @@ import {
BlockchainCallErrs,
BlockchainErrs,
ContractInstance,
+ Fill,
Order as PortalOrder,
Providers,
ProviderType,
@@ -88,7 +89,7 @@ export class Blockchain {
}
return providerNameIfExists;
}
- private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) {
+ private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number): Promise<Provider> {
const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists];
const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId);
@@ -137,7 +138,7 @@ export class Blockchain {
// tslint:disable-next-line:no-floating-promises
this._onPageLoadInitFireAndForgetAsync();
}
- public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number) {
+ public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number): Promise<void> {
const isConnected = !_.isUndefined(newNetworkId);
if (!isConnected) {
this.networkId = newNetworkId;
@@ -147,17 +148,17 @@ export class Blockchain {
this.networkId = newNetworkId;
this._dispatcher.encounteredBlockchainError(BlockchainErrs.NoError);
await this.fetchTokenInformationAsync();
- await this._rehydrateStoreWithContractEvents();
+ await this._rehydrateStoreWithContractEventsAsync();
}
}
- public async userAddressUpdatedFireAndForgetAsync(newUserAddress: string) {
+ public async userAddressUpdatedFireAndForgetAsync(newUserAddress: string): Promise<void> {
if (this._userAddressIfExists !== newUserAddress) {
this._userAddressIfExists = newUserAddress;
await this.fetchTokenInformationAsync();
- await this._rehydrateStoreWithContractEvents();
+ await this._rehydrateStoreWithContractEventsAsync();
}
}
- public async nodeVersionUpdatedFireAndForgetAsync(nodeVersion: string) {
+ public async nodeVersionUpdatedFireAndForgetAsync(nodeVersion: string): Promise<void> {
if (this.nodeVersion !== nodeVersion) {
this.nodeVersion = nodeVersion;
}
@@ -174,13 +175,13 @@ export class Blockchain {
const path = this._ledgerSubprovider.getPath();
return path;
}
- public updateLedgerDerivationPathIfExists(path: string) {
+ public updateLedgerDerivationPathIfExists(path: string): void {
if (_.isUndefined(this._ledgerSubprovider)) {
return; // noop
}
this._ledgerSubprovider.setPath(path);
}
- public async updateProviderToLedgerAsync(networkId: number) {
+ public async updateProviderToLedgerAsync(networkId: number): Promise<void> {
utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
const isU2FSupported = await utils.isU2FSupportedAsync();
@@ -228,7 +229,7 @@ export class Blockchain {
this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceState();
this._dispatcher.updateProviderType(ProviderType.Ledger);
}
- public async updateProviderToInjectedAsync() {
+ public async updateProviderToInjectedAsync(): Promise<void> {
utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
if (_.isUndefined(this._cachedProvider)) {
@@ -367,7 +368,7 @@ export class Blockchain {
const unavailableTakerAmount = await this._zeroEx.exchange.getUnavailableTakerAmountAsync(orderHash);
return unavailableTakerAmount;
}
- public getExchangeContractAddressIfExists() {
+ public getExchangeContractAddressIfExists(): string | undefined {
return this._zeroEx.exchange.getContractAddress();
}
public async validateFillOrderThrowIfInvalidAsync(
@@ -391,7 +392,7 @@ export class Blockchain {
const lowercaseAddress = address.toLowerCase();
return Web3Wrapper.isAddress(lowercaseAddress);
}
- public async pollTokenBalanceAsync(token: Token) {
+ public async pollTokenBalanceAsync(token: Token): Promise<BigNumber> {
utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
const [currBalance] = await this.getTokenBalanceAndAllowanceAsync(this._userAddressIfExists, token.address);
@@ -445,7 +446,7 @@ export class Blockchain {
this._dispatcher.updateECSignature(ecSignature);
return ecSignature;
}
- public async mintTestTokensAsync(token: Token) {
+ public async mintTestTokensAsync(token: Token): Promise<void> {
utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
const mintableContract = await this._instantiateContractIfExistsAsync(MintableArtifacts, token.address);
@@ -489,7 +490,7 @@ export class Blockchain {
);
await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash);
}
- public async doesContractExistAtAddressAsync(address: string) {
+ public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(address);
return doesContractExist;
}
@@ -520,7 +521,7 @@ export class Blockchain {
}
return [balance, allowance];
}
- public async getUserAccountsAsync() {
+ public async getUserAccountsAsync(): Promise<string[]> {
utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.');
const userAccountsIfExists = await this._zeroEx.getAvailableAddressesAsync();
return userAccountsIfExists;
@@ -528,14 +529,14 @@ export class Blockchain {
// HACK: When a user is using a Ledger, we simply dispatch the selected userAddress, which
// by-passes the web3Wrapper logic for updating the prevUserAddress. We therefore need to
// manually update it. This should only be called by the LedgerConfigDialog.
- public updateWeb3WrapperPrevUserAddress(newUserAddress: string) {
+ public updateWeb3WrapperPrevUserAddress(newUserAddress: string): void {
this._blockchainWatcher.updatePrevUserAddress(newUserAddress);
}
- public destroy() {
+ public destroy(): void {
this._blockchainWatcher.destroy();
this._stopWatchingExchangeLogFillEvents();
}
- public async fetchTokenInformationAsync() {
+ public async fetchTokenInformationAsync(): Promise<void> {
utils.assert(
!_.isUndefined(this.networkId),
'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node',
@@ -624,7 +625,7 @@ export class Blockchain {
private _doesUserAddressExist(): boolean {
return !_.isUndefined(this._userAddressIfExists);
}
- private async _rehydrateStoreWithContractEvents() {
+ private async _rehydrateStoreWithContractEventsAsync(): Promise<void> {
// Ensure we are only ever listening to one set of events
this._stopWatchingExchangeLogFillEvents();
@@ -675,7 +676,7 @@ export class Blockchain {
},
);
}
- private async _fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues) {
+ private async _fetchHistoricalExchangeLogFillEventsAsync(indexFilterValues: IndexedFilterValues): Promise<void> {
utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
const fromBlock = tradeHistoryStorage.getFillsLatestBlock(this._userAddressIfExists, this.networkId);
@@ -697,7 +698,9 @@ export class Blockchain {
tradeHistoryStorage.addFillToUser(this._userAddressIfExists, this.networkId, fill);
}
}
- private async _convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
+ private async _convertDecodedLogToFillAsync(
+ decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>,
+ ): Promise<Fill> {
const args = decodedLog.args;
const blockTimestamp = await this._web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash);
const fill = {
@@ -716,12 +719,12 @@ export class Blockchain {
};
return fill;
}
- private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>) {
+ private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>): boolean {
const args = decodedLog.args;
const isUserMakerOrTaker = args.maker === this._userAddressIfExists || args.taker === this._userAddressIfExists;
return isUserMakerOrTaker;
}
- private _updateLatestFillsBlockIfNeeded(blockNumber: number) {
+ private _updateLatestFillsBlockIfNeeded(blockNumber: number): void {
utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses);
const isBlockPending = _.isNull(blockNumber);
@@ -762,7 +765,7 @@ export class Blockchain {
});
return tokenByAddress;
}
- private async _onPageLoadInitFireAndForgetAsync() {
+ private async _onPageLoadInitFireAndForgetAsync(): Promise<void> {
await utils.onPageLoadAsync(); // wait for page to load
// Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in
@@ -807,9 +810,9 @@ export class Blockchain {
this._dispatcher.updateUserAddress(this._userAddressIfExists);
await this.fetchTokenInformationAsync();
this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceState();
- await this._rehydrateStoreWithContractEvents();
+ await this._rehydrateStoreWithContractEventsAsync();
}
- private _updateProviderName(injectedWeb3: Web3) {
+ private _updateProviderName(injectedWeb3: Web3): void {
const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3);
const providerName = doesInjectedWeb3Exist
? Blockchain._getNameGivenProvider(injectedWeb3.currentProvider)
@@ -851,12 +854,12 @@ export class Blockchain {
}
}
}
- private _showFlashMessageIfLedger() {
+ private _showFlashMessageIfLedger(): void {
if (!_.isUndefined(this._ledgerSubprovider)) {
this._dispatcher.showFlashMessage('Confirm the transaction on your Ledger Nano S');
}
}
- private async _updateDefaultGasPriceAsync() {
+ private async _updateDefaultGasPriceAsync(): Promise<void> {
try {
const gasInfo = await backendClient.getGasInfoAsync();
const gasPriceInGwei = new BigNumber(gasInfo.average / 10);
diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts
index 2712b2c04..c420a98a4 100644
--- a/packages/website/ts/blockchain_watcher.ts
+++ b/packages/website/ts/blockchain_watcher.ts
@@ -23,8 +23,8 @@ export class BlockchainWatcher {
this._shouldPollUserAddress = shouldPollUserAddress;
this._web3Wrapper = web3Wrapper;
}
- public destroy() {
- this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
+ public destroy(): void {
+ this._stopEmittingNetworkConnectionAndUserBalanceState();
// HACK: stop() is only available on providerEngine instances
const provider = this._web3Wrapper.getProvider();
if (!_.isUndefined((provider as any).stop)) {
@@ -32,10 +32,10 @@ export class BlockchainWatcher {
}
}
// This should only be called from the LedgerConfigDialog
- public updatePrevUserAddress(userAddress: string) {
+ public updatePrevUserAddress(userAddress: string): void {
this._prevUserAddressIfExists = userAddress;
}
- public startEmittingNetworkConnectionAndUserBalanceState() {
+ public startEmittingNetworkConnectionAndUserBalanceState(): void {
if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
return; // we are already emitting the state
}
@@ -88,18 +88,18 @@ export class BlockchainWatcher {
5000,
(err: Error) => {
logUtils.log(`Watching network and balances failed: ${err.stack}`);
- this._stopEmittingNetworkConnectionAndUserBalanceStateAsync();
+ this._stopEmittingNetworkConnectionAndUserBalanceState();
},
);
}
- private async _updateUserWeiBalanceAsync(userAddress: string) {
+ private async _updateUserWeiBalanceAsync(userAddress: string): Promise<void> {
const balanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(userAddress);
if (!balanceInWei.eq(this._prevUserEtherBalanceInWei)) {
this._prevUserEtherBalanceInWei = balanceInWei;
this._dispatcher.updateUserWeiBalance(balanceInWei);
}
}
- private _stopEmittingNetworkConnectionAndUserBalanceStateAsync() {
+ private _stopEmittingNetworkConnectionAndUserBalanceState(): void {
if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) {
intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId);
}
diff --git a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
index 1c3b7458d..7156e700b 100644
--- a/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
+++ b/packages/website/ts/components/dialogs/blockchain_err_dialog.tsx
@@ -18,7 +18,7 @@ interface BlockchainErrDialogProps {
}
export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProps, undefined> {
- public render() {
+ public render(): React.ReactNode {
const dialogActions = [
<FlatButton
key="blockchainErrOk"
@@ -45,7 +45,7 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
</Dialog>
);
}
- private _getTitle(hasWalletAddress: boolean) {
+ private _getTitle(hasWalletAddress: boolean): string {
if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) {
return '0x smart contracts not found';
} else if (!hasWalletAddress) {
@@ -58,7 +58,7 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
return 'Unexpected error';
}
}
- private _renderExplanation(hasWalletAddress: boolean) {
+ private _renderExplanation(hasWalletAddress: boolean): React.ReactNode {
if (this.props.blockchainErr === BlockchainErrs.AContractNotDeployedOnNetwork) {
return this._renderContractsNotDeployedExplanation();
} else if (!hasWalletAddress) {
@@ -71,7 +71,7 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
return this._renderUnexpectedErrorExplanation();
}
}
- private _renderDisconnectedFromNode() {
+ private _renderDisconnectedFromNode(): React.ReactNode {
return (
<div>
You were disconnected from the backing Ethereum node. If using{' '}
@@ -86,7 +86,7 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
</div>
);
}
- private _renderDefaultTokenNotInTokenRegistry() {
+ private _renderDefaultTokenNotInTokenRegistry(): React.ReactNode {
return (
<div>
The TokenRegistry deployed on your network does not contain the needed default tokens for 0x Portal to
@@ -96,10 +96,10 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
</div>
);
}
- private _renderUnexpectedErrorExplanation() {
+ private _renderUnexpectedErrorExplanation(): React.ReactNode {
return <div>We encountered an unexpected error. Please try refreshing the page.</div>;
}
- private _renderNoWalletFoundExplanation() {
+ private _renderNoWalletFoundExplanation(): React.ReactNode {
return (
<div>
<div>
@@ -137,7 +137,7 @@ export class BlockchainErrDialog extends React.Component<BlockchainErrDialogProp
</div>
);
}
- private _renderContractsNotDeployedExplanation() {
+ private _renderContractsNotDeployedExplanation(): React.ReactNode {
return (
<div>
<div>
diff --git a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
index 42ca1713d..069a75560 100644
--- a/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
+++ b/packages/website/ts/components/dialogs/eth_weth_conversion_dialog.tsx
@@ -47,14 +47,14 @@ export class EthWethConversionDialog extends React.Component<
ethTokenBalance: new BigNumber(0),
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
// tslint:disable-next-line:no-floating-promises
this._fetchEthTokenBalanceAsync();
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
}
- public render() {
+ public render(): React.ReactNode {
const convertDialogActions = [
<FlatButton key="cancel" label="Cancel" onTouchTap={this._onCancel.bind(this)} />,
<FlatButton key="convert" label="Convert" primary={true} onTouchTap={this._onConvertClick.bind(this)} />,
@@ -72,7 +72,7 @@ export class EthWethConversionDialog extends React.Component<
</Dialog>
);
}
- private _renderConversionDialogBody() {
+ private _renderConversionDialogBody(): React.ReactNode {
const explanation =
this.props.direction === Side.Deposit
? 'Convert your Ether into a tokenized, tradable form.'
@@ -137,7 +137,7 @@ export class EthWethConversionDialog extends React.Component<
</div>
);
}
- private _renderCurrency(isWrappedVersion: boolean) {
+ private _renderCurrency(isWrappedVersion: boolean): React.ReactNode {
const name = isWrappedVersion ? 'Wrapped Ether' : 'Ether';
const iconUrl = isWrappedVersion ? '/images/token_icons/ether_erc20.png' : '/images/ether.png';
const symbol = isWrappedVersion ? 'WETH' : 'ETH';
@@ -155,18 +155,18 @@ export class EthWethConversionDialog extends React.Component<
</div>
);
}
- private _onMaxClick() {
+ private _onMaxClick(): void {
this.setState({
value: this.state.ethTokenBalance,
});
}
- private _onValueChange(isValid: boolean, amount?: BigNumber) {
+ private _onValueChange(isValid: boolean, amount?: BigNumber): void {
this.setState({
value: amount,
hasErrors: !isValid,
});
}
- private _onConvertClick() {
+ private _onConvertClick(): void {
if (this.state.hasErrors) {
this.setState({
shouldShowIncompleteErrs: true,
@@ -179,13 +179,13 @@ export class EthWethConversionDialog extends React.Component<
this.props.onComplete(this.props.direction, value);
}
}
- private _onCancel() {
+ private _onCancel(): void {
this.setState({
value: undefined,
});
this.props.onCancelled();
}
- private async _fetchEthTokenBalanceAsync() {
+ private async _fetchEthTokenBalanceAsync(): Promise<void> {
const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [balance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
userAddressIfExists,
diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
index a72d33183..3c839d6f5 100644
--- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
+++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx
@@ -59,7 +59,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
preferredNetworkId: props.networkId,
};
}
- public render() {
+ public render(): React.ReactNode {
const dialogActions = [
<FlatButton key="ledgerConnectCancel" label="Cancel" onTouchTap={this._onClose.bind(this)} />,
];
@@ -82,7 +82,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
</Dialog>
);
}
- private _renderConnectStep() {
+ private _renderConnectStep(): React.ReactNode {
const networkIds = _.values(sharedConstants.NETWORK_ID_BY_NAME);
return (
<div>
@@ -122,7 +122,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
</div>
);
}
- private _renderSelectAddressStep() {
+ private _renderSelectAddressStep(): React.ReactNode {
return (
<div>
<div>
@@ -159,7 +159,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
</div>
);
}
- private _renderAddressTableRows() {
+ private _renderAddressTableRows(): React.ReactNode {
const rows = _.map(this.state.userAddresses, (userAddress: string, i: number) => {
const balanceInWei = this.state.addressBalances[i];
const addressTooltipId = `address-${userAddress}`;
@@ -189,7 +189,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
});
return rows;
}
- private _onClose() {
+ private _onClose(): void {
this.setState({
connectionErrMsg: '',
stepIndex: LedgerSteps.CONNECT,
@@ -197,7 +197,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
const isOpen = false;
this.props.toggleDialogFn(isOpen);
}
- private _onAddressSelected(selectedRowIndexes: number[]) {
+ private _onAddressSelected(selectedRowIndexes: number[]): void {
const selectedRowIndex = selectedRowIndexes[0];
const selectedAddress = this.state.userAddresses[selectedRowIndex];
const selectAddressBalance = this.state.addressBalances[selectedRowIndex];
@@ -228,7 +228,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
}
return didSucceed;
}
- private async _fetchAddressesAndBalancesAsync() {
+ private async _fetchAddressesAndBalancesAsync(): Promise<boolean> {
let userAddresses: string[];
const addressBalances: BigNumber[] = [];
try {
@@ -250,7 +250,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
});
return true;
}
- private _onDerivationPathChanged(e: any, derivationPath: string) {
+ private _onDerivationPathChanged(e: any, derivationPath: string): void {
let derivationErrMsg = '';
if (!_.startsWith(derivationPath, VALID_ETHEREUM_DERIVATION_PATH_PREFIX)) {
derivationErrMsg = 'Must be valid Ethereum path.';
@@ -261,7 +261,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
derivationErrMsg,
});
}
- private async _onConnectLedgerClickAsync() {
+ private async _onConnectLedgerClickAsync(): Promise<boolean> {
const isU2FSupported = await utils.isU2FSupportedAsync();
if (!isU2FSupported) {
logUtils.log(`U2F not supported in this browser`);
@@ -295,7 +295,7 @@ export class LedgerConfigDialog extends React.Component<LedgerConfigDialogProps,
}
return userAddresses;
}
- private _onSelectedNetworkUpdated(e: any, index: number, networkId: number) {
+ private _onSelectedNetworkUpdated(e: any, index: number, networkId: number): void {
this.setState({
preferredNetworkId: networkId,
});
diff --git a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
index b31667121..41a17fe96 100644
--- a/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
+++ b/packages/website/ts/components/dialogs/portal_disclaimer_dialog.tsx
@@ -8,7 +8,7 @@ interface PortalDisclaimerDialogProps {
onToggleDialog: () => void;
}
-export function PortalDisclaimerDialog(props: PortalDisclaimerDialogProps) {
+export const PortalDisclaimerDialog = (props: PortalDisclaimerDialogProps) => {
return (
<Dialog
title="0x Portal Disclaimer"
@@ -33,4 +33,4 @@ export function PortalDisclaimerDialog(props: PortalDisclaimerDialogProps) {
</div>
</Dialog>
);
-}
+};
diff --git a/packages/website/ts/components/dialogs/send_dialog.tsx b/packages/website/ts/components/dialogs/send_dialog.tsx
index 2af7fd7ac..421f18b4f 100644
--- a/packages/website/ts/components/dialogs/send_dialog.tsx
+++ b/packages/website/ts/components/dialogs/send_dialog.tsx
@@ -35,7 +35,7 @@ export class SendDialog extends React.Component<SendDialogProps, SendDialogState
isAmountValid: false,
};
}
- public render() {
+ public render(): React.ReactNode {
const transferDialogActions = [
<FlatButton key="cancelTransfer" label="Cancel" onTouchTap={this._onCancel.bind(this)} />,
<FlatButton
@@ -57,7 +57,7 @@ export class SendDialog extends React.Component<SendDialogProps, SendDialogState
</Dialog>
);
}
- private _renderSendDialogBody() {
+ private _renderSendDialogBody(): React.ReactNode {
return (
<div className="mx-auto" style={{ maxWidth: 300 }}>
<div style={{ height: 80 }}>
@@ -86,19 +86,19 @@ export class SendDialog extends React.Component<SendDialogProps, SendDialogState
</div>
);
}
- private _onRecipientChange(recipient?: string) {
+ private _onRecipientChange(recipient?: string): void {
this.setState({
shouldShowIncompleteErrs: false,
recipient,
});
}
- private _onValueChange(isValid: boolean, amount?: BigNumber) {
+ private _onValueChange(isValid: boolean, amount?: BigNumber): void {
this.setState({
isAmountValid: isValid,
value: amount,
});
}
- private _onSendClick() {
+ private _onSendClick(): void {
if (this._hasErrors()) {
this.setState({
shouldShowIncompleteErrs: true,
@@ -112,13 +112,13 @@ export class SendDialog extends React.Component<SendDialogProps, SendDialogState
this.props.onComplete(this.state.recipient, value);
}
}
- private _onCancel() {
+ private _onCancel(): void {
this.setState({
value: undefined,
});
this.props.onCancelled();
}
- private _hasErrors() {
+ private _hasErrors(): boolean {
return _.isUndefined(this.state.recipient) || _.isUndefined(this.state.value) || !this.state.isAmountValid;
}
}
diff --git a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx
index bb7e3ed1a..ac0b27cdc 100644
--- a/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx
+++ b/packages/website/ts/components/dialogs/track_token_confirmation_dialog.tsx
@@ -33,7 +33,7 @@ export class TrackTokenConfirmationDialog extends React.Component<
isAddingTokenToTracked: false,
};
}
- public render() {
+ public render(): React.ReactNode {
const tokens = this.props.tokens;
return (
<Dialog
@@ -66,7 +66,7 @@ export class TrackTokenConfirmationDialog extends React.Component<
</Dialog>
);
}
- private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) {
+ private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean): Promise<void> {
if (!didUserAcceptTracking) {
this.props.onToggleDialog(didUserAcceptTracking);
return;
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 6ac9cf917..ce86df856 100644
--- a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx
+++ b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx
@@ -9,7 +9,7 @@ interface U2fNotSupportedDialogProps {
onToggleDialog: () => void;
}
-export function U2fNotSupportedDialog(props: U2fNotSupportedDialogProps) {
+export const U2fNotSupportedDialog = (props: U2fNotSupportedDialogProps) => {
return (
<Dialog
title="U2F Not Supported"
@@ -43,4 +43,4 @@ export function U2fNotSupportedDialog(props: U2fNotSupportedDialogProps) {
</div>
</Dialog>
);
-}
+};
diff --git a/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx b/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx
index 9e91ff12d..78b270c1e 100644
--- a/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx
+++ b/packages/website/ts/components/dialogs/wrapped_eth_section_notice_dialog.tsx
@@ -8,7 +8,7 @@ interface WrappedEthSectionNoticeDialogProps {
onToggleDialog: () => void;
}
-export function WrappedEthSectionNoticeDialog(props: WrappedEthSectionNoticeDialogProps) {
+export const WrappedEthSectionNoticeDialog = (props: WrappedEthSectionNoticeDialogProps) => {
return (
<Dialog
title="Dedicated Wrapped Ether Section"
@@ -30,4 +30,4 @@ export function WrappedEthSectionNoticeDialog(props: WrappedEthSectionNoticeDial
</div>
</Dialog>
);
-}
+};
diff --git a/packages/website/ts/components/dropdowns/network_drop_down.tsx b/packages/website/ts/components/dropdowns/network_drop_down.tsx
index b569807dd..2fd2785d1 100644
--- a/packages/website/ts/components/dropdowns/network_drop_down.tsx
+++ b/packages/website/ts/components/dropdowns/network_drop_down.tsx
@@ -13,7 +13,7 @@ interface NetworkDropDownProps {
interface NetworkDropDownState {}
export class NetworkDropDown extends React.Component<NetworkDropDownProps, NetworkDropDownState> {
- public render() {
+ public render(): React.ReactNode {
return (
<div className="mx-auto" style={{ width: 120 }}>
<DropDownMenu value={this.props.selectedNetworkId} onChange={this.props.updateSelectedNetwork}>
@@ -22,7 +22,7 @@ export class NetworkDropDown extends React.Component<NetworkDropDownProps, Netwo
</div>
);
}
- private _renderDropDownItems() {
+ private _renderDropDownItems(): React.ReactNode {
const items = _.map(this.props.avialableNetworkIds, networkId => {
const networkName = sharedConstants.NETWORK_NAME_BY_ID[networkId];
const primaryText = (
diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx
index 586d260fb..e8db42a7a 100644
--- a/packages/website/ts/components/eth_weth_conversion_button.tsx
+++ b/packages/website/ts/components/eth_weth_conversion_button.tsx
@@ -46,7 +46,7 @@ export class EthWethConversionButton extends React.Component<
isEthConversionHappening: false,
};
}
- public render() {
+ public render(): React.ReactNode {
const labelStyle = this.state.isEthConversionHappening ? { fontSize: 10 } : {};
let callToActionLabel;
let inProgressLabel;
@@ -81,12 +81,12 @@ export class EthWethConversionButton extends React.Component<
</div>
);
}
- private _toggleConversionDialog() {
+ private _toggleConversionDialog(): void {
this.setState({
isEthConversionDialogVisible: !this.state.isEthConversionDialogVisible,
});
}
- private async _onConversionAmountSelectedAsync(direction: Side, value: BigNumber) {
+ private async _onConversionAmountSelectedAsync(direction: Side, value: BigNumber): Promise<void> {
this.setState({
isEthConversionHappening: true,
});
diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx
index 59afeb50b..f19b05861 100644
--- a/packages/website/ts/components/eth_wrappers.tsx
+++ b/packages/website/ts/components/eth_wrappers.tsx
@@ -65,7 +65,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
},
};
}
- public componentWillReceiveProps(nextProps: EthWrappersProps) {
+ public componentWillReceiveProps(nextProps: EthWrappersProps): void {
if (
nextProps.userAddress !== this.props.userAddress ||
nextProps.networkId !== this.props.networkId ||
@@ -75,15 +75,15 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
this._fetchWETHStateAsync();
}
}
- public componentDidMount() {
+ public componentDidMount(): void {
window.scrollTo(0, 0);
// tslint:disable-next-line:no-floating-promises
this._fetchWETHStateAsync();
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
}
- public render() {
+ public render(): React.ReactNode {
const etherToken = this._getEthToken();
const wethBalance = ZeroEx.toUnitAmount(this.state.ethTokenState.balance, constants.DECIMAL_PLACES_ETH);
const isBidirectional = true;
@@ -222,7 +222,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
</div>
);
}
- private _renderActionColumnTitle(isBidirectional: boolean) {
+ private _renderActionColumnTitle(isBidirectional: boolean): React.ReactNode {
let iconClass = 'zmdi-long-arrow-right';
let leftSymbol = 'WETH';
let rightSymbol = 'ETH';
@@ -241,7 +241,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
</div>
);
}
- private _renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState) {
+ private _renderOutdatedWeths(etherToken: Token, etherTokenState: TokenState): React.ReactNode {
const rows = _.map(
configs.OUTDATED_WRAPPED_ETHERS,
(outdatedWETHByNetworkId: OutdatedWrappedEtherByNetworkId) => {
@@ -313,7 +313,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
);
return rows;
}
- private _renderTokenLink(tokenLabel: React.ReactNode, etherscanUrl: string) {
+ private _renderTokenLink(tokenLabel: React.ReactNode, etherscanUrl: string): React.ReactNode {
return (
<span>
{_.isUndefined(etherscanUrl) ? (
@@ -326,7 +326,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
</span>
);
}
- private _renderToken(name: string, address: string, imgPath: string) {
+ private _renderToken(name: string, address: string, imgPath: string): React.ReactNode {
const tooltipId = `tooltip-${address}`;
return (
<div className="flex">
@@ -340,7 +340,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
</div>
);
}
- private async _onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) {
+ private async _onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string): Promise<void> {
const currentOutdatedWETHState = this.state.outdatedWETHStateByAddress[outdatedWETHAddress];
this.setState({
outdatedWETHStateByAddress: {
@@ -368,7 +368,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
},
});
}
- private async _fetchWETHStateAsync() {
+ private async _fetchWETHStateAsync(): Promise<void> {
const tokens = _.values(this.props.tokenByAddress);
const wethToken = _.find(tokens, token => token.symbol === 'WETH');
const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
@@ -414,12 +414,12 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt
);
return outdatedWETHAddresses;
}
- private _getEthToken() {
+ private _getEthToken(): Token {
const tokens = _.values(this.props.tokenByAddress);
const etherToken = _.find(tokens, { symbol: 'WETH' });
return etherToken;
}
- private async _refetchEthTokenStateAsync() {
+ private async _refetchEthTokenStateAsync(): Promise<void> {
const etherToken = this._getEthToken();
const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx
index ea94e0987..59c32cebc 100644
--- a/packages/website/ts/components/fill_order.tsx
+++ b/packages/website/ts/components/fill_order.tsx
@@ -82,19 +82,19 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
tokensToTrack: [],
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
if (!_.isEmpty(this.state.orderJSON)) {
// tslint:disable-next-line:no-floating-promises
this._validateFillOrderFireAndForgetAsync(this.state.orderJSON);
}
}
- public componentDidMount() {
+ public componentDidMount(): void {
window.scrollTo(0, 0);
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
}
- public render() {
+ public render(): React.ReactNode {
return (
<div className="clearfix lg-px4 md-px4 sm-px2" style={{ minHeight: 600 }}>
<h3>Fill an order</h3>
@@ -159,7 +159,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
</div>
);
}
- private _renderOrderJsonNotices() {
+ private _renderOrderJsonNotices(): React.ReactNode {
return (
<div>
{!_.isUndefined(this.props.initialOrder) &&
@@ -177,7 +177,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
</div>
);
}
- private _renderVisualOrder() {
+ private _renderVisualOrder(): React.ReactNode {
const takerTokenAddress = this.state.parsedOrder.signedOrder.takerTokenAddress;
const takerToken = this.props.tokenByAddress[takerTokenAddress];
const orderTakerAmount = new BigNumber(this.state.parsedOrder.signedOrder.takerTokenAmount);
@@ -306,7 +306,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
</div>
);
}
- private _renderFillSuccessMsg() {
+ private _renderFillSuccessMsg(): React.ReactNode {
return (
<div>
Order successfully filled. See the trade details in your{' '}
@@ -316,10 +316,10 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
</div>
);
}
- private _renderCancelSuccessMsg() {
+ private _renderCancelSuccessMsg(): React.ReactNode {
return <div>Order successfully cancelled.</div>;
}
- private _onFillOrderClick() {
+ private _onFillOrderClick(): void {
if (!this.state.isMakerTokenAddressInRegistry || !this.state.isTakerTokenAddressInRegistry) {
this.setState({
isFillWarningDialogOpen: true,
@@ -329,7 +329,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
this._onFillOrderClickFireAndForgetAsync();
}
}
- private _onFillWarningClosed(didUserCancel: boolean) {
+ private _onFillWarningClosed(didUserCancel: boolean): void {
this.setState({
isFillWarningDialogOpen: false,
});
@@ -338,10 +338,10 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
this._onFillOrderClickFireAndForgetAsync();
}
}
- private _onFillAmountChange(isValid: boolean, amount?: BigNumber) {
+ private _onFillAmountChange(isValid: boolean, amount?: BigNumber): void {
this.props.dispatcher.updateOrderFillAmount(amount);
}
- private _onFillOrderJSONChanged(event: any) {
+ private _onFillOrderJSONChanged(event: any): void {
const orderJSON = event.target.value;
this.setState({
didOrderValidationRun: _.isEmpty(orderJSON) && _.isEmpty(this.state.orderJSONErrMsg),
@@ -350,7 +350,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
// tslint:disable-next-line:no-floating-promises
this._validateFillOrderFireAndForgetAsync(orderJSON);
}
- private async _checkForUntrackedTokensAndAskToAdd() {
+ private async _checkForUntrackedTokensAndAskToAddAsync(): Promise<void> {
if (!_.isEmpty(this.state.orderJSONErrMsg)) {
return;
}
@@ -396,7 +396,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
});
}
}
- private async _validateFillOrderFireAndForgetAsync(orderJSON: string) {
+ private async _validateFillOrderFireAndForgetAsync(orderJSON: string): Promise<void> {
let orderJSONErrMsg = '';
let parsedOrder: Order;
let orderHash: string;
@@ -491,7 +491,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
unavailableTakerAmount,
});
- await this._checkForUntrackedTokensAndAskToAdd();
+ await this._checkForUntrackedTokensAndAskToAddAsync();
}
private async _onFillOrderClickFireAndForgetAsync(): Promise<void> {
if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) {
@@ -650,7 +650,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> {
const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000;
return roundedUnitAmount;
}
- private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) {
+ private _onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean): void {
if (!didConfirmTokenTracking) {
this.setState({
orderJSON: '',
diff --git a/packages/website/ts/components/fill_order_json.tsx b/packages/website/ts/components/fill_order_json.tsx
index 7d5351ec4..97297d5a1 100644
--- a/packages/website/ts/components/fill_order_json.tsx
+++ b/packages/website/ts/components/fill_order_json.tsx
@@ -19,7 +19,7 @@ interface FillOrderJSONProps {
interface FillOrderJSONState {}
export class FillOrderJSON extends React.Component<FillOrderJSONProps, FillOrderJSONState> {
- public render() {
+ public render(): React.ReactNode {
const tokenAddresses = _.keys(this.props.tokenByAddress);
const exchangeContract = this.props.blockchain.getExchangeContractAddressIfExists();
const hintSideToAssetToken = {
diff --git a/packages/website/ts/components/fill_warning_dialog.tsx b/packages/website/ts/components/fill_warning_dialog.tsx
index d3215a6c1..83095b1d3 100644
--- a/packages/website/ts/components/fill_warning_dialog.tsx
+++ b/packages/website/ts/components/fill_warning_dialog.tsx
@@ -8,7 +8,7 @@ interface FillWarningDialogProps {
onToggleDialog: (didUserCancel: boolean) => void;
}
-export function FillWarningDialog(props: FillWarningDialogProps) {
+export const FillWarningDialog = (props: FillWarningDialogProps) => {
const didCancel = true;
return (
<Dialog
@@ -42,4 +42,4 @@ export function FillWarningDialog(props: FillWarningDialogProps) {
</div>
</Dialog>
);
-}
+};
diff --git a/packages/website/ts/components/flash_messages/token_send_completed.tsx b/packages/website/ts/components/flash_messages/token_send_completed.tsx
index a3b056758..bb5adfa4e 100644
--- a/packages/website/ts/components/flash_messages/token_send_completed.tsx
+++ b/packages/website/ts/components/flash_messages/token_send_completed.tsx
@@ -16,7 +16,7 @@ interface TokenSendCompletedProps {
interface TokenSendCompletedState {}
export class TokenSendCompleted extends React.Component<TokenSendCompletedProps, TokenSendCompletedState> {
- public render() {
+ public render(): React.ReactNode {
const etherScanLink = !_.isUndefined(this.props.etherScanLinkIfExists) && (
<a style={{ color: colors.white }} href={`${this.props.etherScanLinkIfExists}`} target="_blank">
Verify on Etherscan
diff --git a/packages/website/ts/components/flash_messages/transaction_submitted.tsx b/packages/website/ts/components/flash_messages/transaction_submitted.tsx
index 188f1f9a6..2a6d2a64b 100644
--- a/packages/website/ts/components/flash_messages/transaction_submitted.tsx
+++ b/packages/website/ts/components/flash_messages/transaction_submitted.tsx
@@ -9,7 +9,7 @@ interface TransactionSubmittedProps {
interface TransactionSubmittedState {}
export class TransactionSubmitted extends React.Component<TransactionSubmittedProps, TransactionSubmittedState> {
- public render() {
+ public render(): React.ReactNode {
if (_.isUndefined(this.props.etherScanLinkIfExists)) {
return <div>Transaction submitted to the network</div>;
} else {
diff --git a/packages/website/ts/components/footer.tsx b/packages/website/ts/components/footer.tsx
index 487b039b2..c44e41084 100644
--- a/packages/website/ts/components/footer.tsx
+++ b/packages/website/ts/components/footer.tsx
@@ -50,7 +50,7 @@ export class Footer extends React.Component<FooterProps, FooterState> {
selectedLanguage: props.translate.getLanguage(),
};
}
- public render() {
+ public render(): React.ReactNode {
const menuItemsBySection: MenuItemsBySection = {
[Key.Documentation]: [
{
@@ -180,14 +180,14 @@ export class Footer extends React.Component<FooterProps, FooterState> {
</div>
);
}
- private _renderIcon(fileName: string) {
+ private _renderIcon(fileName: string): React.ReactNode {
return (
<div style={{ height: ICON_DIMENSION, width: ICON_DIMENSION }}>
<img src={`/images/social/${fileName}`} style={{ width: ICON_DIMENSION }} />
</div>
);
}
- private _renderMenuItem(item: FooterMenuItem) {
+ private _renderMenuItem(item: FooterMenuItem): React.ReactNode {
const titleToIcon: { [title: string]: string } = {
[this.props.translate.get(Key.RocketChat, Deco.Cap)]: 'rocketchat.png',
[this.props.translate.get(Key.Blog, Deco.Cap)]: 'medium.png',
@@ -222,7 +222,7 @@ export class Footer extends React.Component<FooterProps, FooterState> {
</div>
);
}
- private _renderHeader(key: Key) {
+ private _renderHeader(key: Key): React.ReactNode {
const headerStyle = {
color: colors.grey400,
letterSpacing: 2,
@@ -235,7 +235,7 @@ export class Footer extends React.Component<FooterProps, FooterState> {
</div>
);
}
- private _updateLanguage(e: any, index: number, value: Language) {
+ private _updateLanguage(e: any, index: number, value: Language): void {
this.setState({
selectedLanguage: value,
});
diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx
index 69fb32a21..d7cc554c4 100644
--- a/packages/website/ts/components/generate_order/asset_picker.tsx
+++ b/packages/website/ts/components/generate_order/asset_picker.tsx
@@ -79,7 +79,7 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt
},
};
}
- public render() {
+ public render(): React.ReactNode {
const dialogConfigs: DialogConfigs = this._dialogConfigsByAssetView[this.state.assetView];
return (
<Dialog
@@ -102,7 +102,7 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt
</Dialog>
);
}
- private _renderConfirmTrackToken() {
+ private _renderConfirmTrackToken(): React.ReactNode {
const token = this.props.tokenByAddress[this.state.chosenTrackTokenAddress];
return (
<TrackTokenConfirmation
@@ -113,7 +113,7 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt
/>
);
}
- private _renderAssetPicker() {
+ private _renderAssetPicker(): React.ReactNode {
return (
<div
className="clearfix flex flex-wrap"
@@ -128,7 +128,7 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt
</div>
);
}
- private _renderGridTiles() {
+ private _renderGridTiles(): React.ReactNode {
let isHovered;
let tileStyles;
const gridTiles = _.map(this.props.tokenByAddress, (token: Token, address: string) => {
@@ -195,19 +195,19 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt
}
return gridTiles;
}
- private _onToggleHover(address: string, isHovered: boolean) {
+ private _onToggleHover(address: string, isHovered: boolean): void {
const hoveredAddress = isHovered ? address : undefined;
this.setState({
hoveredAddress,
});
}
- private _onCloseDialog() {
+ private _onCloseDialog(): void {
this.setState({
assetView: AssetViews.ASSET_PICKER,
});
this.props.onTokenChosen(this.props.currentTokenAddress);
}
- private _onChooseToken(tokenAddress: string) {
+ private _onChooseToken(tokenAddress: string): void {
const token = this.props.tokenByAddress[tokenAddress];
if (token.isTracked) {
this.props.onTokenChosen(tokenAddress);
@@ -218,12 +218,12 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt
});
}
}
- private _onCustomAssetChosen() {
+ private _onCustomAssetChosen(): void {
this.setState({
assetView: AssetViews.NEW_TOKEN_FORM,
});
}
- private _onNewTokenSubmitted(newToken: Token) {
+ private _onNewTokenSubmitted(newToken: Token): void {
trackedTokenStorage.addTrackedTokenToUser(this.props.userAddress, this.props.networkId, newToken);
this.props.dispatcher.addTokenToTokenByAddress(newToken);
this.setState({
@@ -231,7 +231,7 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt
});
this.props.onTokenChosen(newToken.address);
}
- private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean) {
+ private async _onTrackConfirmationRespondedAsync(didUserAcceptTracking: boolean): Promise<void> {
if (!didUserAcceptTracking) {
this.setState({
isAddingTokenToTracked: false,
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 eb76cb529..d46c29058 100644
--- a/packages/website/ts/components/generate_order/generate_order_form.tsx
+++ b/packages/website/ts/components/generate_order/generate_order_form.tsx
@@ -63,10 +63,10 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
signingState: SigningState.UNSIGNED,
};
}
- public componentDidMount() {
+ public componentDidMount(): void {
window.scrollTo(0, 0);
}
- public render() {
+ public render(): React.ReactNode {
const dispatcher = this.props.dispatcher;
const depositTokenAddress = this.props.sideToAssetToken[Side.Deposit].address;
const depositToken = this.props.tokenByAddress[depositTokenAddress];
@@ -214,13 +214,13 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G
</div>
);
}
- private _onTokenAmountChange(token: Token, side: Side, isValid: boolean, amount?: BigNumber) {
+ private _onTokenAmountChange(token: Token, side: Side, isValid: boolean, amount?: BigNumber): void {
this.props.dispatcher.updateChosenAssetToken(side, {
address: token.address,
amount,
});
}
- private _onCloseOrderJSONDialog() {
+ private _onCloseOrderJSONDialog(): void {
// Upon closing the order JSON dialog, we update the orderSalt stored in the Redux store
// with a new value so that if a user signs the identical order again, the newly signed
// orderHash will not collide with the previously generated orderHash.
diff --git a/packages/website/ts/components/generate_order/new_token_form.tsx b/packages/website/ts/components/generate_order/new_token_form.tsx
index e7f3b93c6..10f71b430 100644
--- a/packages/website/ts/components/generate_order/new_token_form.tsx
+++ b/packages/website/ts/components/generate_order/new_token_form.tsx
@@ -42,7 +42,7 @@ export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFor
decimalsErrText: '',
};
}
- public render() {
+ public render(): React.ReactNode {
return (
<div className="mx-auto pb2" style={{ width: 256 }}>
<div>
@@ -96,7 +96,7 @@ export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFor
</div>
);
}
- private async _onAddNewTokenClickAsync() {
+ private async _onAddNewTokenClickAsync(): Promise<void> {
// Trigger validation of name and symbol
this._onTokenNameChanged(undefined, this.state.name);
this._onTokenSymbolChanged(undefined, this.state.symbol);
@@ -152,7 +152,7 @@ export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFor
};
this.props.onNewTokenSubmitted(newToken);
}
- private _onTokenNameChanged(e: any, name: string) {
+ private _onTokenNameChanged(e: any, name: string): void {
let nameErrText = '';
const maxLength = 30;
const tokens = _.values(this.props.tokenByAddress);
@@ -173,7 +173,7 @@ export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFor
nameErrText,
});
}
- private _onTokenSymbolChanged(e: any, symbol: string) {
+ private _onTokenSymbolChanged(e: any, symbol: string): void {
let symbolErrText = '';
const maxLength = 5;
const tokens = _.values(this.props.tokenByAddress);
@@ -193,7 +193,7 @@ export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFor
symbolErrText,
});
}
- private _onTokenDecimalsChanged(e: any, decimals: string) {
+ private _onTokenDecimalsChanged(e: any, decimals: string): void {
let decimalsErrText = '';
const maxLength = 2;
if (decimals === '') {
@@ -209,20 +209,20 @@ export class NewTokenForm extends React.Component<NewTokenFormProps, NewTokenFor
decimalsErrText,
});
}
- private _onTokenAddressChanged(address?: string) {
+ private _onTokenAddressChanged(address?: string): void {
if (!_.isUndefined(address)) {
this.setState({
address,
});
}
}
- private _isValidName(input: string) {
+ private _isValidName(input: string): boolean {
return /^[a-z0-9 ]+$/i.test(input);
}
- private _isInteger(input: string) {
+ private _isInteger(input: string): boolean {
return /^[0-9]+$/i.test(input);
}
- private _isAlphanumeric(input: string) {
+ private _isAlphanumeric(input: string): boolean {
return /^[a-zA-Z0-9]+$/i.test(input);
}
}
diff --git a/packages/website/ts/components/inputs/address_input.tsx b/packages/website/ts/components/inputs/address_input.tsx
index 7ca4af968..39ec72f8a 100644
--- a/packages/website/ts/components/inputs/address_input.tsx
+++ b/packages/website/ts/components/inputs/address_input.tsx
@@ -29,14 +29,14 @@ export class AddressInput extends React.Component<AddressInputProps, AddressInpu
errMsg: '',
};
}
- public componentWillReceiveProps(nextProps: AddressInputProps) {
+ public componentWillReceiveProps(nextProps: AddressInputProps): void {
if (nextProps.shouldShowIncompleteErrs && this.props.isRequired && this.state.address === '') {
this.setState({
errMsg: 'Address is required',
});
}
}
- public render() {
+ public render(): React.ReactNode {
const label = this.props.isRequired ? <RequiredLabel label={this.props.label} /> : this.props.label;
const labelDisplay = this.props.shouldHideLabel ? 'hidden' : 'block';
const hintText = this.props.hintText ? this.props.hintText : '';
@@ -57,7 +57,7 @@ export class AddressInput extends React.Component<AddressInputProps, AddressInpu
</div>
);
}
- private _onOrderTakerAddressUpdated(e: any) {
+ private _onOrderTakerAddressUpdated(e: any): void {
const address = e.target.value.toLowerCase();
const isValidAddress = addressUtils.isAddress(address) || address === '';
const errMsg = isValidAddress ? '' : 'Invalid ethereum address';
diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx
index cfe75b751..48c7f9f57 100644
--- a/packages/website/ts/components/inputs/allowance_toggle.tsx
+++ b/packages/website/ts/components/inputs/allowance_toggle.tsx
@@ -63,7 +63,7 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow
prevAllowance: props.tokenState.allowance,
};
}
- public componentWillReceiveProps(nextProps: AllowanceToggleProps) {
+ public componentWillReceiveProps(nextProps: AllowanceToggleProps): void {
if (!nextProps.tokenState.allowance.eq(this.state.prevAllowance)) {
this.setState({
isSpinnerVisible: false,
@@ -71,7 +71,7 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow
});
}
}
- public render() {
+ public render(): React.ReactNode {
return (
<div className="flex">
<div>
@@ -128,7 +128,7 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow
await errorReporter.reportAsync(err);
}
}
- private _isAllowanceSet() {
+ private _isAllowanceSet(): boolean {
return !this.props.tokenState.allowance.eq(0);
}
}
diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx
index e9b8dd369..f91a99355 100644
--- a/packages/website/ts/components/inputs/balance_bounded_input.tsx
+++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx
@@ -5,7 +5,7 @@ import TextField from 'material-ui/TextField';
import * as React from 'react';
import { Link } from 'react-router-dom';
import { RequiredLabel } from 'ts/components/ui/required_label';
-import { InputErrMsg, ValidatedBigNumberCallback, WebsitePaths } from 'ts/types';
+import { ValidatedBigNumberCallback, WebsitePaths } from 'ts/types';
import { utils } from 'ts/utils/utils';
interface BalanceBoundedInputProps {
@@ -14,18 +14,21 @@ interface BalanceBoundedInputProps {
amount?: BigNumber;
hintText?: string;
onChange: ValidatedBigNumberCallback;
+ onErrorMsgChange?: (errorMsg: React.ReactNode) => void;
shouldShowIncompleteErrs?: boolean;
shouldCheckBalance: boolean;
- validate?: (amount: BigNumber) => InputErrMsg;
+ validate?: (amount: BigNumber) => React.ReactNode;
onVisitBalancesPageClick?: () => void;
shouldHideVisitBalancesLink?: boolean;
isDisabled?: boolean;
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
+ inputStyle?: React.CSSProperties;
+ inputHintStyle?: React.CSSProperties;
}
interface BalanceBoundedInputState {
- errMsg: InputErrMsg;
+ errMsg: React.ReactNode;
amountString: string;
}
@@ -36,6 +39,7 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
isDisabled: false,
shouldShowErrs: true,
hintText: 'amount',
+ onErrorMsgChange: _.noop,
shouldShowUnderline: true,
};
constructor(props: BalanceBoundedInputProps) {
@@ -46,7 +50,7 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
amountString,
};
}
- public componentWillReceiveProps(nextProps: BalanceBoundedInputProps) {
+ public componentWillReceiveProps(nextProps: BalanceBoundedInputProps): void {
if (nextProps === this.props) {
return;
}
@@ -63,20 +67,14 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
}
if (shouldResetState) {
const amountString = nextProps.amount.toString();
- this.setState({
- errMsg: this._validate(amountString, nextProps.balance),
- amountString,
- });
+ this._setAmountState(amountString, nextProps.balance);
}
} else if (isCurrentAmountNumeric) {
const amountString = '';
- this.setState({
- errMsg: this._validate(amountString, nextProps.balance),
- amountString,
- });
+ this._setAmountState(amountString, nextProps.balance);
}
}
- public render() {
+ public render(): React.ReactNode {
let errorText;
if (this.props.shouldShowErrs) {
errorText =
@@ -99,21 +97,21 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
hintText={<span style={{ textTransform: 'capitalize' }}>{this.props.hintText}</span>}
onChange={this._onValueChange.bind(this)}
underlineStyle={{ width: 'calc(100% + 50px)' }}
+ inputStyle={this.props.inputStyle}
+ hintStyle={this.props.inputHintStyle}
underlineShow={this.props.shouldShowUnderline}
disabled={this.props.isDisabled}
/>
);
}
- private _onValueChange(e: any, amountString: string) {
- const errMsg = this._validate(amountString, this.props.balance);
- this.setState(
- {
- amountString,
- errMsg,
- },
+ private _onValueChange(e: any, amountString: string): void {
+ this._setAmountState(
+ amountString,
+ this.props.balance,
() => {
- const isValid = _.isUndefined(errMsg);
- if (utils.isNumeric(amountString) && !_.includes(amountString, '-')) {
+ const isValid = _.isUndefined(this._validate(amountString, this.props.balance));
+ const isPositiveNumber = utils.isNumeric(amountString) && !_.includes(amountString, '-');
+ if (isPositiveNumber) {
this.props.onChange(isValid, new BigNumber(amountString));
} else {
this.props.onChange(isValid);
@@ -121,7 +119,7 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
},
);
}
- private _validate(amountString: string, balance: BigNumber): InputErrMsg {
+ private _validate(amountString: string, balance: BigNumber): React.ReactNode {
if (!utils.isNumeric(amountString)) {
return amountString !== '' ? 'Must be a number' : '';
}
@@ -135,7 +133,7 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount);
return errMsg;
}
- private _renderIncreaseBalanceLink() {
+ private _renderIncreaseBalanceLink(): React.ReactNode {
if (this.props.shouldHideVisitBalancesLink) {
return null;
}
@@ -161,4 +159,13 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp
);
}
}
+
+ private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop): void {
+ const errorMsg = this._validate(amount, balance);
+ this.props.onErrorMsgChange(errorMsg);
+ this.setState({
+ amountString: amount,
+ errMsg: errorMsg,
+ }, callback);
+ }
}
diff --git a/packages/website/ts/components/inputs/eth_amount_input.tsx b/packages/website/ts/components/inputs/eth_amount_input.tsx
index f3a879065..fa684d85c 100644
--- a/packages/website/ts/components/inputs/eth_amount_input.tsx
+++ b/packages/website/ts/components/inputs/eth_amount_input.tsx
@@ -12,6 +12,7 @@ interface EthAmountInputProps {
amount?: BigNumber;
hintText?: string;
onChange: ValidatedBigNumberCallback;
+ onErrorMsgChange?: (errorMsg: React.ReactNode) => void;
shouldShowIncompleteErrs: boolean;
onVisitBalancesPageClick?: () => void;
shouldCheckBalance: boolean;
@@ -19,6 +20,8 @@ interface EthAmountInputProps {
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
style?: React.CSSProperties;
+ labelStyle?: React.CSSProperties;
+ inputHintStyle?: React.CSSProperties;
}
interface EthAmountInputState {}
@@ -29,7 +32,7 @@ export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmou
shouldShowUnderline: true,
style: { height: 63 },
};
- public render() {
+ public render(): React.ReactNode {
const amount = this.props.amount
? ZeroEx.toUnitAmount(this.props.amount, constants.DECIMAL_PLACES_ETH)
: undefined;
@@ -40,6 +43,7 @@ export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmou
balance={this.props.balance}
amount={amount}
onChange={this._onChange.bind(this)}
+ onErrorMsgChange={this.props.onErrorMsgChange}
shouldCheckBalance={this.props.shouldCheckBalance}
shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
onVisitBalancesPageClick={this.props.onVisitBalancesPageClick}
@@ -47,15 +51,20 @@ export class EthAmountInput extends React.Component<EthAmountInputProps, EthAmou
hintText={this.props.hintText}
shouldShowErrs={this.props.shouldShowErrs}
shouldShowUnderline={this.props.shouldShowUnderline}
+ inputStyle={this.props.style}
+ inputHintStyle={this.props.inputHintStyle}
/>
- <div style={{ paddingTop: _.isUndefined(this.props.label) ? 15 : 40 }}>ETH</div>
+ <div style={this._getLabelStyle()}>ETH</div>
</div>
);
}
- private _onChange(isValid: boolean, amount?: BigNumber) {
+ private _onChange(isValid: boolean, amount?: BigNumber): void {
const baseUnitAmountIfExists = _.isUndefined(amount)
? undefined
: ZeroEx.toBaseUnitAmount(amount, constants.DECIMAL_PLACES_ETH);
this.props.onChange(isValid, baseUnitAmountIfExists);
}
+ private _getLabelStyle(): React.CSSProperties {
+ return this.props.labelStyle || { paddingTop: _.isUndefined(this.props.label) ? 15 : 40 };
+ }
}
diff --git a/packages/website/ts/components/inputs/expiration_input.tsx b/packages/website/ts/components/inputs/expiration_input.tsx
index e473648d2..5c68080fe 100644
--- a/packages/website/ts/components/inputs/expiration_input.tsx
+++ b/packages/website/ts/components/inputs/expiration_input.tsx
@@ -30,7 +30,7 @@ export class ExpirationInput extends React.Component<ExpirationInputProps, Expir
timeMoment: didUserSetExpiry ? expirationMoment : undefined,
};
}
- public render() {
+ public render(): React.ReactNode {
const date = this.state.dateMoment ? this.state.dateMoment.toDate() : undefined;
const time = this.state.timeMoment ? this.state.timeMoment.toDate() : undefined;
return (
@@ -72,7 +72,7 @@ export class ExpirationInput extends React.Component<ExpirationInputProps, Expir
.startOf('day')
.isBefore(this._earliestPickableMoment);
}
- private _clearDates() {
+ private _clearDates(): void {
this.setState({
dateMoment: undefined,
timeMoment: undefined,
@@ -80,7 +80,7 @@ export class ExpirationInput extends React.Component<ExpirationInputProps, Expir
const defaultDateTime = utils.initialOrderExpiryUnixTimestampSec();
this.props.updateOrderExpiry(defaultDateTime);
}
- private _onDateChanged(e: any, date: Date) {
+ private _onDateChanged(e: any, date: Date): void {
const dateMoment = moment(date);
this.setState({
dateMoment,
@@ -88,7 +88,7 @@ export class ExpirationInput extends React.Component<ExpirationInputProps, Expir
const timestamp = utils.convertToUnixTimestampSeconds(dateMoment, this.state.timeMoment);
this.props.updateOrderExpiry(timestamp);
}
- private _onTimeChanged(e: any, time: Date) {
+ private _onTimeChanged(e: any, time: Date): void {
const timeMoment = moment(time);
this.setState({
timeMoment,
diff --git a/packages/website/ts/components/inputs/hash_input.tsx b/packages/website/ts/components/inputs/hash_input.tsx
index 28305637d..37d4af138 100644
--- a/packages/website/ts/components/inputs/hash_input.tsx
+++ b/packages/website/ts/components/inputs/hash_input.tsx
@@ -27,7 +27,7 @@ interface HashInputProps {
interface HashInputState {}
export class HashInput extends React.Component<HashInputProps, HashInputState> {
- public render() {
+ public render(): React.ReactNode {
const msgHashHex = this.props.blockchainIsLoaded ? this._generateMessageHashHex() : '';
return (
<div>
@@ -40,7 +40,7 @@ export class HashInput extends React.Component<HashInputProps, HashInputState> {
</div>
);
}
- private _generateMessageHashHex() {
+ private _generateMessageHashHex(): string {
const exchangeContractAddress = this.props.blockchain.getExchangeContractAddressIfExists();
const hashData = this.props.hashData;
const order: Order = {
diff --git a/packages/website/ts/components/inputs/identicon_address_input.tsx b/packages/website/ts/components/inputs/identicon_address_input.tsx
index 4cf9af64d..a4dc01ba8 100644
--- a/packages/website/ts/components/inputs/identicon_address_input.tsx
+++ b/packages/website/ts/components/inputs/identicon_address_input.tsx
@@ -23,7 +23,7 @@ export class IdenticonAddressInput extends React.Component<IdenticonAddressInput
address: props.initialAddress,
};
}
- public render() {
+ public render(): React.ReactNode {
const label = this.props.isRequired ? <RequiredLabel label={this.props.label} /> : this.props.label;
return (
<div className="relative" style={{ width: '100%' }}>
diff --git a/packages/website/ts/components/inputs/token_amount_input.tsx b/packages/website/ts/components/inputs/token_amount_input.tsx
index 9e638b67b..f040928f1 100644
--- a/packages/website/ts/components/inputs/token_amount_input.tsx
+++ b/packages/website/ts/components/inputs/token_amount_input.tsx
@@ -6,7 +6,7 @@ import * as React from 'react';
import { Link } from 'react-router-dom';
import { Blockchain } from 'ts/blockchain';
import { BalanceBoundedInput } from 'ts/components/inputs/balance_bounded_input';
-import { InputErrMsg, Token, ValidatedBigNumberCallback, WebsitePaths } from 'ts/types';
+import { Token, ValidatedBigNumberCallback, WebsitePaths } from 'ts/types';
interface TokenAmountInputProps {
userAddress: string;
@@ -20,11 +20,14 @@ interface TokenAmountInputProps {
shouldCheckBalance: boolean;
shouldCheckAllowance: boolean;
onChange: ValidatedBigNumberCallback;
+ onErrorMsgChange?: (errorMsg: React.ReactNode) => void;
onVisitBalancesPageClick?: () => void;
lastForceTokenStateRefetch: number;
shouldShowErrs?: boolean;
shouldShowUnderline?: boolean;
style?: React.CSSProperties;
+ labelStyle?: React.CSSProperties;
+ inputHintStyle?: React.CSSProperties;
}
interface TokenAmountInputState {
@@ -52,14 +55,14 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
isBalanceAndAllowanceLoaded: false,
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
// tslint:disable-next-line:no-floating-promises
this._fetchBalanceAndAllowanceAsync(this.props.token.address, this.props.userAddress);
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
}
- public componentWillReceiveProps(nextProps: TokenAmountInputProps) {
+ public componentWillReceiveProps(nextProps: TokenAmountInputProps): void {
if (
nextProps.userAddress !== this.props.userAddress ||
nextProps.networkId !== this.props.networkId ||
@@ -70,21 +73,18 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
this._fetchBalanceAndAllowanceAsync(nextProps.token.address, nextProps.userAddress);
}
}
- public render() {
+ public render(): React.ReactNode {
const amount = this.props.amount
? ZeroEx.toUnitAmount(this.props.amount, this.props.token.decimals)
: undefined;
- const hasLabel = !_.isUndefined(this.props.label);
- const style = !_.isUndefined(this.props.style)
- ? this.props.style
- : { height: hasLabel ? HEIGHT_WITH_LABEL : HEIGHT_WITHOUT_LABEL };
return (
- <div className="flex overflow-hidden" style={style}>
+ <div className="flex overflow-hidden" style={this._getStyle()}>
<BalanceBoundedInput
label={this.props.label}
amount={amount}
balance={ZeroEx.toUnitAmount(this.state.balance, this.props.token.decimals)}
onChange={this._onChange.bind(this)}
+ onErrorMsgChange={this.props.onErrorMsgChange}
validate={this._validate.bind(this)}
shouldCheckBalance={this.props.shouldCheckBalance}
shouldShowIncompleteErrs={this.props.shouldShowIncompleteErrs}
@@ -93,19 +93,21 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
hintText={this.props.hintText}
shouldShowErrs={this.props.shouldShowErrs}
shouldShowUnderline={this.props.shouldShowUnderline}
+ inputStyle={this.props.style}
+ inputHintStyle={this.props.inputHintStyle}
/>
- <div style={{ paddingTop: hasLabel ? 39 : 14 }}>{this.props.token.symbol}</div>
+ <div style={this._getLabelStyle()}>{this.props.token.symbol}</div>
</div>
);
}
- private _onChange(isValid: boolean, amount?: BigNumber) {
+ private _onChange(isValid: boolean, amount?: BigNumber): void {
let baseUnitAmount;
if (!_.isUndefined(amount)) {
baseUnitAmount = ZeroEx.toBaseUnitAmount(amount, this.props.token.decimals);
}
this.props.onChange(isValid, baseUnitAmount);
}
- private _validate(amount: BigNumber): InputErrMsg {
+ private _validate(amount: BigNumber): React.ReactNode {
if (this.props.shouldCheckAllowance && amount.gt(this.state.allowance)) {
return (
<span>
@@ -122,7 +124,7 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
return undefined;
}
}
- private async _fetchBalanceAndAllowanceAsync(tokenAddress: string, userAddress: string) {
+ private async _fetchBalanceAndAllowanceAsync(tokenAddress: string, userAddress: string): Promise<void> {
this.setState({
isBalanceAndAllowanceLoaded: false,
});
@@ -139,4 +141,14 @@ export class TokenAmountInput extends React.Component<TokenAmountInputProps, Tok
});
}
}
+ private _getStyle(): React.CSSProperties {
+ const hasLabel = !_.isUndefined(this.props.label);
+ return !_.isUndefined(this.props.style)
+ ? this.props.style
+ : { height: hasLabel ? HEIGHT_WITH_LABEL : HEIGHT_WITHOUT_LABEL };
+ }
+ private _getLabelStyle(): React.CSSProperties {
+ const hasLabel = !_.isUndefined(this.props.label);
+ return this.props.labelStyle || { paddingTop: hasLabel ? 39 : 14 };
+ }
}
diff --git a/packages/website/ts/components/inputs/token_input.tsx b/packages/website/ts/components/inputs/token_input.tsx
index 545e9a095..c2c4dd63b 100644
--- a/packages/website/ts/components/inputs/token_input.tsx
+++ b/packages/website/ts/components/inputs/token_input.tsx
@@ -38,7 +38,7 @@ export class TokenInput extends React.Component<TokenInputProps, TokenInputState
isPickerOpen: false,
};
}
- public render() {
+ public render(): React.ReactNode {
const token = this.props.tokenByAddress[this.props.assetToken.address];
const iconStyles = {
cursor: 'pointer',
@@ -76,7 +76,7 @@ export class TokenInput extends React.Component<TokenInputProps, TokenInputState
</div>
);
}
- private _onTokenChosen(tokenAddress: string) {
+ private _onTokenChosen(tokenAddress: string): void {
const assetToken: AssetToken = {
address: tokenAddress,
amount: this.props.assetToken.amount,
@@ -86,12 +86,12 @@ export class TokenInput extends React.Component<TokenInputProps, TokenInputState
isPickerOpen: false,
});
}
- private _onToggleHover(isHoveringIcon: boolean) {
+ private _onToggleHover(isHoveringIcon: boolean): void {
this.setState({
isHoveringIcon,
});
}
- private _onAssetClicked() {
+ private _onAssetClicked(): void {
if (this.props.blockchainErr !== BlockchainErrs.NoError) {
this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true);
return;
diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx
index b79f5e288..a5ea95629 100644
--- a/packages/website/ts/components/portal.tsx
+++ b/packages/website/ts/components/legacy_portal/legacy_portal.tsx
@@ -14,7 +14,7 @@ import { WrappedEthSectionNoticeDialog } from 'ts/components/dialogs/wrapped_eth
import { EthWrappers } from 'ts/components/eth_wrappers';
import { FillOrder } from 'ts/components/fill_order';
import { Footer } from 'ts/components/footer';
-import { PortalMenu } from 'ts/components/portal_menu';
+import { LegacyPortalMenu } from 'ts/components/legacy_portal/legacy_portal_menu';
import { RelayerIndex } from 'ts/components/relayer_index/relayer_index';
import { TokenBalances } from 'ts/components/token_balances';
import { TopBar } from 'ts/components/top_bar/top_bar';
@@ -43,9 +43,7 @@ import { utils } from 'ts/utils/utils';
const THROTTLE_TIMEOUT = 100;
-export interface PortalPassedProps {}
-
-export interface PortalAllProps {
+export interface LegacyPortalProps {
blockchainErr: BlockchainErrs;
blockchainIsLoaded: boolean;
dispatcher: Dispatcher;
@@ -67,7 +65,7 @@ export interface PortalAllProps {
translate: Translate;
}
-interface PortalAllState {
+interface LegacyPortalState {
prevNetworkId: number;
prevNodeVersion: string;
prevUserAddress: string;
@@ -77,22 +75,22 @@ interface PortalAllState {
isLedgerDialogOpen: boolean;
}
-export class Portal extends React.Component<PortalAllProps, PortalAllState> {
+export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPortalState> {
private _blockchain: Blockchain;
private _sharedOrderIfExists: Order;
private _throttledScreenWidthUpdate: () => void;
- public static hasAlreadyDismissedWethNotice() {
+ 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: PortalAllProps) {
+ constructor(props: LegacyPortalProps) {
super(props);
this._sharedOrderIfExists = this._getSharedOrderIfExists();
this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`);
- const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice();
+ const hasAlreadyDismissedWethNotice = LegacyPortal.hasAlreadyDismissedWethNotice();
const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER);
const hasAcceptedDisclaimer =
@@ -107,14 +105,14 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
isLedgerDialogOpen: false,
};
}
- public componentDidMount() {
+ public componentDidMount(): void {
window.addEventListener('resize', this._throttledScreenWidthUpdate);
window.scrollTo(0, 0);
}
- public componentWillMount() {
+ public componentWillMount(): void {
this._blockchain = new Blockchain(this.props.dispatcher);
}
- public componentWillUnmount() {
+ 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
@@ -123,7 +121,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
// become disconnected from their backing Ethereum node, changes user accounts, etc...)
this.props.dispatcher.resetState();
}
- public componentWillReceiveProps(nextProps: PortalAllProps) {
+ public componentWillReceiveProps(nextProps: LegacyPortalProps): void {
if (nextProps.networkId !== this.state.prevNetworkId) {
// tslint:disable-next-line:no-floating-promises
this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId);
@@ -145,18 +143,17 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
}
if (nextProps.location.pathname !== this.state.prevPathname) {
const isViewingBalances = _.includes(nextProps.location.pathname, `${WebsitePaths.Portal}/balances`);
- const hasAlreadyDismissedWethNotice = Portal.hasAlreadyDismissedWethNotice();
+ const hasAlreadyDismissedWethNotice = LegacyPortal.hasAlreadyDismissedWethNotice();
this.setState({
prevPathname: nextProps.location.pathname,
isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances,
});
}
}
- public render() {
+ public render(): React.ReactNode {
const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind(
this.props.dispatcher,
);
- const isDevelopment = configs.ENVIRONMENT === Environments.DEVELOPMENT;
const portalStyle: React.CSSProperties = {
minHeight: '100vh',
display: 'flex',
@@ -200,24 +197,12 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
) : (
<div className="mx-auto flex">
<div className="col col-2 pr2 pt1 sm-hide xs-hide" style={portalMenuContainerStyle}>
- <PortalMenu menuItemStyle={{ color: colors.white }} />
+ <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>
- {isDevelopment && (
- <Route
- path={`${WebsitePaths.Portal}/wallet`}
- render={this._renderWallet.bind(this)}
- />
- )}
- {isDevelopment && (
- <Route
- path={`${WebsitePaths.Portal}/relayers`}
- render={this._renderRelayers.bind(this)}
- />
- )}
<Route
path={`${WebsitePaths.Portal}/weth`}
render={this._renderEthWrapper.bind(this)}
@@ -232,7 +217,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
/>
<Route
path={`${WebsitePaths.Portal}/trades`}
- component={this._renderTradeHistory.bind(this)}
+ render={this._renderTradeHistory.bind(this)}
/>
<Route
path={`${WebsitePaths.Home}`}
@@ -291,46 +276,12 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
</div>
);
}
- public onToggleLedgerDialog() {
+ public onToggleLedgerDialog(): void {
this.setState({
isLedgerDialogOpen: !this.state.isLedgerDialogOpen,
});
}
- private _renderWallet() {
- const allTokens = _.values(this.props.tokenByAddress);
- const trackedTokens = _.filter(allTokens, t => t.isTracked);
- return (
- <div className="flex flex-center">
- <div className="mx-auto">
- <Wallet
- userAddress={this.props.userAddress}
- networkId={this.props.networkId}
- blockchain={this._blockchain}
- blockchainIsLoaded={this.props.blockchainIsLoaded}
- blockchainErr={this.props.blockchainErr}
- dispatcher={this.props.dispatcher}
- tokenByAddress={this.props.tokenByAddress}
- trackedTokens={trackedTokens}
- userEtherBalanceInWei={this.props.userEtherBalanceInWei}
- lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
- injectedProviderName={this.props.injectedProviderName}
- providerType={this.props.providerType}
- onToggleLedgerDialog={this.onToggleLedgerDialog.bind(this)}
- />
- </div>
- </div>
- );
- }
- private _renderRelayers() {
- return (
- <div className="flex flex-center">
- <div className="mx-auto" style={{ width: 800 }}>
- <RelayerIndex networkId={this.props.networkId} />
- </div>
- </div>
- );
- }
- private _renderEthWrapper() {
+ private _renderEthWrapper(): React.ReactNode {
return (
<EthWrappers
networkId={this.props.networkId}
@@ -343,7 +294,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
/>
);
}
- private _renderTradeHistory() {
+ private _renderTradeHistory(): React.ReactNode {
return (
<TradeHistory
tokenByAddress={this.props.tokenByAddress}
@@ -352,7 +303,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
/>
);
}
- private _renderTokenBalances() {
+ private _renderTokenBalances(): React.ReactNode {
const allTokens = _.values(this.props.tokenByAddress);
const trackedTokens = _.filter(allTokens, t => t.isTracked);
return (
@@ -371,7 +322,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
/>
);
}
- private _renderFillOrder(match: any, location: Location, history: History) {
+ private _renderFillOrder(match: any, location: Location, history: History): React.ReactNode {
const initialFillOrder = !_.isUndefined(this.props.userSuppliedOrderCache)
? this.props.userSuppliedOrderCache
: this._sharedOrderIfExists;
@@ -390,7 +341,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
/>
);
}
- private _renderGenerateOrderForm(match: any, location: Location, history: History) {
+ private _renderGenerateOrderForm(match: any, location: Location, history: History): React.ReactNode {
return (
<GenerateOrderForm
blockchain={this._blockchain}
@@ -399,13 +350,13 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
/>
);
}
- private _onPortalDisclaimerAccepted() {
+ private _onPortalDisclaimerAccepted(): void {
localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set');
this.setState({
isDisclaimerDialogOpen: false,
});
}
- private _onWethNoticeAccepted() {
+ private _onWethNoticeAccepted(): void {
localStorage.setItem(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE, 'set');
this.setState({
isWethNoticeDialogOpen: false,
@@ -437,7 +388,7 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> {
}
return order;
}
- private _updateScreenWidth() {
+ private _updateScreenWidth(): void {
const newScreenWidth = utils.getScreenWidth();
this.props.dispatcher.updateScreenWidth(newScreenWidth);
}
diff --git a/packages/website/ts/components/portal_menu.tsx b/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx
index 2b4d7eea2..7469ca14e 100644
--- a/packages/website/ts/components/portal_menu.tsx
+++ b/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx
@@ -4,18 +4,18 @@ import { MenuItem } from 'ts/components/ui/menu_item';
import { Environments, WebsitePaths } from 'ts/types';
import { configs } from 'ts/utils/configs';
-export interface PortalMenuProps {
+export interface LegacyPortalMenuProps {
menuItemStyle: React.CSSProperties;
onClick?: () => void;
}
-interface PortalMenuState {}
+interface LegacyPortalMenuState {}
-export class PortalMenu extends React.Component<PortalMenuProps, PortalMenuState> {
- public static defaultProps: Partial<PortalMenuProps> = {
+export class LegacyPortalMenu extends React.Component<LegacyPortalMenuProps, LegacyPortalMenuState> {
+ public static defaultProps: Partial<LegacyPortalMenuProps> = {
onClick: _.noop,
};
- public render() {
+ public render(): React.ReactNode {
return (
<div>
<MenuItem
@@ -58,30 +58,10 @@ export class PortalMenu extends React.Component<PortalMenuProps, PortalMenuState
>
{this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')}
</MenuItem>
- {configs.ENVIRONMENT === Environments.DEVELOPMENT && (
- <div>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/wallet`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Wallet', 'zmdi-balance-wallet')}
- </MenuItem>
- <MenuItem
- style={this.props.menuItemStyle}
- className="py2"
- to={`${WebsitePaths.Portal}/relayers`}
- onClick={this.props.onClick.bind(this)}
- >
- {this._renderMenuItemWithIcon('Relayers', 'zmdi-input-antenna')}
- </MenuItem>
- </div>
- )}
</div>
);
}
- private _renderMenuItemWithIcon(title: string, iconName: string) {
+ private _renderMenuItemWithIcon(title: string, iconName: string): React.ReactNode {
return (
<div className="flex" style={{ fontWeight: 100 }}>
<div className="pr1 pl2">
diff --git a/packages/website/ts/components/order_json.tsx b/packages/website/ts/components/order_json.tsx
index 02b88b888..6feefea50 100644
--- a/packages/website/ts/components/order_json.tsx
+++ b/packages/website/ts/components/order_json.tsx
@@ -38,7 +38,7 @@ export class OrderJSON extends React.Component<OrderJSONProps, OrderJSONState> {
// tslint:disable-next-line:no-floating-promises
this._setShareLinkAsync();
}
- public render() {
+ public render(): React.ReactNode {
const order = utils.generateOrder(
this.props.exchangeContractIfExists,
this.props.sideToAssetToken,
@@ -116,11 +116,11 @@ export class OrderJSON extends React.Component<OrderJSONProps, OrderJSONState> {
</div>
);
}
- private async _shareViaTwitterAsync() {
+ private _shareViaTwitterAsync(): void {
const tweetText = encodeURIComponent(`Fill my order using the 0x protocol: ${this.state.shareLink}`);
window.open(`https://twitter.com/intent/tweet?text=${tweetText}`, 'Share your order', 'width=500,height=400');
}
- private async _shareViaFacebook() {
+ private _shareViaFacebook(): void {
(window as any).FB.ui(
{
display: 'popup',
@@ -130,14 +130,14 @@ export class OrderJSON extends React.Component<OrderJSONProps, OrderJSONState> {
_.noop,
);
}
- private async _shareViaEmailAsync() {
+ private _shareViaEmailAsync(): void {
const encodedSubject = encodeURIComponent("Let's trade using the 0x protocol");
const encodedBody = encodeURIComponent(`I generated an order with the 0x protocol.
You can see and fill it here: ${this.state.shareLink}`);
const mailToLink = `mailto:mail@example.org?subject=${encodedSubject}&body=${encodedBody}`;
window.open(mailToLink, '_blank');
}
- private async _setShareLinkAsync() {
+ private async _setShareLinkAsync(): Promise<void> {
const shareLink = await this._generateShareLinkAsync();
this.setState({
shareLink,
@@ -159,7 +159,7 @@ You can see and fill it here: ${this.state.shareLink}`);
}
return bodyObj.data.url;
}
- private _getOrderUrl() {
+ private _getOrderUrl(): string {
const order = utils.generateOrder(
this.props.exchangeContractIfExists,
this.props.sideToAssetToken,
diff --git a/packages/website/ts/components/portal/back_button.tsx b/packages/website/ts/components/portal/back_button.tsx
new file mode 100644
index 000000000..68934f88e
--- /dev/null
+++ b/packages/website/ts/components/portal/back_button.tsx
@@ -0,0 +1,41 @@
+import { colors, Styles } from '@0xproject/react-shared';
+import * as React from 'react';
+import { Link } from 'react-router-dom';
+
+export interface BackButtonProps {
+ to: string;
+ labelText: string;
+}
+
+const BACK_BUTTON_HEIGHT = 28;
+
+const styles: Styles = {
+ backButton: {
+ height: BACK_BUTTON_HEIGHT,
+ paddingTop: 10,
+ backgroundColor: colors.white,
+ borderRadius: BACK_BUTTON_HEIGHT,
+ boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`,
+ },
+ backButtonIcon: {
+ color: colors.mediumBlue,
+ fontSize: 20,
+ },
+};
+
+export const BackButton = (props: BackButtonProps) => {
+ return (
+ <div style={{ height: 65, paddingTop: 25 }}>
+ <Link to={props.to} style={{ textDecoration: 'none' }}>
+ <div className="flex right" style={styles.backButton}>
+ <div style={{ marginLeft: 12 }}>
+ <i style={styles.backButtonIcon} className={`zmdi zmdi-arrow-left`} />
+ </div>
+ <div style={{ marginLeft: 12, marginRight: 12 }}>
+ <div style={{ fontSize: 16, color: colors.lightGrey }}>{props.labelText}</div>
+ </div>
+ </div>
+ </Link>
+ </div>
+ );
+};
diff --git a/packages/website/ts/components/portal/loading.tsx b/packages/website/ts/components/portal/loading.tsx
new file mode 100644
index 000000000..d804dd1b8
--- /dev/null
+++ b/packages/website/ts/components/portal/loading.tsx
@@ -0,0 +1,21 @@
+import CircularProgress from 'material-ui/CircularProgress';
+import * as React from 'react';
+
+const CIRCULAR_PROGRESS_SIZE = 40;
+const CIRCULAR_PROGRESS_THICKNESS = 5;
+
+export interface LoadingProps {
+ isLoading: boolean;
+ content: React.ReactNode;
+}
+export const Loading = (props: LoadingProps) => {
+ if (props.isLoading) {
+ return (
+ <div className="center">
+ <CircularProgress size={CIRCULAR_PROGRESS_SIZE} thickness={CIRCULAR_PROGRESS_THICKNESS} />
+ </div>
+ );
+ } else {
+ return <div>{props.content}</div>;
+ }
+};
diff --git a/packages/website/ts/components/portal/menu.tsx b/packages/website/ts/components/portal/menu.tsx
new file mode 100644
index 000000000..9014d8d42
--- /dev/null
+++ b/packages/website/ts/components/portal/menu.tsx
@@ -0,0 +1,88 @@
+import { colors, Styles } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+import { MenuItem } from 'ts/components/ui/menu_item';
+import { Environments, WebsitePaths } from 'ts/types';
+import { configs } from 'ts/utils/configs';
+
+export interface MenuProps {
+ selectedPath?: string;
+}
+
+interface MenuItemEntry {
+ to: string;
+ labelText: string;
+ iconName: string;
+}
+
+const menuItemEntries: MenuItemEntry[] = [
+ {
+ to: `${WebsitePaths.Portal}/account`,
+ labelText: 'Account overview',
+ iconName: 'zmdi-balance-wallet',
+ },
+ {
+ to: `${WebsitePaths.Portal}/trades`,
+ labelText: 'Trade history',
+ iconName: 'zmdi-format-list-bulleted',
+ },
+ {
+ to: `${WebsitePaths.Portal}/weth`,
+ labelText: 'Wrapped ETH',
+ iconName: 'zmdi-circle-o',
+ },
+ {
+ to: `${WebsitePaths.Portal}/direct`,
+ labelText: 'Trade direct',
+ iconName: 'zmdi-swap',
+ },
+];
+
+const DEFAULT_LABEL_COLOR = colors.darkerGrey;
+const DEFAULT_ICON_COLOR = colors.darkerGrey;
+const SELECTED_ICON_COLOR = colors.yellow900;
+
+const LEFT_PADDING = 185;
+
+export const Menu: React.StatelessComponent<MenuProps> = (props: MenuProps) => {
+ return (
+ <div style={{ paddingLeft: LEFT_PADDING }}>
+ {_.map(menuItemEntries, entry => {
+ const selected = entry.to === props.selectedPath;
+ return (
+ <MenuItem key={entry.to} className="py2" to={entry.to}>
+ <MenuItemLabel title={entry.labelText} iconName={entry.iconName} selected={selected} />
+ </MenuItem>
+ );
+ })}
+ </div>
+ );
+};
+
+interface MenuItemLabelProps {
+ title: string;
+ iconName: string;
+ selected: boolean;
+}
+const MenuItemLabel: React.StatelessComponent<MenuItemLabelProps> = (props: MenuItemLabelProps) => {
+ const styles: Styles = {
+ iconStyle: {
+ color: props.selected ? SELECTED_ICON_COLOR : DEFAULT_ICON_COLOR,
+ fontSize: 20,
+ },
+ textStyle: {
+ color: DEFAULT_LABEL_COLOR,
+ fontWeight: props.selected ? 'bold' : 'normal',
+ },
+ };
+ return (
+ <div className="flex">
+ <div className="pr1">
+ <i style={styles.iconStyle} className={`zmdi ${props.iconName}`} />
+ </div>
+ <div className="pl1" style={styles.textStyle}>
+ {props.title}
+ </div>
+ </div>
+ );
+};
diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx
new file mode 100644
index 000000000..d9d50c5ab
--- /dev/null
+++ b/packages/website/ts/components/portal/portal.tsx
@@ -0,0 +1,468 @@
+import { colors, Styles } from '@0xproject/react-shared';
+import { BigNumber } from '@0xproject/utils';
+import * as _ from 'lodash';
+import * as React from 'react';
+import * as DocumentTitle from 'react-document-title';
+import { Link, Route, RouteComponentProps, 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 { EthWrappers } from 'ts/components/eth_wrappers';
+import { AssetPicker } from 'ts/components/generate_order/asset_picker';
+import { BackButton } from 'ts/components/portal/back_button';
+import { Loading } from 'ts/components/portal/loading';
+import { Menu } from 'ts/components/portal/menu';
+import { Section } from 'ts/components/portal/section';
+import { TextHeader } from 'ts/components/portal/text_header';
+import { RelayerIndex } from 'ts/components/relayer_index/relayer_index';
+import { TokenBalances } from 'ts/components/token_balances';
+import { TopBar, TopBarDisplayType } 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 { Wallet } from 'ts/components/wallet/wallet';
+import { GenerateOrderForm } from 'ts/containers/generate_order_form';
+import { localStorage } from 'ts/local_storage/local_storage';
+import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage';
+import { FullscreenMessage } from 'ts/pages/fullscreen_message';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import {
+ BlockchainErrs,
+ HashData,
+ Order,
+ ProviderType,
+ ScreenWidths,
+ TokenByAddress,
+ TokenVisibility,
+ WebsitePaths,
+} from 'ts/types';
+import { configs } from 'ts/utils/configs';
+import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
+import { utils } from 'ts/utils/utils';
+
+export interface PortalProps {
+ blockchainErr: BlockchainErrs;
+ blockchainIsLoaded: boolean;
+ dispatcher: Dispatcher;
+ hashData: HashData;
+ injectedProviderName: string;
+ networkId: number;
+ nodeVersion: string;
+ orderFillAmount: BigNumber;
+ providerType: ProviderType;
+ screenWidth: ScreenWidths;
+ tokenByAddress: TokenByAddress;
+ userEtherBalanceInWei: BigNumber;
+ userAddress: string;
+ shouldBlockchainErrDialogBeOpen: boolean;
+ userSuppliedOrderCache: Order;
+ location: Location;
+ flashMessage?: string | React.ReactNode;
+ lastForceTokenStateRefetch: number;
+ translate: Translate;
+}
+
+interface PortalState {
+ prevNetworkId: number;
+ prevNodeVersion: string;
+ prevUserAddress: string;
+ prevPathname: string;
+ isDisclaimerDialogOpen: boolean;
+ isLedgerDialogOpen: boolean;
+ tokenManagementState: TokenManagementState;
+}
+
+interface AccountManagementItem {
+ pathName: string;
+ headerText: string;
+ render: () => React.ReactNode;
+}
+
+enum TokenManagementState {
+ Add = 'Add',
+ Remove = 'Remove',
+ None = 'None',
+}
+
+const THROTTLE_TIMEOUT = 100;
+const TOP_BAR_HEIGHT = TopBar.heightForDisplayType(TopBarDisplayType.Expanded);
+const LEFT_COLUMN_WIDTH = 346;
+
+const styles: Styles = {
+ root: {
+ width: '100%',
+ height: '100%',
+ backgroundColor: colors.lightestGrey,
+ },
+ body: {
+ height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`,
+ },
+ leftColumn: {
+ width: LEFT_COLUMN_WIDTH,
+ height: '100%',
+ },
+ scrollContainer: {
+ height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`,
+ WebkitOverflowScrolling: 'touch',
+ overflow: 'auto',
+ },
+};
+
+export class Portal extends React.Component<PortalProps, PortalState> {
+ private _blockchain: Blockchain;
+ private _throttledScreenWidthUpdate: () => void;
+ constructor(props: PortalProps) {
+ super(props);
+ this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
+ const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER);
+ const hasAcceptedDisclaimer =
+ !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer);
+ this.state = {
+ prevNetworkId: this.props.networkId,
+ prevNodeVersion: this.props.nodeVersion,
+ prevUserAddress: this.props.userAddress,
+ prevPathname: this.props.location.pathname,
+ isDisclaimerDialogOpen: !hasAcceptedDisclaimer,
+ tokenManagementState: TokenManagementState.None,
+ 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, changed user accounts, etc...)
+ this.props.dispatcher.resetState();
+ }
+ public componentWillReceiveProps(nextProps: PortalProps): 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) {
+ this.setState({
+ prevPathname: nextProps.location.pathname,
+ });
+ }
+ }
+ public render(): React.ReactNode {
+ const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind(
+ this.props.dispatcher,
+ );
+ const isAssetPickerDialogOpen = this.state.tokenManagementState !== TokenManagementState.None;
+ const tokenVisibility =
+ this.state.tokenManagementState === TokenManagementState.Add
+ ? TokenVisibility.UNTRACKED
+ : TokenVisibility.TRACKED;
+ return (
+ <div style={styles.root}>
+ <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}
+ displayType={TopBarDisplayType.Expanded}
+ style={{ backgroundColor: colors.lightestGrey }}
+ />
+ <div id="portal" style={styles.body}>
+ <Switch>
+ <Route
+ path={`${WebsitePaths.Portal}/:route`}
+ render={this._renderMenuAndAccountManagement.bind(this)}
+ />
+ <Route
+ exact={true}
+ path={`${WebsitePaths.Portal}/`}
+ render={this._renderWalletAndRelayerIndex.bind(this)}
+ />
+ </Switch>
+ <BlockchainErrDialog
+ blockchain={this._blockchain}
+ blockchainErr={this.props.blockchainErr}
+ isOpen={this.props.shouldBlockchainErrDialogBeOpen}
+ userAddress={this.props.userAddress}
+ toggleDialogFn={updateShouldBlockchainErrDialogBeOpen}
+ networkId={this.props.networkId}
+ />
+ <PortalDisclaimerDialog
+ isOpen={this.state.isDisclaimerDialogOpen}
+ onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)}
+ />
+ <FlashMessage dispatcher={this.props.dispatcher} flashMessage={this.props.flashMessage} />
+ {this.props.blockchainIsLoaded && (
+ <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}
+ />
+ )}
+ <AssetPicker
+ userAddress={this.props.userAddress}
+ networkId={this.props.networkId}
+ blockchain={this._blockchain}
+ dispatcher={this.props.dispatcher}
+ isOpen={isAssetPickerDialogOpen}
+ currentTokenAddress={''}
+ onTokenChosen={this._onTokenChosen.bind(this)}
+ tokenByAddress={this.props.tokenByAddress}
+ tokenVisibility={tokenVisibility}
+ />
+ </div>
+ </div>
+ );
+ }
+ private _renderWalletAndRelayerIndex(): React.ReactNode {
+ return <PortalLayout left={this._renderWallet()} right={this._renderRelayerIndexSection()} />;
+ }
+ private _renderMenuAndAccountManagement(routeComponentProps: RouteComponentProps<any>): React.ReactNode {
+ return <PortalLayout left={this._renderMenu(routeComponentProps)} right={this._renderAccountManagement()} />;
+ }
+ private _renderMenu(routeComponentProps: RouteComponentProps<any>): React.ReactNode {
+ return (
+ <Section
+ header={<BackButton to={`${WebsitePaths.Portal}`} labelText="back to Relayers" />}
+ body={<Menu selectedPath={routeComponentProps.location.pathname} />}
+ />
+ );
+ }
+ private _renderWallet(): React.ReactNode {
+ const allTokens = _.values(this.props.tokenByAddress);
+ const trackedTokens = _.filter(allTokens, t => t.isTracked);
+ return (
+ <Section
+ header={<TextHeader labelText="Your Account" />}
+ body={
+ <Wallet
+ userAddress={this.props.userAddress}
+ networkId={this.props.networkId}
+ blockchain={this._blockchain}
+ blockchainIsLoaded={this.props.blockchainIsLoaded}
+ blockchainErr={this.props.blockchainErr}
+ dispatcher={this.props.dispatcher}
+ tokenByAddress={this.props.tokenByAddress}
+ trackedTokens={trackedTokens}
+ userEtherBalanceInWei={this.props.userEtherBalanceInWei}
+ lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch}
+ injectedProviderName={this.props.injectedProviderName}
+ providerType={this.props.providerType}
+ onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)}
+ onAddToken={this._onAddToken.bind(this)}
+ onRemoveToken={this._onRemoveToken.bind(this)}
+ />
+ }
+ />
+ );
+ }
+ private _renderAccountManagement(): React.ReactNode {
+ const accountManagementItems: AccountManagementItem[] = [
+ {
+ pathName: `${WebsitePaths.Portal}/weth`,
+ headerText: 'Wrapped ETH',
+ render: this._renderEthWrapper.bind(this),
+ },
+ {
+ pathName: `${WebsitePaths.Portal}/account`,
+ headerText: 'Your Account',
+ render: this._renderTokenBalances.bind(this),
+ },
+ {
+ pathName: `${WebsitePaths.Portal}/trades`,
+ headerText: 'Trade History',
+ render: this._renderTradeHistory.bind(this),
+ },
+ {
+ pathName: `${WebsitePaths.Portal}/direct`,
+ headerText: 'Trade Direct',
+ render: this._renderTradeDirect.bind(this),
+ },
+ ];
+ return (
+ <Switch>
+ {_.map(accountManagementItems, item => {
+ return <Route path={item.pathName} render={this._renderAccountManagementItem.bind(this, item)} />;
+ })}}
+ <Route render={this._renderNotFoundMessage.bind(this)} />
+ </Switch>
+ );
+ }
+ private _renderAccountManagementItem(item: AccountManagementItem): React.ReactNode {
+ return (
+ <Section
+ header={<TextHeader labelText={item.headerText} />}
+ body={<Loading isLoading={!this.props.blockchainIsLoaded} content={item.render()} />}
+ />
+ );
+ }
+ 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 _renderTradeDirect(match: any, location: Location, history: History): React.ReactNode {
+ return (
+ <GenerateOrderForm
+ blockchain={this._blockchain}
+ hashData={this.props.hashData}
+ dispatcher={this.props.dispatcher}
+ />
+ );
+ }
+ private _renderTokenBalances(): React.ReactNode {
+ const allTokens = _.values(this.props.tokenByAddress);
+ const trackedTokens = _.filter(allTokens, t => t.isTracked);
+ 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 _renderRelayerIndexSection(): React.ReactNode {
+ return (
+ <Section
+ header={<TextHeader labelText="Explore 0x Relayers" />}
+ body={<RelayerIndex networkId={this.props.networkId} />}
+ />
+ );
+ }
+ private _renderNotFoundMessage(): React.ReactNode {
+ return (
+ <FullscreenMessage
+ headerText="404 Not Found"
+ bodyText="Hm... looks like we couldn't find what you are looking for."
+ />
+ );
+ }
+ private _onTokenChosen(tokenAddress: string): void {
+ if (_.isEmpty(tokenAddress)) {
+ this.setState({
+ tokenManagementState: TokenManagementState.None,
+ });
+ return;
+ }
+ const token = this.props.tokenByAddress[tokenAddress];
+ const isDefaultTrackedToken = _.includes(configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, token.symbol);
+ if (this.state.tokenManagementState === TokenManagementState.Remove && !isDefaultTrackedToken) {
+ if (token.isRegistered) {
+ // Remove the token from tracked tokens
+ const newToken = {
+ ...token,
+ isTracked: false,
+ };
+ this.props.dispatcher.updateTokenByAddress([newToken]);
+ } else {
+ this.props.dispatcher.removeTokenToTokenByAddress(token);
+ }
+ trackedTokenStorage.removeTrackedToken(this.props.userAddress, this.props.networkId, tokenAddress);
+ } else if (isDefaultTrackedToken) {
+ this.props.dispatcher.showFlashMessage(`Cannot remove ${token.name} because it's a default token`);
+ }
+ this.setState({
+ tokenManagementState: TokenManagementState.None,
+ });
+ }
+ private _onToggleLedgerDialog(): void {
+ this.setState({
+ isLedgerDialogOpen: !this.state.isLedgerDialogOpen,
+ });
+ }
+ private _onAddToken(): void {
+ this.setState({
+ tokenManagementState: TokenManagementState.Add,
+ });
+ }
+ private _onRemoveToken(): void {
+ this.setState({
+ tokenManagementState: TokenManagementState.Remove,
+ });
+ }
+ private _onPortalDisclaimerAccepted(): void {
+ localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set');
+ this.setState({
+ isDisclaimerDialogOpen: false,
+ });
+ }
+ private _updateScreenWidth(): void {
+ const newScreenWidth = utils.getScreenWidth();
+ this.props.dispatcher.updateScreenWidth(newScreenWidth);
+ }
+}
+
+interface PortalLayoutProps {
+ left: React.ReactNode;
+ right: React.ReactNode;
+}
+const PortalLayout = (props: PortalLayoutProps) => {
+ return (
+ <div className="sm-flex flex-center">
+ <div className="flex-last px3">
+ <div style={styles.leftColumn}>{props.left}</div>
+ </div>
+ <div className="flex-auto px3" style={styles.scrollContainer}>
+ {props.right}
+ </div>
+ </div>
+ );
+}; // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/components/portal/section.tsx b/packages/website/ts/components/portal/section.tsx
new file mode 100644
index 000000000..9b172aae0
--- /dev/null
+++ b/packages/website/ts/components/portal/section.tsx
@@ -0,0 +1,15 @@
+import { Styles } from '@0xproject/react-shared';
+import * as React from 'react';
+
+export interface SectionProps {
+ header: React.ReactNode;
+ body: React.ReactNode;
+}
+export const Section = (props: SectionProps) => {
+ return (
+ <div className="flex flex-column" style={{ height: '100%' }}>
+ {props.header}
+ <div className="flex-auto">{props.body}</div>
+ </div>
+ );
+};
diff --git a/packages/website/ts/components/portal/text_header.tsx b/packages/website/ts/components/portal/text_header.tsx
new file mode 100644
index 000000000..4aabd47d0
--- /dev/null
+++ b/packages/website/ts/components/portal/text_header.tsx
@@ -0,0 +1,21 @@
+import { Styles } from '@0xproject/react-shared';
+import * as React from 'react';
+
+export interface TextHeaderProps {
+ labelText: string;
+}
+
+const styles: Styles = {
+ title: {
+ fontWeight: 'bold',
+ fontSize: 20,
+ },
+};
+
+export const TextHeader = (props: TextHeaderProps) => {
+ return (
+ <div className="py3" style={styles.title}>
+ {props.labelText}
+ </div>
+ );
+};
diff --git a/packages/website/ts/components/redirecter.tsx b/packages/website/ts/components/redirecter.tsx
index 2e705af58..629522bbb 100644
--- a/packages/website/ts/components/redirecter.tsx
+++ b/packages/website/ts/components/redirecter.tsx
@@ -5,6 +5,6 @@ interface RedirecterProps {
location: string;
}
-export function Redirecter(props: RedirecterProps) {
+export function Redirecter(props: RedirecterProps): void {
window.location.href = constants.URL_ANGELLIST;
}
diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
index 0c4b2841c..5964dcd56 100644
--- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
+++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
@@ -12,38 +12,6 @@ export interface RelayerGridTileProps {
networkId: number;
}
-// TODO: Get top tokens and headerurl from remote
-const headerUrl = '/images/og_image.png';
-const topTokens = [
- {
- address: '0x1dad4783cf3fe3085c1426157ab175a6119a04ba',
- decimals: 18,
- iconUrl: '/images/token_icons/makerdao.png',
- isRegistered: true,
- isTracked: true,
- name: 'Maker DAO',
- symbol: 'MKR',
- },
- {
- address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
- decimals: 18,
- iconUrl: '/images/token_icons/melon.png',
- isRegistered: true,
- isTracked: true,
- name: 'Melon Token',
- symbol: 'MLN',
- },
- {
- address: '0xb18845c260f680d5b9d84649638813e342e4f8c9',
- decimals: 18,
- iconUrl: '/images/token_icons/augur.png',
- isRegistered: true,
- isTracked: true,
- name: 'Augur Reputation Token',
- symbol: 'REP',
- },
-];
-
const styles: Styles = {
root: {
backgroundColor: colors.white,
@@ -68,6 +36,9 @@ const styles: Styles = {
borderBottomLeftRadius: 4,
borderTopRightRadius: 4,
borderTopLeftRadius: 4,
+ borderWidth: 1,
+ borderStyle: 'solid',
+ borderColor: colors.walletBorder,
},
body: {
paddingLeft: 6,
@@ -76,7 +47,7 @@ const styles: Styles = {
width: '100%',
boxSizing: 'border-box',
},
- dailyTradeVolumeLabel: {
+ weeklyTradeVolumeLabel: {
fontSize: 14,
color: colors.mediumBlue,
},
@@ -91,20 +62,29 @@ const styles: Styles = {
},
};
+const FALLBACK_IMG_SRC = '/images/landing/hero_chip_image.png';
+
export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = (props: RelayerGridTileProps) => {
+ const link = props.relayerInfo.appUrl || props.relayerInfo.url;
return (
<GridTile style={styles.root}>
<div style={styles.innerDiv}>
- <img src={headerUrl} style={styles.header} />
+ <a href={link} target="_blank" style={{ textDecoration: 'none' }}>
+ <ImgWithFallback
+ src={props.relayerInfo.headerImgUrl}
+ fallbackSrc={FALLBACK_IMG_SRC}
+ style={styles.header}
+ />
+ </a>
<div style={styles.body}>
<div className="py1" style={styles.relayerNameLabel}>
{props.relayerInfo.name}
</div>
- <div style={styles.dailyTradeVolumeLabel}>{props.relayerInfo.dailyTxnVolume}</div>
+ <div style={styles.weeklyTradeVolumeLabel}>{props.relayerInfo.weeklyTxnVolume}</div>
<div className="py1" style={styles.subLabel}>
- Daily Trade Volume
+ Weekly Trade Volume
</div>
- <TopTokens tokens={topTokens} networkId={props.networkId} />
+ <TopTokens tokens={props.relayerInfo.topTokens} networkId={props.networkId} />
<div className="py1" style={styles.subLabel}>
Top tokens
</div>
@@ -113,3 +93,32 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = (
</GridTile>
);
};
+
+interface ImgWithFallbackProps {
+ src?: string;
+ fallbackSrc: string;
+ style: React.CSSProperties;
+}
+interface ImgWithFallbackState {
+ imageLoadFailed: boolean;
+}
+class ImgWithFallback extends React.Component<ImgWithFallbackProps, ImgWithFallbackState> {
+ constructor(props: ImgWithFallbackProps) {
+ super(props);
+ this.state = {
+ imageLoadFailed: false,
+ };
+ }
+ public render(): React.ReactNode {
+ if (this.state.imageLoadFailed || _.isUndefined(this.props.src)) {
+ return <img src={this.props.fallbackSrc} style={this.props.style} />;
+ } else {
+ return <img src={this.props.src} onError={this._onError.bind(this)} style={this.props.style} />;
+ }
+ }
+ private _onError(): void {
+ this.setState({
+ imageLoadFailed: true,
+ });
+ }
+}
diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx
index 50760c32d..b327c9817 100644
--- a/packages/website/ts/components/relayer_index/relayer_index.tsx
+++ b/packages/website/ts/components/relayer_index/relayer_index.tsx
@@ -1,5 +1,7 @@
import { colors, Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
+import CircularProgress from 'material-ui/CircularProgress';
+import FlatButton from 'material-ui/FlatButton';
import { GridList } from 'material-ui/GridList';
import * as React from 'react';
@@ -32,9 +34,9 @@ const styles: Styles = {
},
};
-const CELL_HEIGHT = 260;
+const CELL_HEIGHT = 290;
const NUMBER_OF_COLUMNS = 4;
-const GRID_PADDING = 16;
+const GRID_PADDING = 20;
export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerIndexState> {
private _isUnmounted: boolean;
@@ -46,16 +48,27 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde
error: undefined,
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
// tslint:disable-next-line:no-floating-promises
this._fetchRelayerInfosAsync();
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
}
- public render() {
+ public render(): React.ReactNode {
const readyToRender = _.isUndefined(this.state.error) && !_.isUndefined(this.state.relayerInfos);
- if (readyToRender) {
+ if (!readyToRender) {
+ return (
+ // TODO: consolidate this loading component with the one in portal
+ <div className="center">
+ {_.isUndefined(this.state.error) ? (
+ <CircularProgress size={40} thickness={5} />
+ ) : (
+ <Retry onRetry={this._fetchRelayerInfosAsync.bind(this)} />
+ )}
+ </div>
+ );
+ } else {
return (
<div style={styles.root}>
<GridList
@@ -64,23 +77,22 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde
padding={GRID_PADDING}
style={styles.gridList}
>
- {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo) => (
- <RelayerGridTile
- key={relayerInfo.id}
- relayerInfo={relayerInfo}
- networkId={this.props.networkId}
- />
+ {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo, index) => (
+ <RelayerGridTile key={index} relayerInfo={relayerInfo} networkId={this.props.networkId} />
))}
</GridList>
</div>
);
- } else {
- // TODO: loading and error states with a scrolling container
- return null;
}
}
private async _fetchRelayerInfosAsync(): Promise<void> {
try {
+ if (!this._isUnmounted) {
+ this.setState({
+ relayerInfos: undefined,
+ error: undefined,
+ });
+ }
const relayerInfos = await backendClient.getRelayerInfosAsync();
if (!this._isUnmounted) {
this.setState({
@@ -96,3 +108,31 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde
}
}
}
+
+interface RetryProps {
+ onRetry: () => void;
+}
+const Retry = (props: RetryProps) => (
+ <div className="clearfix center" style={{ color: colors.black }}>
+ <div className="mx-auto inline-block align-middle" style={{ lineHeight: '44px', textAlign: 'center' }}>
+ <div className="h2" style={{ fontFamily: 'Roboto Mono' }}>
+ Something went wrong.
+ </div>
+ <div className="py3">
+ <FlatButton
+ label={'reload'}
+ backgroundColor={colors.black}
+ labelStyle={{
+ fontSize: 18,
+ fontFamily: 'Roboto Mono',
+ fontWeight: 'lighter',
+ color: colors.white,
+ textTransform: 'lowercase',
+ }}
+ style={{ width: 280, height: 62, borderRadius: 5 }}
+ onClick={props.onRetry}
+ />
+ </div>
+ </div>
+ </div>
+);
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 233590b78..03c70c9dd 100644
--- a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx
+++ b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx
@@ -3,10 +3,10 @@ import * as _ from 'lodash';
import * as React from 'react';
import { TokenIcon } from 'ts/components/ui/token_icon';
-import { Token } from 'ts/types';
+import { WebsiteBackendTokenInfo } from 'ts/types';
export interface TopTokensProps {
- tokens: Token[];
+ tokens: WebsiteBackendTokenInfo[];
networkId: number;
}
@@ -23,24 +23,57 @@ const styles: Styles = {
export const TopTokens: React.StatelessComponent<TopTokensProps> = (props: TopTokensProps) => {
return (
<div className="flex">
- {_.map(props.tokens, (token: Token, index: number) => {
+ {_.map(props.tokens, (tokenInfo: WebsiteBackendTokenInfo, index: number) => {
const firstItemStyle = { ...styles.tokenLabel, ...styles.followingTokenLabel };
const style = index !== 0 ? firstItemStyle : styles.tokenLabel;
- return (
- <a
- key={token.address}
- href={tokenLinkFromToken(token, props.networkId)}
- target="_blank"
- style={style}
- >
- {token.symbol}
- </a>
- );
+ return <TokenLink tokenInfo={tokenInfo} style={style} networkId={props.networkId} />;
})}
</div>
);
};
-function tokenLinkFromToken(token: Token, networkId: number) {
- return sharedUtils.getEtherScanLinkIfExists(token.address, networkId, EtherscanLinkSuffixes.Address);
+interface TokenLinkProps {
+ tokenInfo: WebsiteBackendTokenInfo;
+ style: React.CSSProperties;
+ networkId: number;
+}
+interface TokenLinkState {
+ isHovering: boolean;
+}
+
+class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> {
+ constructor(props: TokenLinkProps) {
+ super(props);
+ this.state = {
+ isHovering: false,
+ };
+ }
+ public render(): React.ReactNode {
+ const style = {
+ ...this.props.style,
+ cursor: 'pointer',
+ opacity: this.state.isHovering ? 0.5 : 1,
+ };
+ return (
+ <a
+ key={this.props.tokenInfo.address}
+ href={tokenLinkFromToken(this.props.tokenInfo, this.props.networkId)}
+ target="_blank"
+ style={style}
+ onMouseEnter={this._onToggleHover.bind(this, true)}
+ onMouseLeave={this._onToggleHover.bind(this, false)}
+ >
+ {this.props.tokenInfo.symbol}
+ </a>
+ );
+ }
+ private _onToggleHover(isHovering: boolean): void {
+ this.setState({
+ isHovering,
+ });
+ }
+}
+
+function tokenLinkFromToken(tokenInfo: WebsiteBackendTokenInfo, networkId: number): string {
+ return sharedUtils.getEtherScanLinkIfExists(tokenInfo.address, networkId, EtherscanLinkSuffixes.Address);
}
diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx
index 79c103e05..8486dbd8b 100644
--- a/packages/website/ts/components/send_button.tsx
+++ b/packages/website/ts/components/send_button.tsx
@@ -33,7 +33,7 @@ export class SendButton extends React.Component<SendButtonProps, SendButtonState
isSending: false,
};
}
- public render() {
+ public render(): React.ReactNode {
const labelStyle = this.state.isSending ? { fontSize: 10 } : {};
return (
<div>
@@ -57,12 +57,12 @@ export class SendButton extends React.Component<SendButtonProps, SendButtonState
</div>
);
}
- private _toggleSendDialog() {
+ private _toggleSendDialog(): void {
this.setState({
isSendDialogVisible: !this.state.isSendDialogVisible,
});
}
- private async _onSendAmountSelectedAsync(recipient: string, value: BigNumber) {
+ private async _onSendAmountSelectedAsync(recipient: string, value: BigNumber): Promise<void> {
this.setState({
isSending: true,
});
diff --git a/packages/website/ts/components/sidebar_header.tsx b/packages/website/ts/components/sidebar_header.tsx
index a0ea869fb..bf46caad9 100644
--- a/packages/website/ts/components/sidebar_header.tsx
+++ b/packages/website/ts/components/sidebar_header.tsx
@@ -12,7 +12,7 @@ interface SidebarHeaderProps {
interface SidebarHeaderState {}
export class SidebarHeader extends React.Component<SidebarHeaderProps, SidebarHeaderState> {
- public render() {
+ public render(): React.ReactNode {
return (
<div className="pt2 md-px1 sm-px2" style={{ color: colors.black, paddingBottom: 18 }}>
<div className="flex" style={{ fontSize: 25 }}>
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx
index 618b5fe8f..83948e5c2 100644
--- a/packages/website/ts/components/token_balances.tsx
+++ b/packages/website/ts/components/token_balances.tsx
@@ -99,15 +99,15 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
trackedTokenStateByAddress: initialTrackedTokenStateByAddress,
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress);
// tslint:disable-next-line:no-floating-promises
this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses);
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
}
- public componentWillReceiveProps(nextProps: TokenBalancesProps) {
+ public componentWillReceiveProps(nextProps: TokenBalancesProps): void {
if (nextProps.userEtherBalanceInWei !== this.props.userEtherBalanceInWei) {
if (this.state.isBalanceSpinnerVisible) {
const receivedAmountInWei = nextProps.userEtherBalanceInWei.minus(this.props.userEtherBalanceInWei);
@@ -153,10 +153,10 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
this._fetchBalancesAndAllowancesAsync(newTokenAddresses);
}
}
- public componentDidMount() {
+ public componentDidMount(): void {
window.scrollTo(0, 0);
}
- public render() {
+ public render(): React.ReactNode {
const errorDialogActions = [
<FlatButton
key="errorOkBtn"
@@ -294,7 +294,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
</div>
);
}
- private _renderTokenTableRows() {
+ private _renderTokenTableRows(): React.ReactNode {
if (!this.props.blockchainIsLoaded || this.props.blockchainErr !== BlockchainErrs.NoError) {
return '';
}
@@ -313,7 +313,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
);
return tableRows;
}
- private _renderTokenRow(tokenColSpan: number, actionPaddingX: number, token: Token) {
+ private _renderTokenRow(tokenColSpan: number, actionPaddingX: number, token: Token): React.ReactNode {
const tokenState = this.state.trackedTokenStateByAddress[token.address];
const tokenLink = sharedUtils.getEtherScanLinkIfExists(
token.address,
@@ -411,7 +411,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
</TableRow>
);
}
- private _onAssetTokenPicked(tokenAddress: string) {
+ private _onAssetTokenPicked(tokenAddress: string): void {
if (_.isEmpty(tokenAddress)) {
this.setState({
isTokenPickerOpen: false,
@@ -439,16 +439,16 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
isTokenPickerOpen: false,
});
}
- private _onSendFailed() {
+ private _onSendFailed(): void {
this.setState({
errorType: BalanceErrs.sendFailed,
});
}
- private _renderAmount(amount: BigNumber, decimals: number) {
+ private _renderAmount(amount: BigNumber, decimals: number): React.ReactNode {
const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
return unitAmount.toNumber().toFixed(configs.AMOUNT_DISPLAY_PRECSION);
}
- private _renderTokenName(token: Token) {
+ private _renderTokenName(token: Token): React.ReactNode {
const tooltipId = `tooltip-${token.address}`;
return (
<div className="flex">
@@ -460,7 +460,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
</div>
);
}
- private _renderErrorDialogBody() {
+ private _renderErrorDialogBody(): React.ReactNode {
switch (this.state.errorType) {
case BalanceErrs.incorrectNetworkForFaucet:
return (
@@ -499,7 +499,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
throw utils.spawnSwitchErr('errorType', this.state.errorType);
}
}
- private _onErrorOccurred(errorType: BalanceErrs) {
+ private _onErrorOccurred(errorType: BalanceErrs): void {
this.setState({
errorType,
});
@@ -577,24 +577,24 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
}
return true;
}
- private _onErrorDialogToggle(isOpen: boolean) {
+ private _onErrorDialogToggle(isOpen: boolean): void {
this.setState({
errorType: undefined,
});
}
- private _onAddTokenClicked() {
+ private _onAddTokenClicked(): void {
this.setState({
isTokenPickerOpen: true,
isAddingToken: true,
});
}
- private _onRemoveTokenClicked() {
+ private _onRemoveTokenClicked(): void {
this.setState({
isTokenPickerOpen: true,
isAddingToken: false,
});
}
- private async _startPollingZrxBalanceAsync() {
+ private async _startPollingZrxBalanceAsync(): Promise<void> {
const tokens = _.values(this.props.tokenByAddress);
const zrxToken = _.find(tokens, t => t.symbol === ZRX_TOKEN_SYMBOL);
@@ -609,7 +609,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
isZRXSpinnerVisible: false,
});
}
- private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]) {
+ private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]): Promise<void> {
const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress;
const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
for (const tokenAddress of tokenAddresses) {
@@ -629,7 +629,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
});
}
}
- private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]) {
+ private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]): TokenStateByAddress {
const trackedTokenStateByAddress: TokenStateByAddress = {};
_.each(trackedTokens, token => {
trackedTokenStateByAddress[token.address] = {
@@ -640,7 +640,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala
});
return trackedTokenStateByAddress;
}
- private async _refetchTokenStateAsync(tokenAddress: string) {
+ private async _refetchTokenStateAsync(tokenAddress: string): Promise<void> {
const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync(
userAddressIfExists,
diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx
index 79e7c3e2d..bebaa5341 100644
--- a/packages/website/ts/components/top_bar/provider_display.tsx
+++ b/packages/website/ts/components/top_bar/provider_display.tsx
@@ -1,4 +1,4 @@
-import { colors } from '@0xproject/react-shared';
+import { colors, Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
import RaisedButton from 'material-ui/RaisedButton';
import * as React from 'react';
@@ -11,9 +11,9 @@ import { ProviderType } from 'ts/types';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
-const IDENTICON_DIAMETER = 32;
+const ROOT_HEIGHT = 24;
-interface ProviderDisplayProps {
+export interface ProviderDisplayProps {
dispatcher: Dispatcher;
userAddress: string;
networkId: number;
@@ -25,8 +25,17 @@ interface ProviderDisplayProps {
interface ProviderDisplayState {}
+const styles: Styles = {
+ root: {
+ height: ROOT_HEIGHT,
+ backgroundColor: colors.white,
+ borderRadius: ROOT_HEIGHT,
+ boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`,
+ },
+};
+
export class ProviderDisplay extends React.Component<ProviderDisplayProps, ProviderDisplayState> {
- public render() {
+ public render(): React.ReactNode {
const isAddressAvailable = !_.isEmpty(this.props.userAddress);
const isExternallyInjectedProvider =
this.props.providerType === ProviderType.Injected && this.props.injectedProviderName !== '0x Public';
@@ -42,21 +51,20 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
: 'Connect a wallet';
const providerTitle =
this.props.providerType === ProviderType.Injected ? injectedProviderName : 'Ledger Nano S';
+ const isProviderMetamask = providerTitle === constants.PROVIDER_NAME_METAMASK;
const hoverActiveNode = (
- <div className="flex right lg-pr0 md-pr2 sm-pr2" style={{ paddingTop: 16 }}>
+ <div className="flex right lg-pr0 md-pr2 sm-pr2 p1" style={styles.root}>
<div>
- <Identicon address={this.props.userAddress} diameter={IDENTICON_DIAMETER} />
- </div>
- <div style={{ marginLeft: 12, paddingTop: 1 }}>
- <div style={{ fontSize: 12, color: colors.amber800 }}>{providerTitle}</div>
- <div style={{ fontSize: 14 }}>{displayAddress}</div>
+ <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} />
</div>
- <div
- style={{ borderLeft: `1px solid ${colors.grey300}`, marginLeft: 17, paddingTop: 1 }}
- className="px2"
- >
- <i style={{ fontSize: 30, color: colors.grey300 }} className="zmdi zmdi zmdi-chevron-down" />
+ <div style={{ marginLeft: 12, paddingTop: 3 }}>
+ <div style={{ fontSize: 16, color: colors.darkGrey }}>{displayAddress}</div>
</div>
+ {isProviderMetamask && (
+ <div style={{ marginLeft: 16 }}>
+ <img src="/images/metamask_icon.png" style={{ width: ROOT_HEIGHT, height: ROOT_HEIGHT }} />
+ </div>
+ )}
</div>
);
const hasInjectedProvider =
@@ -75,7 +83,7 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi
</div>
);
}
- public renderPopoverContent(hasInjectedProvider: boolean, hasLedgerProvider: boolean) {
+ public renderPopoverContent(hasInjectedProvider: boolean, hasLedgerProvider: boolean): React.ReactNode {
if (hasInjectedProvider || hasLedgerProvider) {
return (
<ProviderPicker
diff --git a/packages/website/ts/components/top_bar/provider_picker.tsx b/packages/website/ts/components/top_bar/provider_picker.tsx
index b986da873..1ecb8389c 100644
--- a/packages/website/ts/components/top_bar/provider_picker.tsx
+++ b/packages/website/ts/components/top_bar/provider_picker.tsx
@@ -19,7 +19,7 @@ interface ProviderPickerProps {
interface ProviderPickerState {}
export class ProviderPicker extends React.Component<ProviderPickerProps, ProviderPickerState> {
- public render() {
+ public render(): React.ReactNode {
const isLedgerSelected = this.props.providerType === ProviderType.Ledger;
const menuStyle = {
padding: 10,
@@ -46,7 +46,7 @@ export class ProviderPicker extends React.Component<ProviderPickerProps, Provide
</div>
);
}
- private _renderLabel(title: string, shouldShowNetwork: boolean) {
+ private _renderLabel(title: string, shouldShowNetwork: boolean): React.ReactNode {
const label = (
<div className="flex">
<div style={{ fontSize: 14 }}>{title}</div>
@@ -55,7 +55,7 @@ export class ProviderPicker extends React.Component<ProviderPickerProps, Provide
);
return label;
}
- private _renderNetwork() {
+ private _renderNetwork(): React.ReactNode {
const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId];
return (
<div className="flex" style={{ marginTop: 1 }}>
@@ -70,7 +70,7 @@ export class ProviderPicker extends React.Component<ProviderPickerProps, Provide
</div>
);
}
- private _onProviderRadioChanged(value: string) {
+ private _onProviderRadioChanged(value: string): void {
if (value === ProviderType.Ledger) {
this.props.onToggleLedgerDialog();
} else {
diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx
index 13351dcdc..5fde007d6 100644
--- a/packages/website/ts/components/top_bar/top_bar.tsx
+++ b/packages/website/ts/components/top_bar/top_bar.tsx
@@ -8,7 +8,7 @@ import * as React from 'react';
import { Link } from 'react-router-dom';
import ReactTooltip = require('react-tooltip');
import { Blockchain } from 'ts/blockchain';
-import { PortalMenu } from 'ts/components/portal_menu';
+import { LegacyPortalMenu } from 'ts/components/legacy_portal/legacy_portal_menu';
import { SidebarHeader } from 'ts/components/sidebar_header';
import { ProviderDisplay } from 'ts/components/top_bar/provider_display';
import { TopBarMenuItem } from 'ts/components/top_bar/top_bar_menu_item';
@@ -19,7 +19,12 @@ import { Deco, Key, ProviderType, WebsiteLegacyPaths, WebsitePaths } from 'ts/ty
import { constants } from 'ts/utils/constants';
import { Translate } from 'ts/utils/translate';
-interface TopBarProps {
+export enum TopBarDisplayType {
+ Default,
+ Expanded,
+}
+
+export interface TopBarProps {
userAddress?: string;
networkId?: number;
injectedProviderName?: string;
@@ -34,7 +39,7 @@ interface TopBarProps {
availableDocVersions?: string[];
menu?: DocsMenu;
menuSubsectionsBySection?: MenuSubsectionsBySection;
- shouldFullWidth?: boolean;
+ displayType?: TopBarDisplayType;
docsInfo?: DocsInfo;
style?: React.CSSProperties;
isNightVersion?: boolean;
@@ -47,17 +52,8 @@ interface TopBarState {
}
const styles: Styles = {
- address: {
- marginRight: 12,
- overflow: 'hidden',
- paddingTop: 4,
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- width: 70,
- },
topBar: {
- backgroundcolor: colors.white,
- height: 59,
+ backgroundColor: colors.white,
width: '100%',
position: 'relative',
top: 0,
@@ -78,22 +74,30 @@ const styles: Styles = {
},
};
+const DEFAULT_HEIGHT = 59;
+const EXPANDED_HEIGHT = 75;
+
export class TopBar extends React.Component<TopBarProps, TopBarState> {
public static defaultProps: Partial<TopBarProps> = {
- shouldFullWidth: false,
+ displayType: TopBarDisplayType.Default,
style: {},
isNightVersion: false,
};
+ public static heightForDisplayType(displayType: TopBarDisplayType): number {
+ const result = displayType === TopBarDisplayType.Expanded ? EXPANDED_HEIGHT : DEFAULT_HEIGHT;
+ return result + 1;
+ }
constructor(props: TopBarProps) {
super(props);
this.state = {
isDrawerOpen: false,
};
}
- public render() {
+ public render(): React.ReactNode {
const isNightVersion = this.props.isNightVersion;
- const isFullWidthPage = this.props.shouldFullWidth;
- const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`;
+ const isExpandedDisplayType = this.props.displayType === TopBarDisplayType.Expanded;
+ const parentClassNames = `flex mx-auto ${isExpandedDisplayType ? 'pl3 py1' : 'max-width-4'}`;
+ const height = isExpandedDisplayType ? EXPANDED_HEIGHT : DEFAULT_HEIGHT;
const developerSectionMenuItems = [
<Link key="subMenuItem-zeroEx" to={WebsitePaths.ZeroExJs} className="text-decoration-none">
<MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x.js" />
@@ -139,10 +143,16 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
primaryText={this.props.translate.get(Key.Web3Wrapper, Deco.CapWords)}
/>
</Link>,
- <Link key="subMenuItem-deployer" to={WebsitePaths.Deployer} className="text-decoration-none">
+ <Link key="subMenuItem-order-utils" to={WebsitePaths.OrderUtils} className="text-decoration-none">
<MenuItem
style={{ fontSize: styles.menuItem.fontSize }}
- primaryText={this.props.translate.get(Key.Deployer, Deco.CapWords)}
+ primaryText={this.props.translate.get(Key.OrderUtils, Deco.CapWords)}
+ />
+ </Link>,
+ <Link key="subMenuItem-sol-compiler" to={WebsitePaths.SolCompiler} className="text-decoration-none">
+ <MenuItem
+ style={{ fontSize: styles.menuItem.fontSize }}
+ primaryText={this.props.translate.get(Key.SolCompiler, Deco.CapWords)}
/>
</Link>,
<Link key="subMenuItem-sol-cov" to={WebsitePaths.SolCov} className="text-decoration-none">
@@ -172,9 +182,11 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</a>,
];
const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {};
- const fullWidthClasses = isFullWidthPage ? 'pr4' : '';
+ const fullWidthClasses = isExpandedDisplayType ? 'pr4' : '';
const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png';
- const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`;
+ const menuClasses = `col col-${
+ isExpandedDisplayType ? '4' : '5'
+ } ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`;
const menuIconStyle = {
fontSize: 25,
color: isNightVersion ? 'white' : 'black',
@@ -191,15 +203,15 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
);
const popoverContent = <Menu style={{ color: colors.darkGrey }}>{developerSectionMenuItems}</Menu>;
return (
- <div style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style }} className="pb1">
+ <div style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style, ...{ height } }} className="pb1">
<div className={parentClassNames}>
- <div className="col col-2 sm-pl2 md-pl2 lg-pl0" style={{ paddingTop: 15 }}>
+ <div className="col col-2 sm-pl1 md-pl2 lg-pl0" style={{ paddingTop: 15 }}>
<Link to={`${WebsitePaths.Home}`} className="text-decoration-none">
<img src={logoUrl} height="30" />
</Link>
</div>
- <div className={`col col-${isFullWidthPage ? '8' : '9'} lg-hide md-hide`} />
- <div className={`col col-${isFullWidthPage ? '6' : '5'} sm-hide xs-hide`} />
+ <div className={`col col-${isExpandedDisplayType ? '8' : '9'} lg-hide md-hide`} />
+ <div className={`col col-${isExpandedDisplayType ? '6' : '5'} sm-hide xs-hide`} />
{!this._isViewingPortal() && (
<div className={menuClasses}>
<div className="flex justify-between">
@@ -236,7 +248,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
path={`${WebsitePaths.Portal}`}
isPrimary={true}
style={styles.menuItem}
- className={`${isFullWidthPage && 'md-hide'}`}
+ className={`${isExpandedDisplayType && 'md-hide'}`}
isNightVersion={isNightVersion}
isExternal={false}
/>
@@ -244,7 +256,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</div>
)}
{this.props.blockchainIsLoaded && (
- <div className="sm-hide xs-hide col col-5">
+ <div className="sm-hide xs-hide col col-5" style={{ paddingTop: 8, marginRight: 36 }}>
<ProviderDisplay
dispatcher={this.props.dispatcher}
userAddress={this.props.userAddress}
@@ -256,7 +268,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
/>
</div>
)}
- <div className={`col ${isFullWidthPage ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}>
+ <div className={`col ${isExpandedDisplayType ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}>
<div style={menuIconStyle}>
<i className="zmdi zmdi-menu" onClick={this._onMenuButtonClick.bind(this)} />
</div>
@@ -266,7 +278,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</div>
);
}
- private _renderDrawer() {
+ private _renderDrawer(): React.ReactNode {
return (
<Drawer
open={this.state.isDrawerOpen}
@@ -316,10 +328,10 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
</MenuItem>
</Link>
)}
- {!this._isViewingDeployerDocs() && (
- <Link to={WebsitePaths.Deployer} className="text-decoration-none">
+ {!this._isViewingSolCompilerDocs() && (
+ <Link to={WebsitePaths.SolCompiler} className="text-decoration-none">
<MenuItem className="py2">
- {this.props.translate.get(Key.Deployer, Deco.Cap)}{' '}
+ {this.props.translate.get(Key.SolCompiler, Deco.Cap)}{' '}
{this.props.translate.get(Key.Docs, Deco.Cap)}
</MenuItem>
</Link>
@@ -378,7 +390,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
(!this._isViewing0xjsDocs() &&
!this._isViewingSmartContractsDocs() &&
!this._isViewingWeb3WrapperDocs() &&
- !this._isViewingDeployerDocs() &&
+ !this._isViewingSolCompilerDocs() &&
!this._isViewingJsonSchemasDocs() &&
!this._isViewingSolCovDocs() &&
!this._isViewingSubprovidersDocs() &&
@@ -431,81 +443,67 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> {
<div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}>
{this.props.translate.get(Key.PortalDApp, Deco.CapWords)}
</div>
- <PortalMenu menuItemStyle={{ color: 'black' }} onClick={this._onMenuButtonClick.bind(this)} />
- </div>
- );
- }
- private _renderUser() {
- const userAddress = this.props.userAddress;
- const identiconDiameter = 26;
- return (
- <div className="flex right lg-pr0 md-pr2 sm-pr2" style={{ paddingTop: 16 }}>
- <div style={styles.address} data-tip={true} data-for="userAddressTooltip">
- {!_.isEmpty(userAddress) ? userAddress : ''}
- </div>
- <ReactTooltip id="userAddressTooltip">{userAddress}</ReactTooltip>
- <div>
- <Identicon address={userAddress} diameter={identiconDiameter} />
- </div>
+ <LegacyPortalMenu menuItemStyle={{ color: 'black' }} onClick={this._onMenuButtonClick.bind(this)} />
</div>
);
}
- private _onMenuButtonClick() {
+ private _onMenuButtonClick(): void {
this.setState({
isDrawerOpen: !this.state.isDrawerOpen,
});
}
- private _isViewingPortal() {
+ private _isViewingPortal(): boolean {
return _.includes(this.props.location.pathname, WebsitePaths.Portal);
}
- private _isViewingFAQ() {
+ private _isViewingFAQ(): boolean {
return _.includes(this.props.location.pathname, WebsitePaths.FAQ);
}
- private _isViewing0xjsDocs() {
+ private _isViewing0xjsDocs(): boolean {
return (
_.includes(this.props.location.pathname, WebsitePaths.ZeroExJs) ||
_.includes(this.props.location.pathname, WebsiteLegacyPaths.ZeroExJs)
);
}
- private _isViewingConnectDocs() {
+ private _isViewingConnectDocs(): boolean {
return _.includes(this.props.location.pathname, WebsitePaths.Connect);
}
- private _isViewingSmartContractsDocs() {
+ private _isViewingSmartContractsDocs(): boolean {
return _.includes(this.props.location.pathname, WebsitePaths.SmartContracts);
}
- private _isViewingWeb3WrapperDocs() {
+ private _isViewingWeb3WrapperDocs(): boolean {
return (
_.includes(this.props.location.pathname, WebsitePaths.Web3Wrapper) ||
_.includes(this.props.location.pathname, WebsiteLegacyPaths.Web3Wrapper)
);
}
- private _isViewingDeployerDocs() {
- return _.includes(this.props.location.pathname, WebsitePaths.Deployer);
+ private _isViewingSolCompilerDocs(): boolean {
+ return _.includes(this.props.location.pathname, WebsitePaths.SolCompiler);
}
- private _isViewingJsonSchemasDocs() {
+ private _isViewingJsonSchemasDocs(): boolean {
return _.includes(this.props.location.pathname, WebsitePaths.JSONSchemas);
}
- private _isViewingSolCovDocs() {
+ private _isViewingSolCovDocs(): boolean {
return _.includes(this.props.location.pathname, WebsitePaths.SolCov);
}
- private _isViewingSubprovidersDocs() {
+ private _isViewingSubprovidersDocs(): boolean {
return _.includes(this.props.location.pathname, WebsitePaths.Subproviders);
}
- private _isViewingWiki() {
+ private _isViewingWiki(): boolean {
return _.includes(this.props.location.pathname, WebsitePaths.Wiki);
}
- private _shouldDisplayBottomBar() {
+ private _shouldDisplayBottomBar(): boolean {
return (
this._isViewingWiki() ||
this._isViewing0xjsDocs() ||
this._isViewingFAQ() ||
this._isViewingSmartContractsDocs() ||
this._isViewingWeb3WrapperDocs() ||
- this._isViewingDeployerDocs() ||
+ this._isViewingSolCompilerDocs() ||
this._isViewingJsonSchemasDocs() ||
this._isViewingSolCovDocs() ||
this._isViewingSubprovidersDocs() ||
- this._isViewingConnectDocs()
+ this._isViewingConnectDocs() ||
+ this._isViewingPortal()
);
}
} // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/components/top_bar/top_bar_menu_item.tsx b/packages/website/ts/components/top_bar/top_bar_menu_item.tsx
index c0e674b17..2e4254cfa 100644
--- a/packages/website/ts/components/top_bar/top_bar_menu_item.tsx
+++ b/packages/website/ts/components/top_bar/top_bar_menu_item.tsx
@@ -26,7 +26,7 @@ export class TopBarMenuItem extends React.Component<TopBarMenuItemProps, TopBarM
className: '',
isNightVersion: false,
};
- public render() {
+ public render(): React.ReactNode {
const primaryStyles = this.props.isPrimary
? {
borderRadius: 4,
diff --git a/packages/website/ts/components/track_token_confirmation.tsx b/packages/website/ts/components/track_token_confirmation.tsx
index 8c5ba7e6f..294fb8590 100644
--- a/packages/website/ts/components/track_token_confirmation.tsx
+++ b/packages/website/ts/components/track_token_confirmation.tsx
@@ -15,7 +15,7 @@ interface TrackTokenConfirmationProps {
interface TrackTokenConfirmationState {}
export class TrackTokenConfirmation extends React.Component<TrackTokenConfirmationProps, TrackTokenConfirmationState> {
- public render() {
+ public render(): React.ReactNode {
const isMultipleTokens = this.props.tokens.length > 1;
const allTokens = _.values(this.props.tokenByAddress);
return (
diff --git a/packages/website/ts/components/trade_history/trade_history.tsx b/packages/website/ts/components/trade_history/trade_history.tsx
index 635358627..1ca9d866f 100644
--- a/packages/website/ts/components/trade_history/trade_history.tsx
+++ b/packages/website/ts/components/trade_history/trade_history.tsx
@@ -28,16 +28,16 @@ export class TradeHistory extends React.Component<TradeHistoryProps, TradeHistor
sortedFills,
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
this._startPollingForFills();
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._stopPollingForFills();
}
- public componentDidMount() {
+ public componentDidMount(): void {
window.scrollTo(0, 0);
}
- public render() {
+ public render(): React.ReactNode {
return (
<div className="lg-px4 md-px4 sm-px2">
<h3>Trade history</h3>
@@ -48,7 +48,7 @@ export class TradeHistory extends React.Component<TradeHistoryProps, TradeHistor
</div>
);
}
- private _renderTrades() {
+ private _renderTrades(): React.ReactNode {
const numNonCustomFills = this._numFillsWithoutCustomERC20Tokens();
if (numNonCustomFills === 0) {
return this._renderEmptyNotice();
@@ -66,14 +66,14 @@ export class TradeHistory extends React.Component<TradeHistoryProps, TradeHistor
);
});
}
- private _renderEmptyNotice() {
+ private _renderEmptyNotice(): React.ReactNode {
return (
<Paper className="mt1 p2 mx-auto center" style={{ width: '80%' }}>
No filled orders yet.
</Paper>
);
}
- private _numFillsWithoutCustomERC20Tokens() {
+ private _numFillsWithoutCustomERC20Tokens(): number {
let numNonCustomFills = 0;
const tokens = _.values(this.props.tokenByAddress);
_.each(this.state.sortedFills, fill => {
@@ -93,7 +93,7 @@ export class TradeHistory extends React.Component<TradeHistoryProps, TradeHistor
});
return numNonCustomFills;
}
- private _startPollingForFills() {
+ private _startPollingForFills(): void {
this._fillPollingIntervalId = window.setInterval(() => {
const sortedFills = this._getSortedFills();
if (!utils.deepEqual(sortedFills, this.state.sortedFills)) {
@@ -103,10 +103,10 @@ export class TradeHistory extends React.Component<TradeHistoryProps, TradeHistor
}
}, FILL_POLLING_INTERVAL);
}
- private _stopPollingForFills() {
+ private _stopPollingForFills(): void {
clearInterval(this._fillPollingIntervalId);
}
- private _getSortedFills() {
+ private _getSortedFills(): Fill[] {
const fillsByHash = tradeHistoryStorage.getUserFillsByHash(this.props.userAddress, this.props.networkId);
const fills = _.values(fillsByHash);
const sortedFills = _.sortBy(fills, [(fill: Fill) => fill.blockTimestamp * -1]);
diff --git a/packages/website/ts/components/trade_history/trade_history_item.tsx b/packages/website/ts/components/trade_history/trade_history_item.tsx
index dbe72259b..adca4d58c 100644
--- a/packages/website/ts/components/trade_history/trade_history_item.tsx
+++ b/packages/website/ts/components/trade_history/trade_history_item.tsx
@@ -23,7 +23,7 @@ interface TradeHistoryItemProps {
interface TradeHistoryItemState {}
export class TradeHistoryItem extends React.Component<TradeHistoryItemProps, TradeHistoryItemState> {
- public render() {
+ public render(): React.ReactNode {
const fill = this.props.fill;
const tokens = _.values(this.props.tokenByAddress);
const takerToken = _.find(tokens, token => {
@@ -88,7 +88,7 @@ export class TradeHistoryItem extends React.Component<TradeHistoryItemProps, Tra
</Paper>
);
}
- private _renderAmounts(makerToken: Token, takerToken: Token) {
+ private _renderAmounts(makerToken: Token, takerToken: Token): React.ReactNode {
const fill = this.props.fill;
const filledTakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledTakerTokenAmount, takerToken.decimals);
const filledMakerTokenAmountInUnits = ZeroEx.toUnitAmount(fill.filledMakerTokenAmount, takerToken.decimals);
@@ -136,7 +136,7 @@ export class TradeHistoryItem extends React.Component<TradeHistoryItemProps, Tra
</div>
);
}
- private _renderDate() {
+ private _renderDate(): React.ReactNode {
const blockMoment = moment.unix(this.props.fill.blockTimestamp);
if (!blockMoment.isValid()) {
return null;
@@ -159,7 +159,7 @@ export class TradeHistoryItem extends React.Component<TradeHistoryItemProps, Tra
</div>
);
}
- private _renderAmount(amount: BigNumber, symbol: string, decimals: number) {
+ private _renderAmount(amount: BigNumber, symbol: string, decimals: number): React.ReactNode {
const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
return (
<span>
diff --git a/packages/website/ts/components/ui/alert.tsx b/packages/website/ts/components/ui/alert.tsx
index f81939255..b571d8c1c 100644
--- a/packages/website/ts/components/ui/alert.tsx
+++ b/packages/website/ts/components/ui/alert.tsx
@@ -7,7 +7,7 @@ interface AlertProps {
message: string | React.ReactNode;
}
-export function Alert(props: AlertProps) {
+export const Alert = (props: AlertProps) => {
const isAlert = props.type === AlertTypes.ERROR;
const errMsgStyles = {
background: isAlert ? colors.red200 : colors.lightestGreen,
@@ -22,4 +22,4 @@ export function Alert(props: AlertProps) {
{props.message}
</div>
);
-}
+};
diff --git a/packages/website/ts/components/ui/copy_icon.tsx b/packages/website/ts/components/ui/copy_icon.tsx
index d58e50815..2c2941067 100644
--- a/packages/website/ts/components/ui/copy_icon.tsx
+++ b/packages/website/ts/components/ui/copy_icon.tsx
@@ -23,14 +23,14 @@ export class CopyIcon extends React.Component<CopyIconProps, CopyIconState> {
isHovering: false,
};
}
- public componentDidUpdate() {
+ public componentDidUpdate(): void {
// Remove tooltip if hover away
if (!this.state.isHovering && this._copyTooltipTimeoutId) {
clearInterval(this._copyTooltipTimeoutId);
this._hideTooltip();
}
}
- public render() {
+ public render(): React.ReactNode {
return (
<div className="inline-block">
<CopyToClipboard text={this.props.data} onCopy={this._onCopy.bind(this)}>
@@ -55,15 +55,15 @@ export class CopyIcon extends React.Component<CopyIconProps, CopyIconState> {
</div>
);
}
- private _setRefToProperty(el: HTMLInputElement) {
+ private _setRefToProperty(el: HTMLInputElement): void {
this._copyable = el;
}
- private _setHoverState(isHovering: boolean) {
+ private _setHoverState(isHovering: boolean): void {
this.setState({
isHovering,
});
}
- private _onCopy() {
+ private _onCopy(): void {
if (this._copyTooltipTimeoutId) {
clearInterval(this._copyTooltipTimeoutId);
}
@@ -73,7 +73,7 @@ export class CopyIcon extends React.Component<CopyIconProps, CopyIconState> {
this._hideTooltip();
}, tooltipLifespanMs);
}
- private _hideTooltip() {
+ private _hideTooltip(): void {
ReactTooltip.hide(ReactDOM.findDOMNode(this._copyable));
}
}
diff --git a/packages/website/ts/components/ui/drop_down.tsx b/packages/website/ts/components/ui/drop_down.tsx
index 63b9eec0b..98a495581 100644
--- a/packages/website/ts/components/ui/drop_down.tsx
+++ b/packages/website/ts/components/ui/drop_down.tsx
@@ -35,15 +35,15 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
isDropDownOpen: false,
};
}
- public componentDidMount() {
+ public componentDidMount(): void {
this._popoverCloseCheckIntervalId = window.setInterval(() => {
this._checkIfShouldClosePopover();
}, CHECK_CLOSE_POPOVER_INTERVAL_MS);
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
window.clearInterval(this._popoverCloseCheckIntervalId);
}
- public componentWillReceiveProps(nextProps: DropDownProps) {
+ public componentWillReceiveProps(nextProps: DropDownProps): void {
// HACK: If the popoverContent is updated to a different dimension and the users
// mouse is no longer above it, the dropdown can enter an inconsistent state where
// it believes the user is still hovering over it. In order to remedy this, we
@@ -52,7 +52,7 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
// dropdowns from having dynamic content.
this._onHoverOff();
}
- public render() {
+ public render(): React.ReactNode {
return (
<div
style={{ ...this.props.style, width: 'fit-content', height: '100%' }}
@@ -77,11 +77,11 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
</div>
);
}
- private _onHover(event: React.FormEvent<HTMLInputElement>) {
+ private _onHover(event: React.FormEvent<HTMLInputElement>): void {
this._isHovering = true;
this._checkIfShouldOpenPopover(event);
}
- private _checkIfShouldOpenPopover(event: React.FormEvent<HTMLInputElement>) {
+ private _checkIfShouldOpenPopover(event: React.FormEvent<HTMLInputElement>): void {
if (this.state.isDropDownOpen) {
return; // noop
}
@@ -91,16 +91,16 @@ export class DropDown extends React.Component<DropDownProps, DropDownState> {
anchorEl: event.currentTarget,
});
}
- private _onHoverOff() {
+ private _onHoverOff(): void {
this._isHovering = false;
}
- private _checkIfShouldClosePopover() {
+ private _checkIfShouldClosePopover(): void {
if (!this.state.isDropDownOpen || this._isHovering) {
return; // noop
}
this._closePopover();
}
- private _closePopover() {
+ private _closePopover(): void {
this.setState({
isDropDownOpen: false,
});
diff --git a/packages/website/ts/components/ui/etherscan_icon.tsx b/packages/website/ts/components/ui/etherscan_icon.tsx
index c1154d3d6..1b032c112 100644
--- a/packages/website/ts/components/ui/etherscan_icon.tsx
+++ b/packages/website/ts/components/ui/etherscan_icon.tsx
@@ -35,6 +35,6 @@ export const EtherScanIcon = (props: EtherScanIconProps) => {
);
};
-function renderIcon() {
+function renderIcon(): React.ReactNode {
return <i style={{ color: colors.amber600 }} className="zmdi zmdi-open-in-new" />;
}
diff --git a/packages/website/ts/components/ui/fake_text_field.tsx b/packages/website/ts/components/ui/fake_text_field.tsx
index 646ae98f6..ac3c30fec 100644
--- a/packages/website/ts/components/ui/fake_text_field.tsx
+++ b/packages/website/ts/components/ui/fake_text_field.tsx
@@ -21,7 +21,7 @@ interface FakeTextFieldProps {
children?: any;
}
-export function FakeTextField(props: FakeTextFieldProps) {
+export const FakeTextField = (props: FakeTextFieldProps) => {
return (
<div className="relative">
{props.label !== '' && <InputLabel text={props.label} />}
@@ -31,4 +31,4 @@ export function FakeTextField(props: FakeTextFieldProps) {
<hr style={styles.hr} />
</div>
);
-}
+};
diff --git a/packages/website/ts/components/ui/flash_message.tsx b/packages/website/ts/components/ui/flash_message.tsx
index 2cb1fc764..2b866676d 100644
--- a/packages/website/ts/components/ui/flash_message.tsx
+++ b/packages/website/ts/components/ui/flash_message.tsx
@@ -19,7 +19,7 @@ export class FlashMessage extends React.Component<FlashMessageProps, FlashMessag
showDurationMs: SHOW_DURATION_MS,
bodyStyle: {},
};
- public render() {
+ public render(): React.ReactNode {
if (!_.isUndefined(this.props.flashMessage)) {
return (
<Snackbar
@@ -34,7 +34,7 @@ export class FlashMessage extends React.Component<FlashMessageProps, FlashMessag
return null;
}
}
- private _onClose() {
+ private _onClose(): void {
this.props.dispatcher.hideFlashMessage();
}
}
diff --git a/packages/website/ts/components/ui/identicon.tsx b/packages/website/ts/components/ui/identicon.tsx
index bad6c2a78..83c86a144 100644
--- a/packages/website/ts/components/ui/identicon.tsx
+++ b/packages/website/ts/components/ui/identicon.tsx
@@ -15,7 +15,7 @@ export class Identicon extends React.Component<IdenticonProps, IdenticonState> {
public static defaultProps: Partial<IdenticonProps> = {
style: {},
};
- public render() {
+ public render(): React.ReactNode {
let address = this.props.address;
if (_.isEmpty(address)) {
address = constants.NULL_ADDRESS;
diff --git a/packages/website/ts/components/ui/input_label.tsx b/packages/website/ts/components/ui/input_label.tsx
index 2506db423..8eda45a5d 100644
--- a/packages/website/ts/components/ui/input_label.tsx
+++ b/packages/website/ts/components/ui/input_label.tsx
@@ -17,7 +17,7 @@ const styles: Styles = {
userSelect: 'none',
width: 240,
zIndex: 1,
- },
+ } as React.CSSProperties,
};
export const InputLabel = (props: InputLabelProps) => {
diff --git a/packages/website/ts/components/ui/lifecycle_raised_button.tsx b/packages/website/ts/components/ui/lifecycle_raised_button.tsx
index c85e11884..b06978f16 100644
--- a/packages/website/ts/components/ui/lifecycle_raised_button.tsx
+++ b/packages/website/ts/components/ui/lifecycle_raised_button.tsx
@@ -42,11 +42,11 @@ export class LifeCycleRaisedButton extends React.Component<LifeCycleRaisedButton
buttonState: ButtonState.READY,
};
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
clearTimeout(this._buttonTimeoutId);
this._didUnmount = true;
}
- public render() {
+ public render(): React.ReactNode {
if (this.props.isHidden) {
return <span />;
}
@@ -77,7 +77,7 @@ export class LifeCycleRaisedButton extends React.Component<LifeCycleRaisedButton
/>
);
}
- public async onClickAsync() {
+ public async onClickAsync(): Promise<void> {
this.setState({
buttonState: ButtonState.LOADING,
});
diff --git a/packages/website/ts/components/ui/menu_item.tsx b/packages/website/ts/components/ui/menu_item.tsx
index 3482f436c..64c0dc49d 100644
--- a/packages/website/ts/components/ui/menu_item.tsx
+++ b/packages/website/ts/components/ui/menu_item.tsx
@@ -24,7 +24,7 @@ export class MenuItem extends React.Component<MenuItemProps, MenuItemState> {
isHovering: false,
};
}
- public render() {
+ public render(): React.ReactNode {
const menuItemStyles = {
cursor: 'pointer',
opacity: this.state.isHovering ? 0.5 : 1,
@@ -43,7 +43,7 @@ export class MenuItem extends React.Component<MenuItemProps, MenuItemState> {
</Link>
);
}
- private _onToggleHover(isHovering: boolean) {
+ private _onToggleHover(isHovering: boolean): void {
this.setState({
isHovering,
});
diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx
index 3d94903d1..0d86a6db5 100644
--- a/packages/website/ts/components/ui/party.tsx
+++ b/packages/website/ts/components/ui/party.tsx
@@ -27,7 +27,7 @@ export class Party extends React.Component<PartyProps, PartyState> {
identiconStyle: {},
identiconDiameter: IDENTICON_DIAMETER,
};
- public render() {
+ public render(): React.ReactNode {
const label = this.props.label;
const address = this.props.address;
const identiconDiameter = this.props.identiconDiameter;
diff --git a/packages/website/ts/components/ui/swap_icon.tsx b/packages/website/ts/components/ui/swap_icon.tsx
index e465a8074..4a6000d1b 100644
--- a/packages/website/ts/components/ui/swap_icon.tsx
+++ b/packages/website/ts/components/ui/swap_icon.tsx
@@ -17,7 +17,7 @@ export class SwapIcon extends React.Component<SwapIconProps, SwapIconState> {
isHovering: false,
};
}
- public render() {
+ public render(): React.ReactNode {
const swapStyles = {
color: this.state.isHovering ? colors.amber600 : colors.amber800,
fontSize: 50,
@@ -34,7 +34,7 @@ export class SwapIcon extends React.Component<SwapIconProps, SwapIconState> {
</div>
);
}
- private _onToggleHover(isHovering: boolean) {
+ private _onToggleHover(isHovering: boolean): void {
this.setState({
isHovering,
});
diff --git a/packages/website/ts/components/ui/token_icon.tsx b/packages/website/ts/components/ui/token_icon.tsx
index ff57a96de..a9ad567ef 100644
--- a/packages/website/ts/components/ui/token_icon.tsx
+++ b/packages/website/ts/components/ui/token_icon.tsx
@@ -11,7 +11,7 @@ interface TokenIconProps {
interface TokenIconState {}
export class TokenIcon extends React.Component<TokenIconProps, TokenIconState> {
- public render() {
+ public render(): React.ReactNode {
const token = this.props.token;
const diameter = this.props.diameter;
return (
diff --git a/packages/website/ts/components/visual_order.tsx b/packages/website/ts/components/visual_order.tsx
index 3bf464e92..76a283547 100644
--- a/packages/website/ts/components/visual_order.tsx
+++ b/packages/website/ts/components/visual_order.tsx
@@ -20,7 +20,7 @@ interface VisualOrderProps {
interface VisualOrderState {}
export class VisualOrder extends React.Component<VisualOrderProps, VisualOrderState> {
- public render() {
+ public render(): React.ReactNode {
const allTokens = _.values(this.props.tokenByAddress);
const makerImage = this.props.makerToken.iconUrl;
const takerImage = this.props.takerToken.iconUrl;
@@ -62,7 +62,7 @@ export class VisualOrder extends React.Component<VisualOrderProps, VisualOrderSt
</div>
);
}
- private _renderAmount(assetToken: AssetToken, token: Token) {
+ private _renderAmount(assetToken: AssetToken, token: Token): React.ReactNode {
const unitAmount = ZeroEx.toUnitAmount(assetToken.amount, token.decimals);
return (
<div style={{ fontSize: 13 }}>
diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx
index d1ae38550..75dbd12e9 100644
--- a/packages/website/ts/components/wallet/wallet.tsx
+++ b/packages/website/ts/components/wallet/wallet.tsx
@@ -9,12 +9,16 @@ import {
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import FlatButton from 'material-ui/FlatButton';
+import FloatingActionButton from 'material-ui/FloatingActionButton';
import { List, ListItem } from 'material-ui/List';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
+import ContentAdd from 'material-ui/svg-icons/content/add';
+import ContentRemove from 'material-ui/svg-icons/content/remove';
import NavigationArrowDownward from 'material-ui/svg-icons/navigation/arrow-downward';
import NavigationArrowUpward from 'material-ui/svg-icons/navigation/arrow-upward';
import Close from 'material-ui/svg-icons/navigation/close';
import * as React from 'react';
+import { Link } from 'react-router-dom';
import ReactTooltip = require('react-tooltip');
import firstBy = require('thenby');
@@ -35,6 +39,7 @@ import {
TokenByAddress,
TokenState,
TokenStateByAddress,
+ WebsitePaths,
} from 'ts/types';
import { backendClient } from 'ts/utils/backend_client';
import { constants } from 'ts/utils/constants';
@@ -55,11 +60,14 @@ export interface WalletProps {
injectedProviderName: string;
providerType: ProviderType;
onToggleLedgerDialog: () => void;
+ onAddToken: () => void;
+ onRemoveToken: () => void;
}
interface WalletState {
trackedTokenStateByAddress: TokenStateByAddress;
wrappedEtherDirection?: Side;
+ isHoveringSidebar: boolean;
}
interface AllowanceToggleConfig {
@@ -74,7 +82,7 @@ interface AccessoryItemConfig {
const styles: Styles = {
root: {
- width: 346,
+ width: '100%',
backgroundColor: colors.white,
borderBottomRightRadius: 10,
borderBottomLeftRadius: 10,
@@ -94,6 +102,9 @@ const styles: Styles = {
},
footerItemInnerDiv: {
paddingLeft: 24,
+ borderTopColor: colors.walletBorder,
+ borderTopStyle: 'solid',
+ borderWidth: 1,
},
borderedItem: {
borderBottomColor: colors.walletBorder,
@@ -114,7 +125,21 @@ const styles: Styles = {
paddingTop: 8,
paddingBottom: 8,
},
- accessoryItemsContainer: { width: 150, right: 8 },
+ accessoryItemsContainer: {
+ width: 150,
+ right: 8,
+ },
+ bodyInnerDiv: {
+ padding: 0,
+ // TODO: make this completely responsive
+ maxHeight: 475,
+ overflow: 'auto',
+ WebkitOverflowScrolling: 'touch',
+ },
+ manageYourWalletText: {
+ color: colors.mediumBlue,
+ fontWeight: 'bold',
+ },
};
const ETHER_ICON_PATH = '/images/ether.png';
@@ -123,6 +148,7 @@ const ZRX_TOKEN_SYMBOL = 'ZRX';
const ETHER_SYMBOL = 'ETH';
const ICON_DIMENSION = 24;
const TOKEN_AMOUNT_DISPLAY_PRECISION = 3;
+const BODY_ITEM_KEY = 'BODY';
const HEADER_ITEM_KEY = 'HEADER';
const FOOTER_ITEM_KEY = 'FOOTER';
const DISCONNECTED_ITEM_KEY = 'DISCONNECTED';
@@ -139,17 +165,18 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
this.state = {
trackedTokenStateByAddress: initialTrackedTokenStateByAddress,
wrappedEtherDirection: undefined,
+ isHoveringSidebar: false,
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress);
// tslint:disable-next-line:no-floating-promises
this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses);
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
}
- public componentWillReceiveProps(nextProps: WalletProps) {
+ public componentWillReceiveProps(nextProps: WalletProps): void {
if (
nextProps.userAddress !== this.props.userAddress ||
nextProps.networkId !== this.props.networkId ||
@@ -175,26 +202,21 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
this._fetchBalancesAndAllowancesAsync(newTokenAddresses);
}
}
- public render() {
+ public render(): React.ReactNode {
const isReadyToRender = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError;
return <div style={styles.root}>{isReadyToRender && this._renderRows()}</div>;
}
- private _renderRows() {
+ private _renderRows(): React.ReactNode {
const isAddressAvailable = !_.isEmpty(this.props.userAddress);
return (
<List style={styles.list}>
{isAddressAvailable
- ? _.concat(
- this._renderConnectedHeaderRows(),
- this._renderEthRows(),
- this._renderTokenRows(),
- this._renderFooterRows(),
- )
+ ? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows())
: _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows())}
</List>
);
}
- private _renderDisconnectedHeaderRows() {
+ private _renderDisconnectedHeaderRows(): React.ReactElement<{}> {
const userAddress = this.props.userAddress;
const primaryText = 'wallet';
return (
@@ -207,7 +229,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
/>
);
}
- private _renderDisconnectedRows() {
+ private _renderDisconnectedRows(): React.ReactElement<{}> {
return (
<WalletDisconnectedItem
key={DISCONNECTED_ITEM_KEY}
@@ -217,24 +239,96 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
/>
);
}
- private _renderConnectedHeaderRows() {
+ private _renderConnectedHeaderRows(): React.ReactElement<{}> {
const userAddress = this.props.userAddress;
const primaryText = utils.getAddressBeginAndEnd(userAddress);
return (
+ <Link key={HEADER_ITEM_KEY} to={`${WebsitePaths.Portal}/account`} style={{ textDecoration: 'none' }}>
+ <ListItem
+ primaryText={primaryText}
+ leftIcon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
+ style={{ ...styles.paddedItem, ...styles.borderedItem }}
+ innerDivStyle={styles.headerItemInnerDiv}
+ />
+ </Link>
+ );
+ }
+ private _renderBody(): React.ReactElement<{}> {
+ const bodyStyle: React.CSSProperties = {
+ ...styles.bodyInnerDiv,
+ overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden',
+ };
+ return (
<ListItem
- key={HEADER_ITEM_KEY}
- primaryText={primaryText}
- leftIcon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />}
- style={{ ...styles.paddedItem, ...styles.borderedItem }}
- innerDivStyle={styles.headerItemInnerDiv}
- />
+ key={BODY_ITEM_KEY}
+ innerDivStyle={bodyStyle}
+ onMouseEnter={this._onSidebarHover.bind(this)}
+ onMouseLeave={this._onSidebarHoverOff.bind(this)}
+ >
+ {this._renderEthRows()}
+ {this._renderTokenRows()}
+ </ListItem>
);
}
- private _renderFooterRows() {
- const primaryText = '+ other tokens';
- return <ListItem key={FOOTER_ITEM_KEY} primaryText={primaryText} innerDivStyle={styles.footerItemInnerDiv} />;
+ private _onSidebarHover(event: React.FormEvent<HTMLInputElement>): void {
+ this.setState({
+ isHoveringSidebar: true,
+ });
+ }
+ private _onSidebarHoverOff(): void {
+ this.setState({
+ isHoveringSidebar: false,
+ });
+ }
+ private _renderFooterRows(): React.ReactElement<{}> {
+ return (
+ <div key={FOOTER_ITEM_KEY}>
+ <ListItem
+ primaryText={
+ <div className="flex">
+ <FloatingActionButton mini={true} zDepth={0} onClick={this.props.onAddToken}>
+ <ContentAdd />
+ </FloatingActionButton>
+ <FloatingActionButton
+ mini={true}
+ zDepth={0}
+ className="px1"
+ onClick={this.props.onRemoveToken}
+ >
+ <ContentRemove />
+ </FloatingActionButton>
+ <div
+ style={{
+ paddingLeft: 10,
+ position: 'relative',
+ top: '50%',
+ transform: 'translateY(33%)',
+ }}
+ >
+ add/remove tokens
+ </div>
+ </div>
+ }
+ disabled={true}
+ innerDivStyle={styles.footerItemInnerDiv}
+ style={styles.borderedItem}
+ />
+ <Link to={`${WebsitePaths.Portal}/account`} style={{ textDecoration: 'none' }}>
+ <ListItem
+ primaryText={
+ <div className="flex right" style={styles.manageYourWalletText}>
+ {'manage your wallet'}
+ </div>
+ // https://github.com/palantir/tslint-react/issues/140
+ // tslint:disable-next-line:jsx-curly-spacing
+ }
+ style={{ ...styles.paddedItem, ...styles.borderedItem }}
+ />
+ </Link>
+ </div>
+ );
}
- private _renderEthRows() {
+ private _renderEthRows(): React.ReactNode {
const primaryText = this._renderAmount(
this.props.userEtherBalanceInWei,
constants.DECIMAL_PLACES_ETH,
@@ -284,7 +378,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
</div>
);
}
- private _renderTokenRows() {
+ private _renderTokenRows(): React.ReactNode {
const trackedTokens = this.props.trackedTokens;
const trackedTokensStartingWithEtherToken = trackedTokens.sort(
firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL)
@@ -293,7 +387,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this));
}
- private _renderTokenRow(token: Token) {
+ private _renderTokenRow(token: Token, index: number): React.ReactNode {
const tokenState = this.state.trackedTokenStateByAddress[token.address];
const tokenLink = sharedUtils.getEtherScanLinkIfExists(
token.address,
@@ -310,12 +404,14 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
tokenState,
},
};
+ // if this is the last item in the list, do not render the border, it is rendered by the footer
+ const borderedStyle = index !== this.props.trackedTokens.length - 1 ? styles.borderedItem : {};
const shouldShowWrapEtherItem =
!_.isUndefined(this.state.wrappedEtherDirection) &&
this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection;
const style = shouldShowWrapEtherItem
? { ...walletItemStyles.focusedItem, ...styles.paddedItem }
- : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem };
+ : { ...styles.tokenItem, ...borderedStyle, ...styles.paddedItem };
const etherToken = this._getEthToken();
return (
<div key={token.address}>
@@ -345,7 +441,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
</div>
);
}
- private _renderAccessoryItems(config: AccessoryItemConfig) {
+ private _renderAccessoryItems(config: AccessoryItemConfig): React.ReactElement<{}> {
const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherDirection);
const shouldShowToggle = !_.isUndefined(config.allowanceToggleConfig);
return (
@@ -361,7 +457,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
</div>
);
}
- private _renderAllowanceToggle(config: AllowanceToggleConfig) {
+ private _renderAllowanceToggle(config: AllowanceToggleConfig): React.ReactNode {
return (
<AllowanceToggle
networkId={this.props.networkId}
@@ -376,13 +472,13 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
/>
);
}
- private _renderAmount(amount: BigNumber, decimals: number, symbol: string) {
+ private _renderAmount(amount: BigNumber, decimals: number, symbol: string): React.ReactNode {
const unitAmount = ZeroEx.toUnitAmount(amount, decimals);
const formattedAmount = unitAmount.toPrecision(TOKEN_AMOUNT_DISPLAY_PRECISION);
const result = `${formattedAmount} ${symbol}`;
return <div style={styles.amountLabel}>{result}</div>;
}
- private _renderValue(amount: BigNumber, decimals: number, price?: BigNumber) {
+ private _renderValue(amount: BigNumber, decimals: number, price?: BigNumber): React.ReactNode {
if (_.isUndefined(price)) {
return null;
}
@@ -392,7 +488,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
const result = `$${formattedAmount}`;
return result;
}
- private _renderTokenIcon(token: Token, tokenLink?: string) {
+ private _renderTokenIcon(token: Token, tokenLink?: string): React.ReactElement<{}> {
const tooltipId = `tooltip-${token.address}`;
const tokenIcon = <TokenIcon token={token} diameter={ICON_DIMENSION} />;
if (_.isUndefined(tokenLink)) {
@@ -405,7 +501,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
}
}
- private _renderWrappedEtherButton(wrappedEtherDirection: Side) {
+ private _renderWrappedEtherButton(wrappedEtherDirection: Side): React.ReactNode {
const isWrappedEtherDirectionOpen = this.state.wrappedEtherDirection === wrappedEtherDirection;
let buttonLabel;
let buttonIcon;
@@ -440,7 +536,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
/>
);
}
- private _getInitialTrackedTokenStateByAddress(tokenAddresses: string[]) {
+ private _getInitialTrackedTokenStateByAddress(tokenAddresses: string[]): TokenStateByAddress {
const trackedTokenStateByAddress: TokenStateByAddress = {};
_.each(tokenAddresses, tokenAddress => {
trackedTokenStateByAddress[tokenAddress] = {
@@ -451,7 +547,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
});
return trackedTokenStateByAddress;
}
- private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]) {
+ private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]): Promise<void> {
const balanceAndAllowanceTupleByAddress: ItemByAddress<BigNumber[]> = {};
const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress;
for (const tokenAddress of tokenAddresses) {
@@ -461,16 +557,16 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
);
balanceAndAllowanceTupleByAddress[tokenAddress] = balanceAndAllowanceTuple;
}
- const pricesByAddress = await this._getPricesByAddressAsync(tokenAddresses);
+ const priceByAddress = await this._getPriceByAddressAsync(tokenAddresses);
const trackedTokenStateByAddress = _.reduce(
tokenAddresses,
(acc, address) => {
const [balance, allowance] = balanceAndAllowanceTupleByAddress[address];
- const price = pricesByAddress[address];
+ const priceIfExists = _.get(priceByAddress, address);
acc[address] = {
balance,
allowance,
- price,
+ price: priceIfExists,
isLoaded: true,
};
return acc;
@@ -484,34 +580,47 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
});
}
}
- private async _refetchTokenStateAsync(tokenAddress: string) {
+ private async _refetchTokenStateAsync(tokenAddress: string): Promise<void> {
await this._fetchBalancesAndAllowancesAsync([tokenAddress]);
}
- private async _getPricesByAddressAsync(tokenAddresses: string[]): Promise<ItemByAddress<BigNumber>> {
+ private async _getPriceByAddressAsync(tokenAddresses: string[]): Promise<ItemByAddress<BigNumber>> {
if (_.isEmpty(tokenAddresses)) {
return {};
}
+ // for each input token address, search for the corresponding symbol in this.props.tokenByAddress, if it exists
+ // create a mapping from existing symbols -> address
+ const tokenAddressBySymbol: { [symbol: string]: string } = {};
+ _.each(tokenAddresses, address => {
+ const tokenIfExists = _.get(this.props.tokenByAddress, address);
+ if (!_.isUndefined(tokenIfExists)) {
+ const symbol = tokenIfExists.symbol;
+ tokenAddressBySymbol[symbol] = address;
+ }
+ });
+ const tokenSymbols = _.keys(tokenAddressBySymbol);
try {
- const websiteBackendPriceInfos = await backendClient.getPriceInfosAsync(tokenAddresses);
- const addresses = _.map(websiteBackendPriceInfos, info => info.address);
- const prices = _.map(websiteBackendPriceInfos, info => new BigNumber(info.price));
- const pricesByAddress = _.zipObject(addresses, prices);
- return pricesByAddress;
+ const priceBySymbol = await backendClient.getPriceInfoAsync(tokenSymbols);
+ const priceByAddress = _.mapKeys(priceBySymbol, (value, symbol) => _.get(tokenAddressBySymbol, symbol));
+ const result = _.mapValues(priceByAddress, price => {
+ const priceBigNumber = new BigNumber(price);
+ return priceBigNumber;
+ });
+ return result;
} catch (err) {
return {};
}
}
- private _openWrappedEtherActionRow(wrappedEtherDirection: Side) {
+ private _openWrappedEtherActionRow(wrappedEtherDirection: Side): void {
this.setState({
wrappedEtherDirection,
});
}
- private _closeWrappedEtherActionRow() {
+ private _closeWrappedEtherActionRow(): void {
this.setState({
wrappedEtherDirection: undefined,
});
}
- private _getEthToken() {
+ private _getEthToken(): Token {
const tokens = _.values(this.props.tokenByAddress);
const etherToken = _.find(tokens, { symbol: ETHER_TOKEN_SYMBOL });
return etherToken;
diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx
index a38163770..581d2ba97 100644
--- a/packages/website/ts/components/wallet/wrap_ether_item.tsx
+++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx
@@ -31,8 +31,8 @@ export interface WrapEtherItemProps {
interface WrapEtherItemState {
currentInputAmount?: BigNumber;
- currentInputHasErrors: boolean;
isEthConversionHappening: boolean;
+ errorMsg: React.ReactNode;
}
const styles: Styles = {
@@ -46,13 +46,29 @@ const styles: Styles = {
padding: 4,
width: 125,
},
- ethAmountInput: { height: 32 },
- innerDiv: { paddingLeft: 60, paddingTop: 0 },
- wrapEtherConfirmationButtonContainer: { width: 128, top: 16 },
+ amountInput: { height: 34 },
+ amountInputLabel: {
+ paddingTop: 10,
+ paddingRight: 10,
+ paddingLeft: 5,
+ color: colors.grey,
+ fontSize: 14,
+ },
+ amountInputHint: {
+ bottom: 18,
+ },
+ innerDiv: { paddingLeft: 60, paddingTop: 0, paddingBottom: 10 },
+ wrapEtherConfirmationButtonContainer: { width: 128, top: 19 },
wrapEtherConfirmationButtonLabel: {
- fontSize: 10,
+ fontSize: 12,
color: colors.white,
},
+ errorMsg: {
+ fontSize: 12,
+ marginTop: 4,
+ color: colors.red,
+ minHeight: 20,
+ },
};
export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEtherItemState> {
@@ -60,11 +76,11 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
super(props);
this.state = {
currentInputAmount: undefined,
- currentInputHasErrors: false,
isEthConversionHappening: false,
+ errorMsg: null,
};
}
- public render() {
+ public render(): React.ReactNode {
const etherBalanceInEth = ZeroEx.toUnitAmount(this.props.userEtherBalanceInWei, constants.DECIMAL_PLACES_ETH);
const isWrappingEth = this.props.direction === Side.Deposit;
const topLabelText = isWrappingEth ? 'Convert ETH into WETH 1:1' : 'Convert WETH into ETH 1:1';
@@ -84,7 +100,10 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
shouldShowIncompleteErrs={false}
shouldShowErrs={false}
shouldShowUnderline={false}
- style={styles.ethAmountInput}
+ style={styles.amountInput}
+ labelStyle={styles.amountInputLabel}
+ inputHintStyle={styles.amountInputHint}
+ onErrorMsgChange={this._onErrorMsgChange.bind(this)}
/>
) : (
<TokenAmountInput
@@ -99,12 +118,16 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
onChange={this._onValueChange.bind(this)}
amount={this.state.currentInputAmount}
hintText="0.00"
- shouldShowErrs={false} // TODO: error handling
+ shouldShowErrs={false}
shouldShowUnderline={false}
- style={styles.ethAmountInput}
+ style={styles.amountInput}
+ labelStyle={styles.amountInputLabel}
+ inputHintStyle={styles.amountInputHint}
+ onErrorMsgChange={this._onErrorMsgChange.bind(this)}
/>
)}
</div>
+ {this._renderErrorMsg()}
</div>
}
secondaryTextLines={2}
@@ -116,20 +139,24 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
/>
);
}
- private _onValueChange(isValid: boolean, amount?: BigNumber) {
+ private _onValueChange(isValid: boolean, amount?: BigNumber): void {
this.setState({
currentInputAmount: amount,
- currentInputHasErrors: !isValid,
});
}
- private _renderIsEthConversionHappeningSpinner() {
+ private _onErrorMsgChange(errorMsg: React.ReactNode): void {
+ this.setState({
+ errorMsg,
+ });
+ }
+ private _renderIsEthConversionHappeningSpinner(): React.ReactElement<{}> {
return this.state.isEthConversionHappening ? (
<div className="pl1" style={{ paddingTop: 10 }}>
<i className="zmdi zmdi-spinner zmdi-hc-spin" />
</div>
) : null;
}
- private _renderWrapEtherConfirmationButton() {
+ private _renderWrapEtherConfirmationButton(): React.ReactElement<{}> {
const isWrappingEth = this.props.direction === Side.Deposit;
const labelText = isWrappingEth ? 'wrap' : 'unwrap';
return (
@@ -138,13 +165,20 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther
backgroundColor={colors.wrapEtherConfirmationButton}
label={labelText}
labelStyle={styles.wrapEtherConfirmationButtonLabel}
- onClick={this._wrapEtherConfirmationAction.bind(this)}
+ onClick={this._wrapEtherConfirmationActionAsync.bind(this)}
disabled={this.state.isEthConversionHappening}
/>
</div>
);
}
- private async _wrapEtherConfirmationAction() {
+ private _renderErrorMsg(): React.ReactNode {
+ return (
+ <div style={styles.errorMsg}>
+ {this.state.errorMsg}
+ </div>
+ );
+ }
+ private async _wrapEtherConfirmationActionAsync(): Promise<void> {
this.setState({
isEthConversionHappening: true,
});
diff --git a/packages/website/ts/containers/legacy_portal.ts b/packages/website/ts/containers/legacy_portal.ts
new file mode 100644
index 000000000..3b1172a44
--- /dev/null
+++ b/packages/website/ts/containers/legacy_portal.ts
@@ -0,0 +1,92 @@
+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/order_utils_documentation.ts b/packages/website/ts/containers/order_utils_documentation.ts
new file mode 100644
index 000000000..64aa7300f
--- /dev/null
+++ b/packages/website/ts/containers/order_utils_documentation.ts
@@ -0,0 +1,99 @@
+import { constants as docConstants, DocsInfo, DocsInfoConfig, SupportedDocJson } from '@0xproject/react-docs';
+import * as _ from 'lodash';
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { State } from 'ts/redux/reducer';
+import { DocPackages, Environments, WebsitePaths } from 'ts/types';
+import { configs } from 'ts/utils/configs';
+import { constants } from 'ts/utils/constants';
+import { Translate } from 'ts/utils/translate';
+
+/* tslint:disable:no-var-requires */
+const IntroMarkdown = require('md/docs/order_utils/introduction');
+const InstallationMarkdown = require('md/docs/order_utils/installation');
+/* tslint:enable:no-var-requires */
+
+const docSections = {
+ introduction: 'introduction',
+ installation: 'installation',
+ usage: 'usage',
+ types: 'types',
+};
+
+const docsInfoConfig: DocsInfoConfig = {
+ id: DocPackages.OrderUtils,
+ type: SupportedDocJson.TypeDoc,
+ displayName: 'Order utils',
+ packageUrl: 'https://github.com/0xProject/0x-monorepo',
+ menu: {
+ introduction: [docSections.introduction],
+ install: [docSections.installation],
+ usage: [docSections.usage],
+ types: [docSections.types],
+ },
+ sectionNameToMarkdown: {
+ [docSections.introduction]: IntroMarkdown,
+ [docSections.installation]: InstallationMarkdown,
+ },
+ sectionNameToModulePath: {
+ [docSections.usage]: [
+ '"order-utils/src/order_hash"',
+ '"order-utils/src/signature_utils"',
+ '"order-utils/src/order_factory"',
+ '"order-utils/src/salt"',
+ '"order-utils/src/assert"',
+ '"order-utils/src/constants"',
+ ],
+ [docSections.types]: ['"order-utils/src/types"', '"types/src/index"'],
+ },
+ menuSubsectionToVersionWhenIntroduced: {},
+ sections: docSections,
+ visibleConstructors: [],
+ typeConfigs: {
+ // Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
+ // currently no way to extract the re-exported types from index.ts via TypeDoc :(
+ publicTypes: [
+ 'OrderError',
+ 'Order',
+ 'SignedOrder',
+ 'ECSignature',
+ 'Provider',
+ 'JSONRPCRequestPayload',
+ 'JSONRPCResponsePayload',
+ 'JSONRPCErrorCallback',
+ ],
+ typeNameToExternalLink: {
+ BigNumber: constants.URL_BIGNUMBERJS_GITHUB,
+ },
+ },
+};
+const docsInfo = new DocsInfo(docsInfoConfig);
+
+interface ConnectedState {
+ docsVersion: string;
+ availableDocVersions: string[];
+ docsInfo: DocsInfo;
+ translate: Translate;
+}
+
+interface ConnectedDispatch {
+ dispatcher: Dispatcher;
+}
+
+const mapStateToProps = (state: State, ownProps: DocPageProps): ConnectedState => ({
+ docsVersion: state.docsVersion,
+ availableDocVersions: state.availableDocVersions,
+ translate: state.translate,
+ docsInfo,
+});
+
+const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
+ dispatcher: new Dispatcher(dispatch),
+});
+
+export const Documentation: React.ComponentClass<DocPageProps> = connect(mapStateToProps, mapDispatchToProps)(
+ DocPageComponent,
+);
diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts
index 725564ead..3f0feb6e9 100644
--- a/packages/website/ts/containers/portal.ts
+++ b/packages/website/ts/containers/portal.ts
@@ -3,7 +3,7 @@ import * as _ from 'lodash';
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
-import { Portal as PortalComponent, PortalAllProps as PortalComponentAllProps } from 'ts/components/portal';
+import { Portal as PortalComponent, PortalProps as PortalComponentProps } from 'ts/components/portal/portal';
import { Dispatcher } from 'ts/redux/dispatcher';
import { State } from 'ts/redux/reducer';
import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types';
@@ -34,7 +34,7 @@ interface ConnectedDispatch {
dispatcher: Dispatcher;
}
-const mapStateToProps = (state: State, ownProps: PortalComponentAllProps): ConnectedState => {
+const mapStateToProps = (state: State, ownProps: PortalComponentProps): ConnectedState => {
const receiveAssetToken = state.sideToAssetToken[Side.Receive];
const depositAssetToken = state.sideToAssetToken[Side.Deposit];
const receiveAddress = !_.isUndefined(receiveAssetToken.address)
@@ -83,6 +83,6 @@ const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
dispatcher: new Dispatcher(dispatch),
});
-export const Portal: React.ComponentClass<PortalComponentAllProps> = connect(mapStateToProps, mapDispatchToProps)(
+export const Portal: React.ComponentClass<PortalComponentProps> = connect(mapStateToProps, mapDispatchToProps)(
PortalComponent,
);
diff --git a/packages/website/ts/containers/deployer_documentation.ts b/packages/website/ts/containers/sol_compiler_documentation.ts
index e20cc195b..2f6486146 100644
--- a/packages/website/ts/containers/deployer_documentation.ts
+++ b/packages/website/ts/containers/sol_compiler_documentation.ts
@@ -12,9 +12,9 @@ import { constants } from 'ts/utils/constants';
import { Translate } from 'ts/utils/translate';
/* tslint:disable:no-var-requires */
-const IntroMarkdown = require('md/docs/deployer/introduction');
-const InstallationMarkdown = require('md/docs/deployer/installation');
-const UsageMarkdown = require('md/docs/deployer/usage');
+const IntroMarkdown = require('md/docs/sol-compiler/introduction');
+const InstallationMarkdown = require('md/docs/sol-compiler/installation');
+const UsageMarkdown = require('md/docs/sol-compiler/usage');
/* tslint:enable:no-var-requires */
const docSections = {
@@ -22,21 +22,19 @@ const docSections = {
installation: 'installation',
usage: 'usage',
compiler: 'compiler',
- deployer: 'deployer',
types: docConstants.TYPES_SECTION_NAME,
};
const docsInfoConfig: DocsInfoConfig = {
- id: DocPackages.Deployer,
+ id: DocPackages.SolCompiler,
type: SupportedDocJson.TypeDoc,
- displayName: 'Deployer',
+ displayName: 'Solidity Compiler',
packageUrl: 'https://github.com/0xProject/0x-monorepo',
menu: {
introduction: [docSections.introduction],
install: [docSections.installation],
usage: [docSections.usage],
compiler: [docSections.compiler],
- deployer: [docSections.deployer],
types: [docSections.types],
},
sectionNameToMarkdown: {
@@ -45,32 +43,18 @@ const docsInfoConfig: DocsInfoConfig = {
[docSections.usage]: UsageMarkdown,
},
sectionNameToModulePath: {
- [docSections.compiler]: ['"deployer/src/compiler"'],
- [docSections.deployer]: ['"deployer/src/deployer"'],
- [docSections.types]: ['"deployer/src/utils/types"', '"types/src/index"'],
+ [docSections.compiler]: ['"sol-compiler/src/compiler"'],
+ [docSections.types]: ['"sol-compiler/src/utils/types"', '"types/src/index"'],
},
menuSubsectionToVersionWhenIntroduced: {},
sections: docSections,
- visibleConstructors: [docSections.compiler, docSections.deployer],
+ visibleConstructors: [docSections.compiler],
typeConfigs: {
// Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
// currently no way to extract the re-exported types from index.ts via TypeDoc :(
- publicTypes: [
- 'CompilerOptions',
- 'DeployerOptions',
- 'BaseDeployerOptions',
- 'UrlDeployerOptions',
- 'ProviderDeployerOptions',
- 'TxData',
- ],
- typeNameToExternalLink: {
- Web3: constants.URL_WEB3_DOCS,
- BigNumber: constants.URL_BIGNUMBERJS_GITHUB,
- ContractInstance: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L98',
- },
- typeNameToPrefix: {
- ContractInstance: 'Web3',
- },
+ publicTypes: ['CompilerOptions'],
+ typeNameToExternalLink: {},
+ typeNameToPrefix: {},
},
};
const docsInfo = new DocsInfo(docsInfoConfig);
diff --git a/packages/website/ts/containers/subproviders_documentation.ts b/packages/website/ts/containers/subproviders_documentation.ts
index a14d06a3f..2178baea8 100644
--- a/packages/website/ts/containers/subproviders_documentation.ts
+++ b/packages/website/ts/containers/subproviders_documentation.ts
@@ -74,7 +74,7 @@ const docsInfoConfig: DocsInfoConfig = {
[docSections.redundantRPCSubprovider]: ['"subproviders/src/subproviders/redundant_rpc"'],
[docSections.ganacheSubprovider]: ['"subproviders/src/subproviders/ganache"'],
[docSections.nonceTrackerSubprovider]: ['"subproviders/src/subproviders/nonce_tracker"'],
- [docSections.types]: ['"deployer/src/utils/types"', '"types/src/index"', '"subproviders/src/types"'],
+ [docSections.types]: ['"sol-compiler/src/utils/types"', '"types/src/index"', '"subproviders/src/types"'],
},
menuSubsectionToVersionWhenIntroduced: {},
sections: docSections,
diff --git a/packages/website/ts/containers/zero_ex_js_documentation.ts b/packages/website/ts/containers/zero_ex_js_documentation.ts
index 40853cb9e..f68e2335f 100644
--- a/packages/website/ts/containers/zero_ex_js_documentation.ts
+++ b/packages/website/ts/containers/zero_ex_js_documentation.ts
@@ -68,32 +68,38 @@ const docsInfoConfig: DocsInfoConfig = {
[zeroExJsDocSections.exchange]: [
'"0x.js/src/contract_wrappers/exchange_wrapper"',
'"src/contract_wrappers/exchange_wrapper"',
+ '"contract-wrappers/src/contract_wrappers/exchange_wrapper"',
],
[zeroExJsDocSections.tokenRegistry]: [
'"0x.js/src/contract_wrappers/token_registry_wrapper"',
'"src/contract_wrappers/token_registry_wrapper"',
+ '"contract-wrappers/src/contract_wrappers/token_registry_wrapper"',
],
[zeroExJsDocSections.token]: [
'"0x.js/src/contract_wrappers/token_wrapper"',
'"src/contract_wrappers/token_wrapper"',
+ '"contract-wrappers/src/contract_wrappers/token_wrapper"',
],
[zeroExJsDocSections.etherToken]: [
'"0x.js/src/contract_wrappers/ether_token_wrapper"',
'"src/contract_wrappers/ether_token_wrapper"',
+ '"contract-wrappers/src/contract_wrappers/ether_token_wrapper"',
],
[zeroExJsDocSections.proxy]: [
'"0x.js/src/contract_wrappers/proxy_wrapper"',
'"0x.js/src/contract_wrappers/token_transfer_proxy_wrapper"',
- '"src/contract_wrappers/token_transfer_proxy_wrapper"',
+ '"contract-wrappers/src/contract_wrappers/token_transfer_proxy_wrapper"',
],
[zeroExJsDocSections.orderWatcher]: [
'"0x.js/src/order_watcher/order_state_watcher"',
'"src/order_watcher/order_state_watcher"',
+ '"order-watcher/src/order_watcher/order_watcher"',
],
[zeroExJsDocSections.types]: [
'"0x.js/src/types"',
'"src/types"',
'"types/src/index"',
+ '"contract-wrappers/src/types"',
'"0x.js/src/contract_wrappers/generated/ether_token"',
'"0x.js/src/contract_wrappers/generated/token"',
'"0x.js/src/contract_wrappers/generated/exchange"',
@@ -114,7 +120,7 @@ const docsInfoConfig: DocsInfoConfig = {
'Order',
'SignedOrder',
'ECSignature',
- 'ZeroExError',
+ 'ContractWrappersError',
'EventCallback',
'EventCallbackAsync',
'EventCallbackSync',
diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx
index 6b347145f..f255f81e7 100644
--- a/packages/website/ts/index.tsx
+++ b/packages/website/ts/index.tsx
@@ -34,9 +34,14 @@ import 'less/all.less';
// cause we only want to import the module when the user navigates to the page.
// At the same time webpack statically parses for System.import() to determine bundle chunk split points
// so each lazy import needs it's own `System.import()` declaration.
-const LazyPortal = createLazyComponent('Portal', async () =>
- System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'),
-);
+const LazyPortal =
+ utils.isDevelopment() || utils.isStaging() || utils.isDogfood()
+ ? createLazyComponent('Portal', async () =>
+ System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'),
+ )
+ : createLazyComponent('LegacyPortal', async () =>
+ System.import<any>(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'),
+ );
const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () =>
System.import<any>(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'),
);
@@ -49,8 +54,8 @@ const LazyConnectDocumentation = createLazyComponent('Documentation', async () =
const LazyWeb3WrapperDocumentation = createLazyComponent('Documentation', async () =>
System.import<any>(/* webpackChunkName: "web3WrapperDocs" */ 'ts/containers/web3_wrapper_documentation'),
);
-const LazyDeployerDocumentation = createLazyComponent('Documentation', async () =>
- System.import<any>(/* webpackChunkName: "deployerDocs" */ 'ts/containers/deployer_documentation'),
+const LazySolCompilerDocumentation = createLazyComponent('Documentation', async () =>
+ System.import<any>(/* webpackChunkName: "solCompilerDocs" */ 'ts/containers/sol_compiler_documentation'),
);
const LazyJSONSchemasDocumentation = createLazyComponent('Documentation', async () =>
System.import<any>(/* webpackChunkName: "jsonSchemasDocs" */ 'ts/containers/json_schemas_documentation'),
@@ -61,6 +66,9 @@ const LazySolCovDocumentation = createLazyComponent('Documentation', async () =>
const LazySubprovidersDocumentation = createLazyComponent('Documentation', async () =>
System.import<any>(/* webpackChunkName: "subproviderDocs" */ 'ts/containers/subproviders_documentation'),
);
+const LazyOrderUtilsDocumentation = createLazyComponent('Documentation', async () =>
+ System.import<any>(/* webpackChunkName: "orderUtilsDocs" */ 'ts/containers/order_utils_documentation'),
+);
analytics.init();
// tslint:disable-next-line:no-floating-promises
@@ -83,7 +91,10 @@ render(
<Route path={WebsitePaths.Wiki} component={Wiki as any} />
<Route path={`${WebsitePaths.ZeroExJs}/:version?`} component={LazyZeroExJSDocumentation} />
<Route path={`${WebsitePaths.Connect}/:version?`} component={LazyConnectDocumentation} />
- <Route path={`${WebsitePaths.Deployer}/:version?`} component={LazyDeployerDocumentation} />
+ <Route
+ path={`${WebsitePaths.SolCompiler}/:version?`}
+ component={LazySolCompilerDocumentation}
+ />
<Route path={`${WebsitePaths.SolCov}/:version?`} component={LazySolCovDocumentation} />
<Route
path={`${WebsitePaths.JSONSchemas}/:version?`}
@@ -94,6 +105,10 @@ render(
component={LazySubprovidersDocumentation}
/>
<Route
+ path={`${WebsitePaths.OrderUtils}/:version?`}
+ component={LazyOrderUtilsDocumentation}
+ />
+ <Route
path={`${WebsitePaths.Web3Wrapper}/:version?`}
component={LazyWeb3WrapperDocumentation}
/>
@@ -111,6 +126,10 @@ render(
path={`${WebsiteLegacyPaths.Web3Wrapper}/:version?`}
component={LazyWeb3WrapperDocumentation}
/>
+ <Route
+ path={`${WebsiteLegacyPaths.Deployer}/:version?`}
+ component={LazySolCompilerDocumentation}
+ />
<Route component={NotFound as any} />
</Switch>
diff --git a/packages/website/ts/lazy_component.tsx b/packages/website/ts/lazy_component.tsx
index 48800c2dd..dce06ed8d 100644
--- a/packages/website/ts/lazy_component.tsx
+++ b/packages/website/ts/lazy_component.tsx
@@ -21,22 +21,22 @@ export class LazyComponent extends React.Component<LazyComponentProps, LazyCompo
component: undefined,
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
// tslint:disable-next-line:no-floating-promises
this._loadComponentFireAndForgetAsync(this.props);
}
- public componentWillReceiveProps(nextProps: LazyComponentProps) {
+ public componentWillReceiveProps(nextProps: LazyComponentProps): void {
if (nextProps.reactComponentPromise !== this.props.reactComponentPromise) {
// tslint:disable-next-line:no-floating-promises
this._loadComponentFireAndForgetAsync(nextProps);
}
}
- public render() {
+ public render(): React.ReactNode {
return _.isUndefined(this.state.component)
? null
: React.createElement(this.state.component, this.props.reactComponentProps);
}
- private async _loadComponentFireAndForgetAsync(props: LazyComponentProps) {
+ private async _loadComponentFireAndForgetAsync(props: LazyComponentProps): Promise<void> {
const component = await props.reactComponentPromise;
this.setState({
component,
diff --git a/packages/website/ts/local_storage/local_storage.ts b/packages/website/ts/local_storage/local_storage.ts
index d94e6877b..20a533a91 100644
--- a/packages/website/ts/local_storage/local_storage.ts
+++ b/packages/website/ts/local_storage/local_storage.ts
@@ -1,7 +1,7 @@
import * as _ from 'lodash';
export const localStorage = {
- doesExist() {
+ doesExist(): boolean {
return !!window.localStorage;
},
getItemIfExists(key: string): string {
@@ -14,13 +14,13 @@ export const localStorage = {
}
return item;
},
- setItem(key: string, value: string) {
+ setItem(key: string, value: string): void {
if (!this.doesExist || _.isUndefined(value)) {
return;
}
window.localStorage.setItem(key, value);
},
- removeItem(key: string) {
+ removeItem(key: string): void {
if (!this.doesExist) {
return;
}
diff --git a/packages/website/ts/local_storage/trade_history_storage.tsx b/packages/website/ts/local_storage/trade_history_storage.tsx
index df731236e..cc764d98e 100644
--- a/packages/website/ts/local_storage/trade_history_storage.tsx
+++ b/packages/website/ts/local_storage/trade_history_storage.tsx
@@ -14,7 +14,7 @@ export const tradeHistoryStorage = {
// Clear all fill related localStorage if we've updated the config variable in an update
// that introduced a backward incompatible change requiring the user to re-fetch the fills from
// the blockchain
- clearIfRequired() {
+ clearIfRequired(): void {
const lastClearFillDate = localStorage.getItemIfExists(FILL_CLEAR_KEY);
if (lastClearFillDate !== configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE) {
const localStorageKeys = localStorage.getAllKeys();
@@ -26,7 +26,7 @@ export const tradeHistoryStorage = {
}
localStorage.setItem(FILL_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE);
},
- addFillToUser(userAddress: string, networkId: number, fill: Fill) {
+ addFillToUser(userAddress: string, networkId: number, fill: Fill): void {
const fillsByHash = this.getUserFillsByHash(userAddress, networkId);
const fillHash = this._getFillHash(fill);
const doesFillExist = !_.isUndefined(fillsByHash[fillHash]);
@@ -38,7 +38,7 @@ export const tradeHistoryStorage = {
const userFillsKey = this._getUserFillsKey(userAddress, networkId);
localStorage.setItem(userFillsKey, userFillsJSONString);
},
- removeFillFromUser(userAddress: string, networkId: number, fill: Fill) {
+ removeFillFromUser(userAddress: string, networkId: number, fill: Fill): void {
const fillsByHash = this.getUserFillsByHash(userAddress, networkId);
const fillHash = this._getFillHash(fill);
const doesFillExist = !_.isUndefined(fillsByHash[fillHash]);
@@ -74,15 +74,15 @@ export const tradeHistoryStorage = {
const blockNumber = _.parseInt(blockNumberStr);
return blockNumber;
},
- setFillsLatestBlock(userAddress: string, networkId: number, blockNumber: number) {
+ setFillsLatestBlock(userAddress: string, networkId: number, blockNumber: number): void {
const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId);
localStorage.setItem(userFillsLatestBlockKey, `${blockNumber}`);
},
- _getUserFillsKey(userAddress: string, networkId: number) {
+ _getUserFillsKey(userAddress: string, networkId: number): string {
const userFillsKey = `${FILLS_KEY}-${userAddress}-${networkId}`;
return userFillsKey;
},
- _getFillsLatestBlockKey(userAddress: string, networkId: number) {
+ _getFillsLatestBlockKey(userAddress: string, networkId: number): string {
const userFillsLatestBlockKey = `${FILLS_LATEST_BLOCK}-${userAddress}-${networkId}`;
return userFillsLatestBlockKey;
},
diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx
index 97be59526..24ea7614f 100644
--- a/packages/website/ts/pages/about/about.tsx
+++ b/packages/website/ts/pages/about/about.tsx
@@ -37,7 +37,7 @@ const teamRow1: ProfileInfo[] = [
name: 'Fabio Berger',
title: 'Senior Engineer',
description: `Full-stack blockchain engineer. Previously software engineer \
- at Airtable and founder of WealthLift. Computer science at Duke.`,
+ at Airtable and founder of WealthLift. Computer Science at Duke.`,
image: '/images/team/fabio.jpg',
linkedIn: 'https://www.linkedin.com/in/fabio-berger-03ab261a/',
github: 'https://github.com/fabioberger',
@@ -60,7 +60,7 @@ const teamRow2: ProfileInfo[] = [
name: 'Leonid Logvinov',
title: 'Engineer',
description: `Full-stack blockchain engineer. Previously blockchain engineer \
- at Neufund. Computer science at University of Warsaw.`,
+ at Neufund. Computer Science at University of Warsaw.`,
image: '/images/team/leonid.png',
linkedIn: 'https://www.linkedin.com/in/leonidlogvinov/',
github: 'https://github.com/LogvinovLeon',
@@ -113,7 +113,7 @@ const teamRow4: ProfileInfo[] = [
{
name: 'Blake Henderson',
title: 'Operations Associate',
- description: `Operations and Analytics. Previously analytics at LinkedIn. Economics at UC San Diego. `,
+ description: `Operations and Analytics. Previously analytics at LinkedIn. Economics at UC San Diego.`,
image: '/images/team/blake.jpg',
linkedIn: 'https://www.linkedin.com/in/blakerhenderson/',
github: '',
@@ -128,6 +128,35 @@ const teamRow4: ProfileInfo[] = [
github: '',
medium: '',
},
+ {
+ name: 'Greg Hysen',
+ title: 'Blockchain Engineer',
+ description: `Smart contract R&D. Previously lead distributed systems engineer at Hivemapper. Computer Science at University of Waterloo.`,
+ image: '/images/team/greg.jpeg',
+ linkedIn: 'https://www.linkedin.com/in/gregory-hysen-71741463/',
+ github: 'https://github.com/hysz',
+ medium: '',
+ },
+];
+
+const teamRow5: ProfileInfo[] = [
+ {
+ name: 'Remco Bloemen',
+ title: 'Technical Fellow',
+ description: `Previously cofounder at Neufund and Coblue. Part III at Cambridge. PhD dropout at Twente Business School.`,
+ image: '/images/team/remco.jpeg',
+ linkedIn: 'https://www.linkedin.com/in/remcobloemen/',
+ github: 'http://github.com/recmo',
+ medium: '',
+ },
+ {
+ name: 'Francesco Agosti',
+ title: 'Engineer',
+ description: `Full-stack engineer. Previously senior software engineer at Yelp. Computer Science at Duke.`,
+ image: 'images/team/fragosti.png',
+ linkedIn: 'https://www.linkedin.com/in/fragosti/',
+ github: 'http://github.com/fragosti',
+ },
];
const advisors: ProfileInfo[] = [
@@ -189,10 +218,10 @@ const styles: Styles = {
};
export class About extends React.Component<AboutProps, AboutState> {
- public componentDidMount() {
+ public componentDidMount(): void {
window.scrollTo(0, 0);
}
- public render() {
+ public render(): React.ReactNode {
return (
<div style={{ backgroundColor: colors.lightestGrey }}>
<DocumentTitle title="0x About Us" />
@@ -223,6 +252,7 @@ export class About extends React.Component<AboutProps, AboutState> {
<div className="clearfix">{this._renderProfiles(teamRow2)}</div>
<div className="clearfix">{this._renderProfiles(teamRow3)}</div>
<div className="clearfix">{this._renderProfiles(teamRow4)}</div>
+ <div className="clearfix">{this._renderProfiles(teamRow5)}</div>
</div>
<div className="pt3 pb2">
<div
@@ -262,7 +292,7 @@ export class About extends React.Component<AboutProps, AboutState> {
</div>
);
}
- private _renderProfiles(profiles: ProfileInfo[]) {
+ private _renderProfiles(profiles: ProfileInfo[]): React.ReactNode {
const numIndiv = profiles.length;
const colSize = utils.getColSize(numIndiv);
return _.map(profiles, profile => {
diff --git a/packages/website/ts/pages/about/profile.tsx b/packages/website/ts/pages/about/profile.tsx
index 4361da103..dd046a8cb 100644
--- a/packages/website/ts/pages/about/profile.tsx
+++ b/packages/website/ts/pages/about/profile.tsx
@@ -22,7 +22,7 @@ interface ProfileProps {
profileInfo: ProfileInfo;
}
-export function Profile(props: ProfileProps) {
+export const Profile = (props: ProfileProps) => {
return (
<div className={`lg-col md-col lg-col-${props.colSize} md-col-6`}>
<div style={{ maxWidth: 300 }} className="mx-auto lg-px3 md-px3 sm-px4 sm-pb3">
@@ -53,9 +53,9 @@ export function Profile(props: ProfileProps) {
</div>
</div>
);
-}
+};
-function renderSocialMediaIcons(profileInfo: ProfileInfo) {
+function renderSocialMediaIcons(profileInfo: ProfileInfo): React.ReactNode {
const icons = [
renderSocialMediaIcon('zmdi-github-box', profileInfo.github),
renderSocialMediaIcon('zmdi-linkedin-box', profileInfo.linkedIn),
@@ -64,7 +64,7 @@ function renderSocialMediaIcons(profileInfo: ProfileInfo) {
return icons;
}
-function renderSocialMediaIcon(iconName: string, url: string) {
+function renderSocialMediaIcon(iconName: string, url: string): React.ReactNode {
if (_.isEmpty(url)) {
return null;
}
diff --git a/packages/website/ts/pages/documentation/doc_page.tsx b/packages/website/ts/pages/documentation/doc_page.tsx
index aca20447b..17efc56ed 100644
--- a/packages/website/ts/pages/documentation/doc_page.tsx
+++ b/packages/website/ts/pages/documentation/doc_page.tsx
@@ -30,10 +30,11 @@ const docIdToSubpackageName: { [id: string]: string } = {
[DocPackages.Connect]: 'connect',
[DocPackages.SmartContracts]: 'contracts',
[DocPackages.Web3Wrapper]: 'web3-wrapper',
- [DocPackages.Deployer]: 'deployer',
+ [DocPackages.SolCompiler]: 'sol-compiler',
[DocPackages.JSONSchemas]: 'json-schemas',
[DocPackages.SolCov]: 'sol-cov',
[DocPackages.Subproviders]: 'subproviders',
+ [DocPackages.OrderUtils]: 'order-utils',
};
export interface DocPageProps {
@@ -58,7 +59,7 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> {
docAgnosticFormat: undefined,
};
}
- public componentWillMount() {
+ public componentWillMount(): void {
const pathName = this.props.location.pathname;
const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1);
const versions = findVersions(lastSegment);
@@ -66,10 +67,10 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> {
// tslint:disable-next-line:no-floating-promises
this._fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists);
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
}
- public render() {
+ public render(): React.ReactNode {
const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat)
? {}
: this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat);
@@ -134,7 +135,7 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> {
});
}
}
- private _getSourceUrl() {
+ private _getSourceUrl(): string {
const url = this.props.docsInfo.packageUrl;
let pkg = docIdToSubpackageName[this.props.docsInfo.id];
let tagPrefix = pkg;
@@ -154,7 +155,7 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> {
const sourceUrl = `${url}/blob/${tagPrefix}%40${this.props.docsVersion}/packages${pkg}`;
return sourceUrl;
}
- private _onVersionSelected(semver: string) {
+ private _onVersionSelected(semver: string): void {
let path = window.location.pathname;
const lastChar = path[path.length - 1];
if (_.isFinite(_.parseInt(lastChar))) {
diff --git a/packages/website/ts/pages/faq/faq.tsx b/packages/website/ts/pages/faq/faq.tsx
index 701031d44..c9295d806 100644
--- a/packages/website/ts/pages/faq/faq.tsx
+++ b/packages/website/ts/pages/faq/faq.tsx
@@ -404,10 +404,10 @@ const sections: FAQSection[] = [
];
export class FAQ extends React.Component<FAQProps, FAQState> {
- public componentDidMount() {
+ public componentDidMount(): void {
window.scrollTo(0, 0);
}
- public render() {
+ public render(): React.ReactNode {
return (
<div>
<DocumentTitle title="0x FAQ" />
@@ -422,7 +422,7 @@ export class FAQ extends React.Component<FAQProps, FAQState> {
</div>
);
}
- private _renderSections() {
+ private _renderSections(): React.ReactNode {
const renderedSections = _.map(sections, (section: FAQSection, i: number) => {
const isFirstSection = i === 0;
return (
@@ -434,7 +434,7 @@ export class FAQ extends React.Component<FAQProps, FAQState> {
});
return renderedSections;
}
- private _renderQuestions(questions: FAQQuestion[], isFirstSection: boolean) {
+ private _renderQuestions(questions: FAQQuestion[], isFirstSection: boolean): React.ReactNode {
const renderedQuestions = _.map(questions, (question: FAQQuestion, i: number) => {
const isFirstQuestion = i === 0;
return (
diff --git a/packages/website/ts/pages/faq/question.tsx b/packages/website/ts/pages/faq/question.tsx
index 240dae910..28ea6881a 100644
--- a/packages/website/ts/pages/faq/question.tsx
+++ b/packages/website/ts/pages/faq/question.tsx
@@ -20,7 +20,7 @@ export class Question extends React.Component<QuestionProps, QuestionState> {
isExpanded: props.shouldDisplayExpanded,
};
}
- public render() {
+ public render(): React.ReactNode {
return (
<div className="py1">
<Card
@@ -43,7 +43,7 @@ export class Question extends React.Component<QuestionProps, QuestionState> {
</div>
);
}
- private _onExchangeChange() {
+ private _onExchangeChange(): void {
this.setState({
isExpanded: !this.state.isExpanded,
});
diff --git a/packages/website/ts/pages/fullscreen_message.tsx b/packages/website/ts/pages/fullscreen_message.tsx
new file mode 100644
index 000000000..6fcf7b32c
--- /dev/null
+++ b/packages/website/ts/pages/fullscreen_message.tsx
@@ -0,0 +1,30 @@
+import { Styles } from '@0xproject/react-shared';
+import * as React from 'react';
+
+export interface FullscreenMessageProps {
+ headerText: string;
+ bodyText: string;
+}
+
+const styles: Styles = {
+ thin: {
+ fontWeight: 100,
+ },
+};
+
+export const FullscreenMessage = (props: FullscreenMessageProps) => {
+ return (
+ <div className="mx-auto max-width-4 py4">
+ <div className="center py4">
+ <div className="py4">
+ <div className="py4">
+ <h1 style={styles.thin}>{props.headerText}</h1>
+ <div className="py1">
+ <div className="py3">{props.bodyText}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+};
diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx
index f961220fd..02ecfa117 100644
--- a/packages/website/ts/pages/landing/landing.tsx
+++ b/packages/website/ts/pages/landing/landing.tsx
@@ -37,6 +37,8 @@ interface Project {
}
const THROTTLE_TIMEOUT = 100;
+const WHATS_NEW_TITLE = '18 ideas for 0x relayers in 2018';
+const WHATS_NEW_URL = 'https://blog.0xproject.com/18-ideas-for-0x-relayers-in-2018-80a1498b955f';
const relayersAndDappProjects: Project[] = [
{
@@ -175,14 +177,14 @@ export class Landing extends React.Component<LandingProps, LandingState> {
};
this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
}
- public componentDidMount() {
+ public componentDidMount(): void {
window.addEventListener('resize', this._throttledScreenWidthUpdate);
window.scrollTo(0, 0);
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
window.removeEventListener('resize', this._throttledScreenWidthUpdate);
}
- public render() {
+ public render(): React.ReactNode {
return (
<div id="landing" className="clearfix" style={{ color: colors.grey500 }}>
<DocumentTitle title="0x Protocol" />
@@ -216,7 +218,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderHero() {
+ private _renderHero(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const buttonLabelStyle: React.CSSProperties = {
textTransform: 'none',
@@ -233,6 +235,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
return (
<div className="clearfix py4" style={{ backgroundColor: colors.heroGrey }}>
<div className="mx-auto max-width-4 clearfix">
+ {this._renderWhatsNew()}
<div className="lg-pt4 md-pt4 sm-pt2 lg-pb4 md-pb4 lg-my4 md-my4 sm-mt2 sm-mb4 clearfix">
<div className="col lg-col-5 md-col-5 col-12 sm-center">
<img src="/images/landing/hero_chip_image.png" height={isSmallScreen ? 300 : 395} />
@@ -302,7 +305,34 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderProjects(projects: Project[], title: string, backgroundColor: string, isTitleCenter: boolean) {
+ private _renderWhatsNew(): React.ReactNode {
+ return (
+ <div className="sm-center sm-px1">
+ <a href={WHATS_NEW_URL} target="_blank" className="inline-block text-decoration-none">
+ <div className="flex sm-pl0 md-pl2 lg-pl0" style={{ fontFamily: 'Roboto Mono', fontWeight: 600 }}>
+ <div
+ className="mr1 px1"
+ style={{
+ backgroundColor: colors.lightTurquois,
+ borderRadius: 3,
+ color: colors.heroGrey,
+ height: 23,
+ }}
+ >
+ New
+ </div>
+ <div style={{ color: colors.darkGrey }}>{WHATS_NEW_TITLE}</div>
+ </div>
+ </a>
+ </div>
+ );
+ }
+ private _renderProjects(
+ projects: Project[],
+ title: string,
+ backgroundColor: string,
+ isTitleCenter: boolean,
+ ): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const projectList = _.map(projects, (project: Project, i: number) => {
const isRelayersOnly = projects.length === 12;
@@ -368,7 +398,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderTokenizationSection() {
+ private _renderTokenizationSection(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
return (
<div className="clearfix lg-py4 md-py4 sm-pb4 sm-pt2" style={{ backgroundColor: colors.grey100 }}>
@@ -399,7 +429,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderProtocolSection() {
+ private _renderProtocolSection(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
return (
<div className="clearfix pt4" style={{ backgroundColor: colors.heroGrey }}>
@@ -444,7 +474,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderBuildingBlocksSection() {
+ private _renderBuildingBlocksSection(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const descriptionStyle: React.CSSProperties = {
fontFamily: 'Roboto Mono',
@@ -501,7 +531,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderBlockChipImage() {
+ private _renderBlockChipImage(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
return (
<div className="col lg-col-6 md-col-6 col-12 sm-center">
@@ -509,7 +539,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderTokenCloud() {
+ private _renderTokenCloud(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
return (
<div className="col lg-col-6 md-col-6 col-12 center">
@@ -517,7 +547,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderAssetTypes() {
+ private _renderAssetTypes(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const assetTypes: AssetType[] = [
{
@@ -560,7 +590,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
});
return assets;
}
- private _renderInfoBoxes() {
+ private _renderInfoBoxes(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const boxStyle: React.CSSProperties = {
maxWidth: 253,
@@ -623,7 +653,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderUseCases() {
+ private _renderUseCases(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const useCases: UseCase[] = [
@@ -721,7 +751,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _renderCallToAction() {
+ private _renderCallToAction(): React.ReactNode {
const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm;
const buttonLabelStyle: React.CSSProperties = {
textTransform: 'none',
@@ -768,7 +798,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
);
}
- private _updateScreenWidth() {
+ private _updateScreenWidth(): void {
const newScreenWidth = utils.getScreenWidth();
if (newScreenWidth !== this.state.screenWidth) {
this.setState({
@@ -776,7 +806,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
});
}
}
- private _onLanguageSelected(language: Language) {
+ private _onLanguageSelected(language: Language): void {
this.props.dispatcher.updateSelectedLanguage(language);
}
} // tslint:disable:max-file-line-count
diff --git a/packages/website/ts/pages/not_found.tsx b/packages/website/ts/pages/not_found.tsx
index ff25a35e9..674271636 100644
--- a/packages/website/ts/pages/not_found.tsx
+++ b/packages/website/ts/pages/not_found.tsx
@@ -3,6 +3,7 @@ import * as _ from 'lodash';
import * as React from 'react';
import { Footer } from 'ts/components/footer';
import { TopBar } from 'ts/components/top_bar/top_bar';
+import { FullscreenMessage } from 'ts/pages/fullscreen_message';
import { Dispatcher } from 'ts/redux/dispatcher';
import { Translate } from 'ts/utils/translate';
@@ -12,35 +13,15 @@ export interface NotFoundProps {
dispatcher: Dispatcher;
}
-interface NotFoundState {}
-
-const styles: Styles = {
- thin: {
- fontWeight: 100,
- },
+export const NotFound = (props: NotFoundProps) => {
+ return (
+ <div>
+ <TopBar blockchainIsLoaded={false} location={this.props.location} translate={this.props.translate} />
+ <FullscreenMessage
+ headerText={'404 Not Found'}
+ bodyText={"Hm... looks like we couldn't find what you are looking for."}
+ />
+ <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
+ </div>
+ );
};
-
-export class NotFound extends React.Component<NotFoundProps, NotFoundState> {
- public render() {
- return (
- <div>
- <TopBar blockchainIsLoaded={false} location={this.props.location} translate={this.props.translate} />
- <div className="mx-auto max-width-4 py4">
- <div className="center py4">
- <div className="py4">
- <div className="py4">
- <h1 style={{ ...styles.thin }}>404 Not Found</h1>
- <div className="py1">
- <div className="py3">
- Hm... looks like we couldn't find what you are looking for.
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
- </div>
- );
- }
-}
diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx
index 7ed2b750d..720c1cc37 100644
--- a/packages/website/ts/pages/wiki/wiki.tsx
+++ b/packages/website/ts/pages/wiki/wiki.tsx
@@ -47,7 +47,7 @@ const styles: Styles = {
left: 0,
bottom: 0,
right: 0,
- overflowZ: 'hidden',
+ overflow: 'hidden',
height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`,
WebkitOverflowScrolling: 'touch',
},
@@ -69,19 +69,19 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
isHoveringSidebar: false,
};
}
- public componentDidMount() {
+ public componentDidMount(): void {
window.addEventListener('hashchange', this._onHashChanged.bind(this), false);
}
- public componentWillMount() {
+ public componentWillMount(): void {
// tslint:disable-next-line:no-floating-promises
this._fetchArticlesBySectionAsync();
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
this._isUnmounted = true;
clearTimeout(this._wikiBackoffTimeoutId);
window.removeEventListener('hashchange', this._onHashChanged.bind(this), false);
}
- public render() {
+ public render(): React.ReactNode {
const menuSubsectionsBySection = _.isUndefined(this.state.articlesBySection)
? {}
: this._getMenuSubsectionsBySection(this.state.articlesBySection);
@@ -171,7 +171,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
const sections = _.map(sectionNames, sectionName => this._renderSection(sectionName));
return sections;
}
- private _renderSection(sectionName: string) {
+ private _renderSection(sectionName: string): React.ReactNode {
const articles = this.state.articlesBySection[sectionName];
const renderedArticles = _.map(articles, (article: Article) => {
const githubLink = `${constants.URL_GITHUB_WIKI}/edit/master/${sectionName}/${article.fileName}`;
@@ -227,7 +227,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
}
}
}
- private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection) {
+ private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection): { [section: string]: string[] } {
const sectionNames = _.keys(articlesBySection);
const menuSubsectionsBySection: { [section: string]: string[] } = {};
for (const sectionName of sectionNames) {
@@ -237,17 +237,17 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
}
return menuSubsectionsBySection;
}
- private _onSidebarHover(event: React.FormEvent<HTMLInputElement>) {
+ private _onSidebarHover(event: React.FormEvent<HTMLInputElement>): void {
this.setState({
isHoveringSidebar: true,
});
}
- private _onSidebarHoverOff() {
+ private _onSidebarHoverOff(): void {
this.setState({
isHoveringSidebar: false,
});
}
- private _onHashChanged(event: any) {
+ private _onHashChanged(event: any): void {
const hash = window.location.hash.slice(1);
sharedUtils.scrollToHash(hash, sharedConstants.SCROLL_CONTAINER_ID);
}
diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts
index 13e9a10cc..981522360 100644
--- a/packages/website/ts/redux/dispatcher.ts
+++ b/packages/website/ts/redux/dispatcher.ts
@@ -22,47 +22,47 @@ export class Dispatcher {
this._dispatch = dispatch;
}
// Portal
- public resetState() {
+ public resetState(): void {
this._dispatch({
type: ActionTypes.ResetState,
});
}
- public updateNodeVersion(nodeVersion: string) {
+ public updateNodeVersion(nodeVersion: string): void {
this._dispatch({
data: nodeVersion,
type: ActionTypes.UpdateNodeVersion,
});
}
- public updateScreenWidth(screenWidth: ScreenWidths) {
+ public updateScreenWidth(screenWidth: ScreenWidths): void {
this._dispatch({
data: screenWidth,
type: ActionTypes.UpdateScreenWidth,
});
}
- public swapAssetTokenSymbols() {
+ public swapAssetTokenSymbols(): void {
this._dispatch({
type: ActionTypes.SwapAssetTokens,
});
}
- public updateOrderSalt(salt: BigNumber) {
+ public updateOrderSalt(salt: BigNumber): void {
this._dispatch({
data: salt,
type: ActionTypes.UpdateOrderSalt,
});
}
- public updateUserSuppliedOrderCache(order: Order) {
+ public updateUserSuppliedOrderCache(order: Order): void {
this._dispatch({
data: order,
type: ActionTypes.UpdateUserSuppliedOrderCache,
});
}
- public updateShouldBlockchainErrDialogBeOpen(shouldBeOpen: boolean) {
+ public updateShouldBlockchainErrDialogBeOpen(shouldBeOpen: boolean): void {
this._dispatch({
data: shouldBeOpen,
type: ActionTypes.UpdateShouldBlockchainErrDialogBeOpen,
});
}
- public updateChosenAssetToken(side: Side, token: AssetToken) {
+ public updateChosenAssetToken(side: Side, token: AssetToken): void {
this._dispatch({
data: {
side,
@@ -71,7 +71,7 @@ export class Dispatcher {
type: ActionTypes.UpdateChosenAssetToken,
});
}
- public updateChosenAssetTokenAddress(side: Side, address: string) {
+ public updateChosenAssetTokenAddress(side: Side, address: string): void {
this._dispatch({
data: {
address,
@@ -80,43 +80,43 @@ export class Dispatcher {
type: ActionTypes.UpdateChosenAssetTokenAddress,
});
}
- public updateOrderTakerAddress(address: string) {
+ public updateOrderTakerAddress(address: string): void {
this._dispatch({
data: address,
type: ActionTypes.UpdateOrderTakerAddress,
});
}
- public updateUserAddress(address?: string) {
+ public updateUserAddress(address?: string): void {
this._dispatch({
data: address,
type: ActionTypes.UpdateUserAddress,
});
}
- public updateOrderExpiry(unixTimestampSec: BigNumber) {
+ public updateOrderExpiry(unixTimestampSec: BigNumber): void {
this._dispatch({
data: unixTimestampSec,
type: ActionTypes.UpdateOrderExpiry,
});
}
- public encounteredBlockchainError(err: BlockchainErrs) {
+ public encounteredBlockchainError(err: BlockchainErrs): void {
this._dispatch({
data: err,
type: ActionTypes.BlockchainErrEncountered,
});
}
- public updateBlockchainIsLoaded(isLoaded: boolean) {
+ public updateBlockchainIsLoaded(isLoaded: boolean): void {
this._dispatch({
data: isLoaded,
type: ActionTypes.UpdateBlockchainIsLoaded,
});
}
- public addTokenToTokenByAddress(token: Token) {
+ public addTokenToTokenByAddress(token: Token): void {
this._dispatch({
data: token,
type: ActionTypes.AddTokenToTokenByAddress,
});
}
- public removeTokenToTokenByAddress(token: Token) {
+ public removeTokenToTokenByAddress(token: Token): void {
this._dispatch({
data: token,
type: ActionTypes.RemoveTokenFromTokenByAddress,
@@ -127,7 +127,7 @@ export class Dispatcher {
networkId: number,
userAddressIfExists: string | undefined,
sideToAssetToken: SideToAssetToken,
- ) {
+ ): void {
this._dispatch({
data: {
tokenByAddress,
@@ -138,36 +138,36 @@ export class Dispatcher {
type: ActionTypes.BatchDispatch,
});
}
- public updateTokenByAddress(tokens: Token[]) {
+ public updateTokenByAddress(tokens: Token[]): void {
this._dispatch({
data: tokens,
type: ActionTypes.UpdateTokenByAddress,
});
}
- public forceTokenStateRefetch() {
+ public forceTokenStateRefetch(): void {
this._dispatch({
type: ActionTypes.ForceTokenStateRefetch,
});
}
- public updateECSignature(ecSignature: ECSignature) {
+ public updateECSignature(ecSignature: ECSignature): void {
this._dispatch({
data: ecSignature,
type: ActionTypes.UpdateOrderECSignature,
});
}
- public updateUserWeiBalance(balance: BigNumber) {
+ public updateUserWeiBalance(balance: BigNumber): void {
this._dispatch({
data: balance,
type: ActionTypes.UpdateUserEtherBalance,
});
}
- public updateNetworkId(networkId: number) {
+ public updateNetworkId(networkId: number): void {
this._dispatch({
data: networkId,
type: ActionTypes.UpdateNetworkId,
});
}
- public updateOrderFillAmount(amount: BigNumber) {
+ public updateOrderFillAmount(amount: BigNumber): void {
this._dispatch({
data: amount,
type: ActionTypes.UpdateOrderFillAmount,
@@ -175,13 +175,13 @@ export class Dispatcher {
}
// Docs
- public updateCurrentDocsVersion(version: string) {
+ public updateCurrentDocsVersion(version: string): void {
this._dispatch({
data: version,
type: ActionTypes.UpdateLibraryVersion,
});
}
- public updateAvailableDocVersions(versions: string[]) {
+ public updateAvailableDocVersions(versions: string[]): void {
this._dispatch({
data: versions,
type: ActionTypes.UpdateAvailableLibraryVersions,
@@ -189,30 +189,30 @@ export class Dispatcher {
}
// Shared
- public showFlashMessage(msg: string | React.ReactNode) {
+ public showFlashMessage(msg: string | React.ReactNode): void {
this._dispatch({
data: msg,
type: ActionTypes.ShowFlashMessage,
});
}
- public hideFlashMessage() {
+ public hideFlashMessage(): void {
this._dispatch({
type: ActionTypes.HideFlashMessage,
});
}
- public updateProviderType(providerType: ProviderType) {
+ public updateProviderType(providerType: ProviderType): void {
this._dispatch({
type: ActionTypes.UpdateProviderType,
data: providerType,
});
}
- public updateInjectedProviderName(injectedProviderName: string) {
+ public updateInjectedProviderName(injectedProviderName: string): void {
this._dispatch({
type: ActionTypes.UpdateInjectedProviderName,
data: injectedProviderName,
});
}
- public updateSelectedLanguage(language: Language) {
+ public updateSelectedLanguage(language: Language): void {
this._dispatch({
type: ActionTypes.UpdateSelectedLanguage,
data: language,
diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts
index a628f65c2..bb8f1472b 100644
--- a/packages/website/ts/redux/reducer.ts
+++ b/packages/website/ts/redux/reducer.ts
@@ -91,7 +91,7 @@ const INITIAL_STATE: State = {
translate: new Translate(),
};
-export function reducer(state: State = INITIAL_STATE, action: Action) {
+export function reducer(state: State = INITIAL_STATE, action: Action): State {
switch (action.type) {
// Portal
case ActionTypes.ResetState:
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts
index 2544e6735..33e4d6c30 100644
--- a/packages/website/ts/types.ts
+++ b/packages/website/ts/types.ts
@@ -207,7 +207,6 @@ export interface ContractEvent {
args: any;
}
-export type InputErrMsg = React.ReactNode | string | undefined;
export type ValidatedBigNumberCallback = (isValid: boolean, amount?: BigNumber) => void;
export enum ScreenWidths {
Sm = 'SM',
@@ -344,6 +343,7 @@ export enum Docs {
export enum WebsiteLegacyPaths {
ZeroExJs = '/docs/0xjs',
Web3Wrapper = '/docs/web3_wrapper',
+ Deployer = '/docs/deployer',
}
export enum WebsitePaths {
@@ -357,10 +357,11 @@ export enum WebsitePaths {
SmartContracts = '/docs/contracts',
Connect = '/docs/connect',
Web3Wrapper = '/docs/web3-wrapper',
- Deployer = '/docs/deployer',
+ SolCompiler = '/docs/sol-compiler',
JSONSchemas = '/docs/json-schemas',
SolCov = '/docs/sol-cov',
Subproviders = '/docs/subproviders',
+ OrderUtils = '/docs/order-utils',
Jobs = '/jobs',
}
@@ -369,10 +370,11 @@ export enum DocPackages {
ZeroExJs = 'ZERO_EX_JS',
SmartContracts = 'SMART_CONTRACTS',
Web3Wrapper = 'WEB3_WRAPPER',
- Deployer = 'DEPLOYER',
+ SolCompiler = 'SOL_COMPILER',
JSONSchemas = 'JSON_SCHEMAS',
SolCov = 'SOL_COV',
Subproviders = 'SUBPROVIDERS',
+ OrderUtils = 'ORDER_UTILS',
}
export enum Key {
@@ -421,7 +423,7 @@ export enum Key {
About = 'ABOUT',
Careers = 'CAREERS',
Contact = 'CONTACT',
- Deployer = 'DEPLOYER',
+ SolCompiler = 'SOL_COMPILER',
JsonSchemas = 'JSON_SCHEMAS',
SolCov = 'SOL_COV',
Subproviders = 'SUBPROVIDERS',
@@ -431,6 +433,7 @@ export enum Key {
Whitepaper = 'WHITEPAPER',
Wiki = 'WIKI',
Web3Wrapper = 'WEB3_WRAPPER',
+ OrderUtils = 'ORDER_UTILS',
And = 'AND',
Faq = 'FAQ',
SmartContracts = 'SMART_CONTRACTS',
@@ -500,17 +503,24 @@ export interface TokenState {
price?: BigNumber;
}
-// TODO: Add topTokens and headerUrl properties once available from backend
export interface WebsiteBackendRelayerInfo {
- id: string;
name: string;
- dailyTxnVolume: string;
+ weeklyTxnVolume: string;
url: string;
+ appUrl?: string;
+ headerImgUrl?: string;
+ topTokens: WebsiteBackendTokenInfo[];
}
export interface WebsiteBackendPriceInfo {
- price: string;
+ [symbol: string]: string;
+}
+
+export interface WebsiteBackendTokenInfo {
address: string;
+ decimals: number;
+ name: string;
+ symbol: string;
}
export interface WebsiteBackendGasInfo {
diff --git a/packages/website/ts/utils/analytics.ts b/packages/website/ts/utils/analytics.ts
index 37c47c7b0..928e45bc3 100644
--- a/packages/website/ts/utils/analytics.ts
+++ b/packages/website/ts/utils/analytics.ts
@@ -5,10 +5,10 @@ import { utils } from 'ts/utils/utils';
import * as Web3 from 'web3';
export const analytics = {
- init() {
+ init(): void {
ReactGA.initialize(configs.GOOGLE_ANALYTICS_ID);
},
- logEvent(category: string, action: string, label: string, value?: any) {
+ logEvent(category: string, action: string, label: string, value?: any): void {
ReactGA.event({
category,
action,
@@ -16,7 +16,7 @@ export const analytics = {
value,
});
},
- async logProviderAsync(web3IfExists: Web3) {
+ async logProviderAsync(web3IfExists: Web3): Promise<void> {
await utils.onPageLoadAsync();
const providerType = !_.isUndefined(web3IfExists)
? utils.getProviderType(web3IfExists.currentProvider)
diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts
index fdbb3e03a..c440b1604 100644
--- a/packages/website/ts/utils/backend_client.ts
+++ b/packages/website/ts/utils/backend_client.ts
@@ -1,16 +1,8 @@
-import { BigNumber, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
-import * as queryString from 'query-string';
-import {
- ArticlesBySection,
- ItemByAddress,
- WebsiteBackendGasInfo,
- WebsiteBackendPriceInfo,
- WebsiteBackendRelayerInfo,
-} from 'ts/types';
-import { configs } from 'ts/utils/configs';
-import { errorReporter } from 'ts/utils/error_reporter';
+import { ArticlesBySection, WebsiteBackendGasInfo, WebsiteBackendPriceInfo, WebsiteBackendRelayerInfo } from 'ts/types';
+import { fetchUtils } from 'ts/utils/fetch_utils';
+import { utils } from 'ts/utils/utils';
const ETH_GAS_STATION_ENDPOINT = '/eth_gas_station';
const PRICES_ENDPOINT = '/prices';
@@ -19,52 +11,26 @@ const WIKI_ENDPOINT = '/wiki';
export const backendClient = {
async getGasInfoAsync(): Promise<WebsiteBackendGasInfo> {
- const result = await requestAsync(ETH_GAS_STATION_ENDPOINT);
+ const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), ETH_GAS_STATION_ENDPOINT);
return result;
},
- async getPriceInfosAsync(tokenAddresses: string[]): Promise<WebsiteBackendPriceInfo[]> {
- if (_.isEmpty(tokenAddresses)) {
- return [];
+ async getPriceInfoAsync(tokenSymbols: string[]): Promise<WebsiteBackendPriceInfo> {
+ if (_.isEmpty(tokenSymbols)) {
+ return {};
}
- const joinedTokenAddresses = tokenAddresses.join(',');
+ const joinedTokenSymbols = tokenSymbols.join(',');
const queryParams = {
- tokens: joinedTokenAddresses,
+ tokens: joinedTokenSymbols,
};
- const result = await requestAsync(PRICES_ENDPOINT, queryParams);
+ const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), PRICES_ENDPOINT, queryParams);
return result;
},
async getRelayerInfosAsync(): Promise<WebsiteBackendRelayerInfo[]> {
- const result = await requestAsync(RELAYERS_ENDPOINT);
+ const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), RELAYERS_ENDPOINT);
return result;
},
async getWikiArticlesBySectionAsync(): Promise<ArticlesBySection> {
- const result = await requestAsync(WIKI_ENDPOINT);
+ const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), WIKI_ENDPOINT);
return result;
},
};
-
-async function requestAsync(endpoint: string, queryParams?: object): Promise<any> {
- const query = queryStringFromQueryParams(queryParams);
- const url = `${configs.BACKEND_BASE_URL}${endpoint}${query}`;
- const response = await fetch(url);
- if (response.status !== 200) {
- const errorText = `Error requesting url: ${url}, ${response.status}: ${response.statusText}`;
- logUtils.log(errorText);
- const error = Error(errorText);
- // tslint:disable-next-line:no-floating-promises
- errorReporter.reportAsync(error);
- throw error;
- }
- const result = await response.json();
- return result;
-}
-
-function queryStringFromQueryParams(queryParams?: object): string {
- // if params are undefined or empty, return an empty string
- if (_.isUndefined(queryParams) || _.isEmpty(queryParams)) {
- return '';
- }
- // stringify the formatted object
- const stringifiedParams = queryString.stringify(queryParams);
- return `?${stringifiedParams}`;
-}
diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts
index a54fc56a8..9fec814b7 100644
--- a/packages/website/ts/utils/configs.ts
+++ b/packages/website/ts/utils/configs.ts
@@ -1,5 +1,6 @@
import * as _ from 'lodash';
import { Environments, OutdatedWrappedEtherByNetworkId, PublicNodeUrlsByNetworkId } from 'ts/types';
+import { utils } from 'ts/utils/utils';
const BASE_URL = window.location.origin;
const isDevelopment = _.includes(
@@ -10,13 +11,15 @@ const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs';
export const configs = {
AMOUNT_DISPLAY_PRECSION: 5,
- BACKEND_BASE_URL: 'https://website-api.0xproject.com',
+ BACKEND_BASE_PROD_URL: 'https://website-api.0xproject.com',
+ BACKEND_BASE_STAGING_URL: 'http://ec2-52-91-181-85.compute-1.amazonaws.com',
BASE_URL,
BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208',
DEFAULT_DERIVATION_PATH: `44'/60'/0'`,
// WARNING: ZRX & WETH MUST always be default trackedTokens
DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'],
DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com',
+ DOMAIN_DOGFOOD: 'dogfood-0xproject.s3-website-us-east-1.amazonaws.com',
DOMAIN_DEVELOPMENT: '0xproject.localhost:3572',
DOMAIN_PRODUCTION: '0xproject.com',
ENVIRONMENT: isDevelopment ? Environments.DEVELOPMENT : Environments.PRODUCTION,
diff --git a/packages/website/ts/utils/fetch_utils.ts b/packages/website/ts/utils/fetch_utils.ts
new file mode 100644
index 000000000..d2e902db5
--- /dev/null
+++ b/packages/website/ts/utils/fetch_utils.ts
@@ -0,0 +1,33 @@
+import { logUtils } from '@0xproject/utils';
+import * as _ from 'lodash';
+import * as queryString from 'query-string';
+
+import { errorReporter } from 'ts/utils/error_reporter';
+
+export const fetchUtils = {
+ async requestAsync(baseUrl: string, path: string, queryParams?: object): Promise<any> {
+ const query = queryStringFromQueryParams(queryParams);
+ const url = `${baseUrl}${path}${query}`;
+ const response = await fetch(url);
+ if (response.status !== 200) {
+ const errorText = `Error requesting url: ${url}, ${response.status}: ${response.statusText}`;
+ logUtils.log(errorText);
+ const error = Error(errorText);
+ // tslint:disable-next-line:no-floating-promises
+ errorReporter.reportAsync(error);
+ throw error;
+ }
+ const result = await response.json();
+ return result;
+ },
+};
+
+function queryStringFromQueryParams(queryParams?: object): string {
+ // if params are undefined or empty, return an empty string
+ if (_.isUndefined(queryParams) || _.isEmpty(queryParams)) {
+ return '';
+ }
+ // stringify the formatted object
+ const stringifiedParams = queryString.stringify(queryParams);
+ return `?${stringifiedParams}`;
+}
diff --git a/packages/website/ts/utils/translate.ts b/packages/website/ts/utils/translate.ts
index 5148e48ad..39924b6f7 100644
--- a/packages/website/ts/utils/translate.ts
+++ b/packages/website/ts/utils/translate.ts
@@ -42,10 +42,10 @@ export class Translate {
}
this.setLanguage(language);
}
- public getLanguage() {
+ public getLanguage(): Language {
return this._selectedLanguage;
}
- public setLanguage(language: Language) {
+ public setLanguage(language: Language): void {
const isLanguageSupported = !_.isUndefined(languageToTranslations[language]);
if (!isLanguageSupported) {
throw new Error(`${language} not supported`);
@@ -53,7 +53,7 @@ export class Translate {
this._selectedLanguage = language;
this._translation = languageToTranslations[language];
}
- public get(key: Key, decoration?: Deco) {
+ public get(key: Key, decoration?: Deco): string {
let text = this._translation[key];
if (!_.isUndefined(decoration) && !_.includes(languagesWithoutCaps, this._selectedLanguage)) {
switch (decoration) {
@@ -77,7 +77,7 @@ export class Translate {
}
return text;
}
- private _capitalize(text: string) {
+ private _capitalize(text: string): string {
return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
}
}
diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts
index 53d60b3ce..3c99bd2fe 100644
--- a/packages/website/ts/utils/utils.ts
+++ b/packages/website/ts/utils/utils.ts
@@ -1,11 +1,22 @@
-import { ECSignature, ExchangeContractErrs, ZeroEx, ZeroExError } from '0x.js';
+import { ContractWrappersError, ECSignature, ExchangeContractErrs, ZeroEx } from '0x.js';
+import { OrderError } from '@0xproject/order-utils';
import { constants as sharedConstants, EtherscanLinkSuffixes, Networks } from '@0xproject/react-shared';
import { Provider } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import deepEqual = require('deep-equal');
import * as _ from 'lodash';
import * as moment from 'moment';
-import { Environments, Order, Providers, ScreenWidths, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types';
+import {
+ BlockchainCallErrs,
+ Environments,
+ Order,
+ Providers,
+ ScreenWidths,
+ Side,
+ SideToAssetToken,
+ Token,
+ TokenByAddress,
+} from 'ts/types';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import * as u2f from 'ts/vendor/u2f_api';
@@ -13,16 +24,18 @@ import * as u2f from 'ts/vendor/u2f_api';
const LG_MIN_EM = 64;
const MD_MIN_EM = 52;
+const isDogfood = (): boolean => _.includes(window.location.href, configs.DOMAIN_DOGFOOD);
+
export const utils = {
- assert(condition: boolean, message: string) {
+ assert(condition: boolean, message: string): void {
if (!condition) {
throw new Error(message);
}
},
- spawnSwitchErr(name: string, value: any) {
+ spawnSwitchErr(name: string, value: any): Error {
return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
},
- isNumeric(n: string) {
+ isNumeric(n: string): boolean {
return !isNaN(parseFloat(n)) && isFinite(Number(n));
},
// This default unix timestamp is used for orders where the user does not specify an expiry date.
@@ -95,13 +108,13 @@ export const utils = {
};
return order;
},
- async sleepAsync(ms: number) {
- return new Promise(resolve => setTimeout(resolve, ms));
+ async sleepAsync(ms: number): Promise<NodeJS.Timer> {
+ return new Promise<NodeJS.Timer>(resolve => setTimeout(resolve, ms));
},
- deepEqual(actual: any, expected: any, opts?: { strict: boolean }) {
+ deepEqual(actual: any, expected: any, opts?: { strict: boolean }): boolean {
return deepEqual(actual, expected, opts);
},
- getColSize(items: number) {
+ getColSize(items: number): number {
const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid
const colSize = bassCssGridSize / items;
if (!_.isInteger(colSize)) {
@@ -109,7 +122,7 @@ export const utils = {
}
return colSize;
},
- getScreenWidth() {
+ getScreenWidth(): ScreenWidths {
const documentEl = document.documentElement;
const body = document.getElementsByTagName('body')[0];
const widthInPx = window.innerWidth || documentEl.clientWidth || body.clientWidth;
@@ -151,7 +164,7 @@ export const utils = {
// This checks the error message returned from an injected Web3 instance on the page
// after a user was prompted to sign a message or send a transaction and decided to
// reject the request.
- didUserDenyWeb3Request(errMsg: string) {
+ didUserDenyWeb3Request(errMsg: string): boolean {
const metamaskDenialErrMsg = 'User denied';
const paritySignerDenialErrMsg = 'Request has been rejected';
const ledgerDenialErrMsg = 'Invalid status 6985';
@@ -161,7 +174,7 @@ export const utils = {
_.includes(errMsg, ledgerDenialErrMsg);
return isUserDeniedErrMsg;
},
- getCurrentEnvironment() {
+ getCurrentEnvironment(): string {
switch (location.host) {
case configs.DOMAIN_DEVELOPMENT:
return 'development';
@@ -177,7 +190,7 @@ export const utils = {
const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287
return truncatedAddress;
},
- hasUniqueNameAndSymbol(tokens: Token[], token: Token) {
+ hasUniqueNameAndSymbol(tokens: Token[], token: Token): boolean {
if (token.isRegistered) {
return true; // Since it's registered, it is the canonical token
}
@@ -192,21 +205,20 @@ export const utils = {
const isUniqueSymbol = _.isUndefined(tokenWithSameSymbolIfExists);
return isUniqueName && isUniqueSymbol;
},
- zeroExErrToHumanReadableErrMsg(error: ZeroExError | ExchangeContractErrs, takerAddress: string): string {
- const ZeroExErrorToHumanReadableError: { [error: string]: string } = {
- [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist',
- [ZeroExError.EtherTokenContractDoesNotExist]: 'EtherToken contract does not exist',
- [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'TokenTransferProxy contract does not exist',
- [ZeroExError.TokenRegistryContractDoesNotExist]: 'TokenRegistry contract does not exist',
- [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist',
- [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist',
- [ZeroExError.UnhandledError]: 'Unhandled error occured',
- [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available',
- [ZeroExError.InvalidSignature]: 'Order signature is not valid',
- [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network',
- [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction',
- [ZeroExError.OutOfGas]: 'Transaction ran out of gas',
- [ZeroExError.NoNetworkId]: 'No network id detected',
+ zeroExErrToHumanReadableErrMsg(error: ContractWrappersError | ExchangeContractErrs, takerAddress: string): string {
+ const ContractWrappersErrorToHumanReadableError: { [error: string]: string } = {
+ [ContractWrappersError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist',
+ [ContractWrappersError.EtherTokenContractDoesNotExist]: 'EtherToken contract does not exist',
+ [ContractWrappersError.TokenTransferProxyContractDoesNotExist]:
+ 'TokenTransferProxy contract does not exist',
+ [ContractWrappersError.TokenRegistryContractDoesNotExist]: 'TokenRegistry contract does not exist',
+ [ContractWrappersError.TokenContractDoesNotExist]: 'Token contract does not exist',
+ [ContractWrappersError.ZRXContractDoesNotExist]: 'ZRX contract does not exist',
+ [BlockchainCallErrs.UserHasNoAssociatedAddresses]: 'User has no addresses available',
+ [OrderError.InvalidSignature]: 'Order signature is not valid',
+ [ContractWrappersError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network',
+ [ContractWrappersError.InvalidJump]: 'Invalid jump occured while executing the transaction',
+ [ContractWrappersError.OutOfGas]: 'Transaction ran out of gas',
};
const exchangeContractErrorToHumanReadableError: {
[error: string]: string;
@@ -239,7 +251,7 @@ export const utils = {
[ExchangeContractErrs.InsufficientRemainingFillAmount]: 'Insufficient remaining fill amount',
};
const humanReadableErrorMsg =
- exchangeContractErrorToHumanReadableError[error] || ZeroExErrorToHumanReadableError[error];
+ exchangeContractErrorToHumanReadableError[error] || ContractWrappersErrorToHumanReadableError[error];
return humanReadableErrorMsg;
},
isParityNode(nodeVersion: string): boolean {
@@ -259,7 +271,7 @@ export const utils = {
);
return isTestNetwork;
},
- getCurrentBaseUrl() {
+ getCurrentBaseUrl(): string {
const port = window.location.port;
const hasPort = !_.isUndefined(port);
const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}`;
@@ -292,10 +304,14 @@ export const utils = {
}
return parsedProviderName;
},
- isDevelopment() {
+ getBackendBaseUrl(): string {
+ return isDogfood() ? configs.BACKEND_BASE_STAGING_URL : configs.BACKEND_BASE_PROD_URL;
+ },
+ isDevelopment(): boolean {
return configs.ENVIRONMENT === Environments.DEVELOPMENT;
},
- isStaging() {
+ isStaging(): boolean {
return _.includes(window.location.href, configs.DOMAIN_STAGING);
},
+ isDogfood,
};