aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-06-06 17:10:27 +0800
committerFabio Berger <me@fabioberger.com>2018-06-06 17:10:27 +0800
commitcbfed99bc6f9c86dee2dc31cafe401ded0fc9084 (patch)
tree6a1608b6fca9366f6b407e8a2cf746a79b86858b /packages
parent05b9dfbe30c55a67e6d2d63898f52e5fa412fcfb (diff)
parent39570a9663abae56b0220745306386197fae65c1 (diff)
downloaddexon-sol-tools-cbfed99bc6f9c86dee2dc31cafe401ded0fc9084.tar
dexon-sol-tools-cbfed99bc6f9c86dee2dc31cafe401ded0fc9084.tar.gz
dexon-sol-tools-cbfed99bc6f9c86dee2dc31cafe401ded0fc9084.tar.bz2
dexon-sol-tools-cbfed99bc6f9c86dee2dc31cafe401ded0fc9084.tar.lz
dexon-sol-tools-cbfed99bc6f9c86dee2dc31cafe401ded0fc9084.tar.xz
dexon-sol-tools-cbfed99bc6f9c86dee2dc31cafe401ded0fc9084.tar.zst
dexon-sol-tools-cbfed99bc6f9c86dee2dc31cafe401ded0fc9084.zip
Merge branch 'v2-prototype' into fixes/misc-small-fixes
* v2-prototype: Remove TranslatedText Fix prettier Add back UMD bundles for 0x.js Move portal disclaimer to the account management section Move prices into portal Use stricter check for subscribe input text Make buttons stack on mobile Do not show subscribe form if language is not english Address PR feedback Lint and cleanup Implement subscription form Add styled-components and polished Have basic newsletter subscribe form working
Diffstat (limited to 'packages')
-rw-r--r--packages/0x.js/package.json4
-rw-r--r--packages/react-shared/src/utils/colors.ts1
-rw-r--r--packages/website/package.json2
-rw-r--r--packages/website/ts/components/forms/subscribe_form.tsx124
-rw-r--r--packages/website/ts/components/inputs/allowance_toggle.tsx2
-rw-r--r--packages/website/ts/components/onboarding/onboarding_flow.tsx2
-rw-r--r--packages/website/ts/components/portal/back_button.tsx2
-rw-r--r--packages/website/ts/components/portal/drawer_menu.tsx2
-rw-r--r--packages/website/ts/components/portal/menu.tsx2
-rw-r--r--packages/website/ts/components/portal/portal.tsx66
-rw-r--r--packages/website/ts/components/relayer_index/relayer_grid_tile.tsx2
-rw-r--r--packages/website/ts/components/relayer_index/relayer_index.tsx2
-rw-r--r--packages/website/ts/components/token_balances.tsx2
-rw-r--r--packages/website/ts/components/top_bar/provider_display.tsx4
-rw-r--r--packages/website/ts/components/top_bar/top_bar.tsx2
-rw-r--r--packages/website/ts/components/ui/button.tsx79
-rw-r--r--packages/website/ts/components/ui/container.tsx15
-rw-r--r--packages/website/ts/components/ui/input.tsx43
-rw-r--r--packages/website/ts/components/ui/island.tsx2
-rw-r--r--packages/website/ts/components/ui/overlay.tsx2
-rw-r--r--packages/website/ts/components/ui/text.tsx41
-rw-r--r--packages/website/ts/components/wallet/wallet.tsx34
-rw-r--r--packages/website/ts/components/wallet/wallet_disconnected_item.tsx2
-rw-r--r--packages/website/ts/components/wallet/wrap_ether_item.tsx2
-rw-r--r--packages/website/ts/pages/landing/landing.tsx49
-rw-r--r--packages/website/ts/style/colors.ts (renamed from packages/website/ts/utils/colors.ts)0
-rw-r--r--packages/website/ts/style/theme.ts15
-rw-r--r--packages/website/ts/style/z_index.ts (renamed from packages/website/ts/utils/style.ts)0
-rw-r--r--packages/website/ts/utils/backend_client.ts8
-rw-r--r--packages/website/ts/utils/fetch_utils.ts32
-rw-r--r--packages/website/ts/utils/wallet_item_styles.ts2
31 files changed, 436 insertions, 109 deletions
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json
index e8b944498..4365edba6 100644
--- a/packages/0x.js/package.json
+++ b/packages/0x.js/package.json
@@ -17,7 +17,7 @@
"scripts": {
"watch": "tsc -w",
"prebuild": "run-s clean generate_contract_wrappers",
- "build": "tsc && copyfiles -u 2 './src/compact_artifacts/**/*.json' ./lib/src/compact_artifacts && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts",
+ "build": "run-p build:umd:prod build:commonjs; exit 0;",
"generate_contract_wrappers": "abi-gen --abis 'src/compact_artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'",
"lint": "tslint --project .",
"test:circleci": "run-s test:coverage",
@@ -26,6 +26,8 @@
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
"clean": "shx rm -rf _bundles lib test_temp scripts src/contract_wrappers/generated",
+ "build:umd:prod": "NODE_ENV=production webpack",
+ "build:commonjs": "tsc && copyfiles -u 2 './src/compact_artifacts/**/*.json' ./lib/src/compact_artifacts && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts",
"run_mocha": "mocha lib/test/**/*_test.js lib/test/global_hooks.js --timeout 10000 --bail --exit",
"manual:postpublish": "yarn build; node ./scripts/postpublish.js",
"docs:stage": "node scripts/stage_docs.js",
diff --git a/packages/react-shared/src/utils/colors.ts b/packages/react-shared/src/utils/colors.ts
index 71d92c290..7d047a50e 100644
--- a/packages/react-shared/src/utils/colors.ts
+++ b/packages/react-shared/src/utils/colors.ts
@@ -22,7 +22,6 @@ const baseColors = {
heroGrey: '#404040',
projectsGrey: '#343333',
darkestGrey: '#272727',
- dharmaDarkGrey: '#252525',
lightBlue: '#60A4F4',
lightBlueA700: '#0091EA',
linkBlue: '#1D5CDE',
diff --git a/packages/website/package.json b/packages/website/package.json
index a17964f2b..54780f600 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -37,6 +37,7 @@
"lodash": "^4.17.4",
"material-ui": "^0.17.1",
"moment": "2.21.0",
+ "polished": "^1.9.2",
"query-string": "^6.0.0",
"react": "15.6.1",
"react-copy-to-clipboard": "^4.2.3",
@@ -52,6 +53,7 @@
"redux": "^3.6.0",
"redux-devtools-extension": "^2.13.2",
"semver-sort": "0.0.4",
+ "styled-components": "^3.3.0",
"thenby": "^1.2.3",
"truffle-contract": "2.0.1",
"web3": "^0.20.0",
diff --git a/packages/website/ts/components/forms/subscribe_form.tsx b/packages/website/ts/components/forms/subscribe_form.tsx
new file mode 100644
index 000000000..04ef28e70
--- /dev/null
+++ b/packages/website/ts/components/forms/subscribe_form.tsx
@@ -0,0 +1,124 @@
+import { colors } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { Button } from 'ts/components/ui/button';
+import { Container } from 'ts/components/ui/container';
+import { Input } from 'ts/components/ui/input';
+import { Text } from 'ts/components/ui/text';
+import { styled } from 'ts/style/theme';
+import { backendClient } from 'ts/utils/backend_client';
+
+export interface SubscribeFormProps {}
+
+export enum SubscribeFormStatus {
+ None,
+ Error,
+ Success,
+ Loading,
+ Other,
+}
+
+export interface SubscribeFormState {
+ emailText: string;
+ lastSubmittedEmail: string;
+ status: SubscribeFormStatus;
+}
+
+const FORM_FONT_SIZE = '15px';
+
+// TODO: Translate visible strings. https://app.asana.com/0/628666249318202/697485674422001
+export class SubscribeForm extends React.Component<SubscribeFormProps, SubscribeFormState> {
+ public state = {
+ emailText: '',
+ lastSubmittedEmail: '',
+ status: SubscribeFormStatus.None,
+ };
+ public render(): React.ReactNode {
+ return (
+ <Container className="flex flex-column items-center justify-between md-mx2 sm-mx2">
+ <Container marginBottom="15px">
+ <Text fontFamily="Roboto Mono" fontColor={colors.grey} center={true}>
+ Subscribe to our newsletter for 0x relayer and dApp updates
+ </Text>
+ </Container>
+ <form onSubmit={this._handleFormSubmitAsync.bind(this)}>
+ <Container className="flex flex-wrap justify-center items-center">
+ <Container marginTop="15px">
+ <Input
+ placeholder="you@email.com"
+ value={this.state.emailText}
+ fontColor={colors.white}
+ fontSize={FORM_FONT_SIZE}
+ backgroundColor={colors.projectsGrey}
+ width="300px"
+ onChange={this._handleEmailInputChange.bind(this)}
+ />
+ </Container>
+ <Container marginLeft="15px" marginTop="15px">
+ <Button
+ type="submit"
+ backgroundColor={colors.darkestGrey}
+ fontColor={colors.white}
+ fontSize={FORM_FONT_SIZE}
+ >
+ Subscribe
+ </Button>
+ </Container>
+ </Container>
+ </form>
+ {this._renderMessage()}
+ </Container>
+ );
+ }
+ private _renderMessage(): React.ReactNode {
+ let message = null;
+ switch (this.state.status) {
+ case SubscribeFormStatus.Error:
+ message = 'Sorry, something went wrong. Try again later.';
+ break;
+ case SubscribeFormStatus.Loading:
+ message = 'One second...';
+ break;
+ case SubscribeFormStatus.Success:
+ message = `Thanks! ${this.state.lastSubmittedEmail} is now on the mailing list.`;
+ break;
+ case SubscribeFormStatus.None:
+ break;
+ default:
+ throw new Error(
+ 'The SubscribeFormStatus switch statement is not exhaustive when choosing an error message.',
+ );
+ }
+ return (
+ <Container isHidden={!message} marginTop="30px">
+ <Text center={true} fontFamily="Roboto Mono" fontColor={colors.grey}>
+ {message || 'spacer text (never shown to user)'}
+ </Text>
+ </Container>
+ );
+ }
+ private _handleEmailInputChange(event: React.ChangeEvent<HTMLInputElement>): void {
+ this.setState({ emailText: event.target.value });
+ }
+ private async _handleFormSubmitAsync(event: React.FormEvent<HTMLInputElement>): Promise<void> {
+ event.preventDefault();
+ if (_.isUndefined(this.state.emailText) || _.isEmpty(this.state.emailText)) {
+ return;
+ }
+ this.setState({
+ status: SubscribeFormStatus.Loading,
+ lastSubmittedEmail: this.state.emailText,
+ });
+ try {
+ const response = await backendClient.subscribeToNewsletterAsync(this.state.emailText);
+ const status = response.status === 200 ? SubscribeFormStatus.Success : SubscribeFormStatus.Error;
+ this.setState({ status, emailText: '' });
+ } catch (error) {
+ this._setStatus(SubscribeFormStatus.Error);
+ }
+ }
+ private _setStatus(status: SubscribeFormStatus): void {
+ this.setState({ status });
+ }
+}
diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx
index d61dfa87d..a8df4935a 100644
--- a/packages/website/ts/components/inputs/allowance_toggle.tsx
+++ b/packages/website/ts/components/inputs/allowance_toggle.tsx
@@ -5,9 +5,9 @@ import Toggle from 'material-ui/Toggle';
import * as React from 'react';
import { Blockchain } from 'ts/blockchain';
import { Dispatcher } from 'ts/redux/dispatcher';
+import { colors } from 'ts/style/colors';
import { BalanceErrs, Token, TokenState } from 'ts/types';
import { analytics } from 'ts/utils/analytics';
-import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
diff --git a/packages/website/ts/components/onboarding/onboarding_flow.tsx b/packages/website/ts/components/onboarding/onboarding_flow.tsx
index 4066babaf..158ac23ec 100644
--- a/packages/website/ts/components/onboarding/onboarding_flow.tsx
+++ b/packages/website/ts/components/onboarding/onboarding_flow.tsx
@@ -5,7 +5,7 @@ import { Placement, Popper, PopperChildrenProps } from 'react-popper';
import { ContinueButtonDisplay, OnboardingTooltip } from 'ts/components/onboarding/onboarding_tooltip';
import { Container } from 'ts/components/ui/container';
import { Overlay } from 'ts/components/ui/overlay';
-import { zIndex } from 'ts/utils/style';
+import { zIndex } from 'ts/style/z_index';
export interface Step {
target: string;
diff --git a/packages/website/ts/components/portal/back_button.tsx b/packages/website/ts/components/portal/back_button.tsx
index 48858613c..2d0bbefc3 100644
--- a/packages/website/ts/components/portal/back_button.tsx
+++ b/packages/website/ts/components/portal/back_button.tsx
@@ -2,7 +2,7 @@ import { Styles } from '@0xproject/react-shared';
import * as React from 'react';
import { Link } from 'react-router-dom';
-import { colors } from 'ts/utils/colors';
+import { colors } from 'ts/style/colors';
export interface BackButtonProps {
to: string;
diff --git a/packages/website/ts/components/portal/drawer_menu.tsx b/packages/website/ts/components/portal/drawer_menu.tsx
index ace11639a..8ac2b9091 100644
--- a/packages/website/ts/components/portal/drawer_menu.tsx
+++ b/packages/website/ts/components/portal/drawer_menu.tsx
@@ -4,8 +4,8 @@ import * as React from 'react';
import { defaultMenuItemEntries, Menu } from 'ts/components/portal/menu';
import { Identicon } from 'ts/components/ui/identicon';
+import { colors } from 'ts/style/colors';
import { WebsitePaths } from 'ts/types';
-import { colors } from 'ts/utils/colors';
import { utils } from 'ts/utils/utils';
const IDENTICON_DIAMETER = 45;
diff --git a/packages/website/ts/components/portal/menu.tsx b/packages/website/ts/components/portal/menu.tsx
index 6e97ee37e..f98346101 100644
--- a/packages/website/ts/components/portal/menu.tsx
+++ b/packages/website/ts/components/portal/menu.tsx
@@ -2,8 +2,8 @@ import { Styles } from '@0xproject/react-shared';
import * as _ from 'lodash';
import * as React from 'react';
import { MenuItem } from 'ts/components/ui/menu_item';
+import { colors } from 'ts/style/colors';
import { Environments, WebsitePaths } from 'ts/types';
-import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
export interface MenuTheme {
diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx
index 9aa83546a..9420395cf 100644
--- a/packages/website/ts/components/portal/portal.tsx
+++ b/packages/website/ts/components/portal/portal.tsx
@@ -34,6 +34,7 @@ import { Dispatcher } from 'ts/redux/dispatcher';
import {
BlockchainErrs,
HashData,
+ ItemByAddress,
Order,
ProviderType,
ScreenWidths,
@@ -43,6 +44,7 @@ import {
TokenVisibility,
WebsitePaths,
} from 'ts/types';
+import { backendClient } from 'ts/utils/backend_client';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import { orderParser } from 'ts/utils/order_parser';
@@ -266,10 +268,6 @@ export class Portal extends React.Component<PortalProps, PortalState> {
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
@@ -394,18 +392,24 @@ export class Portal extends React.Component<PortalProps, PortalState> {
},
];
return (
- <Switch>
- {_.map(accountManagementItems, item => {
- return (
- <Route
- key={item.pathName}
- path={item.pathName}
- render={this._renderAccountManagementItem.bind(this, item)}
- />
- );
- })}}
- <Route render={this._renderNotFoundMessage.bind(this)} />
- </Switch>
+ <div>
+ <Switch>
+ {_.map(accountManagementItems, item => {
+ return (
+ <Route
+ key={item.pathName}
+ path={item.pathName}
+ render={this._renderAccountManagementItem.bind(this, item)}
+ />
+ );
+ })}}
+ <Route render={this._renderNotFoundMessage.bind(this)} />
+ </Switch>
+ <PortalDisclaimerDialog
+ isOpen={this.state.isDisclaimerDialogOpen}
+ onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)}
+ />
+ </div>
);
}
private _renderAccountManagementItem(item: AccountManagementItem): React.ReactNode {
@@ -587,6 +591,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
return this._blockchain.getTokenBalanceAndAllowanceAsync(userAddressIfExists, tokenAddress);
}),
);
+ const priceByAddress = await this._getPriceByAddressAsync(tokenAddresses);
for (let i = 0; i < tokenAddresses.length; i++) {
// Order is preserved in Promise.all
const [balance, allowance] = balancesAndAllowances[i];
@@ -595,6 +600,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
balance,
allowance,
isLoaded: true,
+ price: priceByAddress[tokenAddress],
};
}
this.setState({
@@ -602,6 +608,34 @@ export class Portal extends React.Component<PortalProps, PortalState> {
});
}
+ 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 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 async _refetchTokenStateAsync(tokenAddress: string): Promise<void> {
await this._fetchBalancesAndAllowancesAsync([tokenAddress]);
}
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 ad6ab3de1..41febebe9 100644
--- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
+++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx
@@ -7,8 +7,8 @@ import { TopTokens } from 'ts/components/relayer_index/relayer_top_tokens';
import { Container } from 'ts/components/ui/container';
import { Island } from 'ts/components/ui/island';
import { TokenIcon } from 'ts/components/ui/token_icon';
+import { colors } from 'ts/style/colors';
import { Token, WebsiteBackendRelayerInfo } from 'ts/types';
-import { colors } from 'ts/utils/colors';
export interface RelayerGridTileProps {
relayerInfo: WebsiteBackendRelayerInfo;
diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx
index 9ef6eaf59..683f7084b 100644
--- a/packages/website/ts/components/relayer_index/relayer_index.tsx
+++ b/packages/website/ts/components/relayer_index/relayer_index.tsx
@@ -6,9 +6,9 @@ import { GridList } from 'material-ui/GridList';
import * as React from 'react';
import { RelayerGridTile } from 'ts/components/relayer_index/relayer_grid_tile';
+import { colors } from 'ts/style/colors';
import { ScreenWidths, WebsiteBackendRelayerInfo } from 'ts/types';
import { backendClient } from 'ts/utils/backend_client';
-import { colors } from 'ts/utils/colors';
export interface RelayerIndexProps {
networkId: number;
diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx
index 7a0742bbe..2bc065b0f 100644
--- a/packages/website/ts/components/token_balances.tsx
+++ b/packages/website/ts/components/token_balances.tsx
@@ -77,11 +77,11 @@ interface TokenBalancesProps {
interface TokenBalancesState {
errorType: BalanceErrs;
+ trackedTokenStateByAddress: TokenStateByAddress;
isBalanceSpinnerVisible: boolean;
isZRXSpinnerVisible: boolean;
isTokenPickerOpen: boolean;
isAddingToken: boolean;
- trackedTokenStateByAddress: TokenStateByAddress;
}
export class TokenBalances extends React.Component<TokenBalancesProps, TokenBalancesState> {
diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx
index 8a337119a..dba08f85c 100644
--- a/packages/website/ts/components/top_bar/provider_display.tsx
+++ b/packages/website/ts/components/top_bar/provider_display.tsx
@@ -9,10 +9,10 @@ import { ProviderPicker } from 'ts/components/top_bar/provider_picker';
import { DropDown } from 'ts/components/ui/drop_down';
import { Identicon } from 'ts/components/ui/identicon';
import { Dispatcher } from 'ts/redux/dispatcher';
+import { colors } from 'ts/style/colors';
+import { zIndex } from 'ts/style/z_index';
import { ProviderType } from 'ts/types';
-import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
-import { zIndex } from 'ts/utils/style';
import { utils } from 'ts/utils/utils';
const ROOT_HEIGHT = 24;
diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx
index e2d791ae3..606fd845a 100644
--- a/packages/website/ts/components/top_bar/top_bar.tsx
+++ b/packages/website/ts/components/top_bar/top_bar.tsx
@@ -16,9 +16,9 @@ import { TopBarMenuItem } from 'ts/components/top_bar/top_bar_menu_item';
import { DropDown } from 'ts/components/ui/drop_down';
import { Identicon } from 'ts/components/ui/identicon';
import { Dispatcher } from 'ts/redux/dispatcher';
+import { zIndex } from 'ts/style/z_index';
import { Deco, Key, ProviderType, WebsiteLegacyPaths, WebsitePaths } from 'ts/types';
import { constants } from 'ts/utils/constants';
-import { zIndex } from 'ts/utils/style';
import { Translate } from 'ts/utils/translate';
import { utils } from 'ts/utils/utils';
diff --git a/packages/website/ts/components/ui/button.tsx b/packages/website/ts/components/ui/button.tsx
new file mode 100644
index 000000000..4c7d59839
--- /dev/null
+++ b/packages/website/ts/components/ui/button.tsx
@@ -0,0 +1,79 @@
+import { colors } from '@0xproject/react-shared';
+import { darken } from 'polished';
+import * as React from 'react';
+import { styled } from 'ts/style/theme';
+
+export interface ButtonProps {
+ className?: string;
+ fontSize?: string;
+ fontColor?: string;
+ backgroundColor?: string;
+ borderColor?: string;
+ width?: string;
+ type?: string;
+ onClick?: (event: React.MouseEvent<HTMLElement>) => void;
+}
+
+const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, onClick, type, className }) => (
+ <button type={type} className={className} onClick={onClick}>
+ {children}
+ </button>
+);
+
+export const Button = styled(PlainButton)`
+ cursor: pointer;
+ font-size: ${props => props.fontSize};
+ color: ${props => props.fontColor};
+ padding: 0.8em 2.2em;
+ border-radius: 6px;
+ box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
+ font-weight: 500;
+ font-family: 'Roboto';
+ width: ${props => props.width};
+ background-color: ${props => props.backgroundColor};
+ border: ${props => (props.borderColor ? `1px solid ${props.borderColor}` : 'none')};
+ &:hover {
+ background-color: ${props => darken(0.1, props.backgroundColor)};
+ }
+ &:active {
+ background-color: ${props => darken(0.2, props.backgroundColor)};
+ }
+`;
+
+Button.defaultProps = {
+ fontSize: '12px',
+ backgroundColor: colors.white,
+ width: 'auto',
+};
+
+Button.displayName = 'Button';
+
+type CallToActionType = 'light' | 'dark';
+
+export interface CallToActionProps {
+ type?: CallToActionType;
+ fontSize?: string;
+ width?: string;
+}
+
+export const CallToAction: React.StatelessComponent<CallToActionProps> = ({ children, type, fontSize, width }) => {
+ const isLight = type === 'light';
+ const backgroundColor = isLight ? colors.white : colors.heroGrey;
+ const fontColor = isLight ? colors.heroGrey : colors.white;
+ const borderColor = isLight ? undefined : colors.white;
+ return (
+ <Button
+ fontSize={fontSize}
+ backgroundColor={backgroundColor}
+ fontColor={fontColor}
+ width={width}
+ borderColor={borderColor}
+ >
+ {children}
+ </Button>
+ );
+};
+
+CallToAction.defaultProps = {
+ type: 'dark',
+};
diff --git a/packages/website/ts/components/ui/container.tsx b/packages/website/ts/components/ui/container.tsx
index d577447b0..c6a78e181 100644
--- a/packages/website/ts/components/ui/container.tsx
+++ b/packages/website/ts/components/ui/container.tsx
@@ -11,13 +11,20 @@ export interface ContainerProps {
paddingBottom?: StringOrNum;
paddingRight?: StringOrNum;
paddingLeft?: StringOrNum;
+ backgroundColor?: string;
+ borderRadius?: StringOrNum;
maxWidth?: StringOrNum;
- children?: React.ReactNode;
+ isHidden?: boolean;
+ className?: string;
}
-export const Container: React.StatelessComponent<ContainerProps> = (props: ContainerProps) => {
- const { children, ...style } = props;
- return <div style={style}>{children}</div>;
+export const Container: React.StatelessComponent<ContainerProps> = ({ children, className, isHidden, ...style }) => {
+ const visibility = isHidden ? 'hidden' : undefined;
+ return (
+ <div style={{ ...style, visibility }} className={className}>
+ {children}
+ </div>
+ );
};
Container.displayName = 'Container';
diff --git a/packages/website/ts/components/ui/input.tsx b/packages/website/ts/components/ui/input.tsx
new file mode 100644
index 000000000..e01a71a53
--- /dev/null
+++ b/packages/website/ts/components/ui/input.tsx
@@ -0,0 +1,43 @@
+import { colors } from '@0xproject/react-shared';
+import * as React from 'react';
+import { styled } from 'ts/style/theme';
+
+export interface InputProps {
+ className?: string;
+ value?: string;
+ width?: string;
+ fontSize?: string;
+ fontColor?: string;
+ placeholderColor?: string;
+ placeholder?: string;
+ backgroundColor?: string;
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
+}
+
+const PlainInput: React.StatelessComponent<InputProps> = ({ value, className, placeholder, onChange }) => (
+ <input className={className} value={value} onChange={onChange} placeholder={placeholder} />
+);
+
+export const Input = styled(PlainInput)`
+ font-size: ${props => props.fontSize};
+ width: ${props => props.width};
+ padding: 0.8em 1.2em;
+ border-radius: 3px;
+ font-family: 'Roboto Mono';
+ color: ${props => props.fontColor};
+ border: none;
+ background-color: ${props => props.backgroundColor};
+ &::placeholder {
+ color: ${props => props.placeholderColor};
+ }
+`;
+
+Input.defaultProps = {
+ width: 'auto',
+ backgroundColor: colors.white,
+ fontColor: colors.darkestGrey,
+ placeholderColor: colors.darkGrey,
+ fontSize: '12px',
+};
+
+Input.displayName = 'Input';
diff --git a/packages/website/ts/components/ui/island.tsx b/packages/website/ts/components/ui/island.tsx
index f5480c9c9..802a7830a 100644
--- a/packages/website/ts/components/ui/island.tsx
+++ b/packages/website/ts/components/ui/island.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
+import { colors } from 'ts/style/colors';
import { Styleable } from 'ts/types';
-import { colors } from 'ts/utils/colors';
export interface IslandProps {
style?: React.CSSProperties;
diff --git a/packages/website/ts/components/ui/overlay.tsx b/packages/website/ts/components/ui/overlay.tsx
index bb2ed8e59..acca8194f 100644
--- a/packages/website/ts/components/ui/overlay.tsx
+++ b/packages/website/ts/components/ui/overlay.tsx
@@ -2,7 +2,7 @@ import { colors } from '@0xproject/react-shared';
import * as _ from 'lodash';
import * as React from 'react';
-import { zIndex } from 'ts/utils/style';
+import { zIndex } from 'ts/style/z_index';
export interface OverlayProps {
children?: React.ReactNode;
diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx
new file mode 100644
index 000000000..99bf89966
--- /dev/null
+++ b/packages/website/ts/components/ui/text.tsx
@@ -0,0 +1,41 @@
+import { colors } from '@0xproject/react-shared';
+import * as React from 'react';
+import { styled } from 'ts/style/theme';
+import { Deco, Key } from 'ts/types';
+import { Translate } from 'ts/utils/translate';
+
+export type TextTag = 'p' | 'div' | 'span' | 'label';
+
+export interface TextProps {
+ className?: string;
+ Tag?: TextTag;
+ fontSize?: string;
+ fontFamily?: string;
+ fontColor?: string;
+ lineHeight?: string;
+ center?: boolean;
+ fontWeight?: number;
+}
+
+const PlainText: React.StatelessComponent<TextProps> = ({ children, className, Tag }) => (
+ <Tag className={className}>{children}</Tag>
+);
+
+export const Text = styled(PlainText)`
+ font-family: ${props => props.fontFamily};
+ font-weight: ${props => props.fontWeight};
+ font-size: ${props => props.fontSize};
+ ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')};
+ ${props => (props.center ? 'text-align: center' : '')};
+ color: ${props => props.fontColor};
+`;
+
+Text.defaultProps = {
+ fontFamily: 'Roboto',
+ fontWeight: 400,
+ fontColor: colors.white,
+ fontSize: '14px',
+ Tag: 'div',
+};
+
+Text.displayName = 'Text';
diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx
index 7059ca350..37233930e 100644
--- a/packages/website/ts/components/wallet/wallet.tsx
+++ b/packages/website/ts/components/wallet/wallet.tsx
@@ -32,10 +32,11 @@ import { TokenIcon } from 'ts/components/ui/token_icon';
import { WalletDisconnectedItem } from 'ts/components/wallet/wallet_disconnected_item';
import { WrapEtherItem } from 'ts/components/wallet/wrap_ether_item';
import { Dispatcher } from 'ts/redux/dispatcher';
+import { colors } from 'ts/style/colors';
+import { zIndex } from 'ts/style/z_index';
import {
BalanceErrs,
BlockchainErrs,
- ItemByAddress,
ProviderType,
ScreenWidths,
Side,
@@ -45,10 +46,7 @@ import {
TokenStateByAddress,
WebsitePaths,
} from 'ts/types';
-import { backendClient } from 'ts/utils/backend_client';
-import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
-import { zIndex } from 'ts/utils/style';
import { utils } from 'ts/utils/utils';
import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles';
@@ -523,34 +521,6 @@ export class Wallet extends React.Component<WalletProps, WalletState> {
});
return trackedTokenStateByAddress;
}
-
- 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 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): void {
this.setState({
wrappedEtherDirection,
diff --git a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
index 17fd8a19e..e3b88dc54 100644
--- a/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
+++ b/packages/website/ts/components/wallet/wallet_disconnected_item.tsx
@@ -3,8 +3,8 @@ import FlatButton from 'material-ui/FlatButton';
import ActionAccountBalanceWallet from 'material-ui/svg-icons/action/account-balance-wallet';
import * as React from 'react';
+import { colors } from 'ts/style/colors';
import { ProviderType } from 'ts/types';
-import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
import { utils } from 'ts/utils/utils';
diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx
index 1dfcffadf..50b64d05c 100644
--- a/packages/website/ts/components/wallet/wrap_ether_item.tsx
+++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx
@@ -10,8 +10,8 @@ import { Blockchain } from 'ts/blockchain';
import { EthAmountInput } from 'ts/components/inputs/eth_amount_input';
import { TokenAmountInput } from 'ts/components/inputs/token_amount_input';
import { Dispatcher } from 'ts/redux/dispatcher';
+import { colors } from 'ts/style/colors';
import { BlockchainCallErrs, Side, Token } from 'ts/types';
-import { colors } from 'ts/utils/colors';
import { constants } from 'ts/utils/constants';
import { errorReporter } from 'ts/utils/error_reporter';
import { utils } from 'ts/utils/utils';
diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx
index 9d5e54c22..ed6ec4ec6 100644
--- a/packages/website/ts/pages/landing/landing.tsx
+++ b/packages/website/ts/pages/landing/landing.tsx
@@ -1,11 +1,13 @@
import { colors } from '@0xproject/react-shared';
import * as _ from 'lodash';
-import RaisedButton from 'material-ui/RaisedButton';
import * as React from 'react';
import DocumentTitle = require('react-document-title');
import { Link } from 'react-router-dom';
import { Footer } from 'ts/components/footer';
+import { SubscribeForm } from 'ts/components/forms/subscribe_form';
import { TopBar } from 'ts/components/top_bar/top_bar';
+import { CallToAction } from 'ts/components/ui/button';
+import { Container } from 'ts/components/ui/container';
import { Dispatcher } from 'ts/redux/dispatcher';
import { Deco, Key, Language, ScreenWidths, WebsitePaths } from 'ts/types';
import { constants } from 'ts/utils/constants';
@@ -236,7 +238,7 @@ export class Landing extends React.Component<LandingProps, LandingState> {
<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="lg-pt4 md-pt4 sm-pt2 lg-pb4 md-pb4 lg-mt4 md-mt4 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} />
</div>
@@ -268,40 +270,31 @@ export class Landing extends React.Component<LandingProps, LandingState> {
>
{this.props.translate.get(Key.TopTagline)}
</div>
- <div className="pt3 clearfix sm-mx-auto" style={{ maxWidth: 389 }}>
- <div className="lg-pr2 md-pr2 col col-6 sm-center">
+ <Container className="pt3 clearfix sm-mx-auto" maxWidth="390px">
+ <div className="lg-pr2 md-pr2 lg-col lg-col-6 sm-center sm-col sm-col-12 mb2">
<Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 157.36 }}
- buttonStyle={{ borderRadius: 6 }}
- labelStyle={buttonLabelStyle}
- label={this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
- onClick={_.noop}
- />
+ <CallToAction width="175px" type="light">
+ {this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
+ </CallToAction>
</Link>
</div>
- <div className="col col-6 sm-center">
+ <div className="lg-col lg-col-6 sm-center sm-col sm-col-12">
<a
href={constants.URL_ZEROEX_CHAT}
target="_blank"
className="text-decoration-none"
>
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 150 }}
- buttonStyle={lightButtonStyle}
- labelColor="white"
- backgroundColor={colors.heroGrey}
- labelStyle={buttonLabelStyle}
- label={this.props.translate.get(Key.CommunityCallToAction, Deco.Cap)}
- onClick={_.noop}
- />
+ <CallToAction width="175px">
+ {this.props.translate.get(Key.CommunityCallToAction, Deco.Cap)}
+ </CallToAction>
</a>
</div>
- </div>
+ </Container>
</div>
</div>
</div>
</div>
+ {this.props.translate.getLanguage() === Language.English && <SubscribeForm />}
</div>
);
}
@@ -785,15 +778,9 @@ export class Landing extends React.Component<LandingProps, LandingState> {
</div>
<div className="sm-center sm-pt2 lg-table-cell md-table-cell">
<Link to={WebsitePaths.ZeroExJs} className="text-decoration-none">
- <RaisedButton
- style={{ borderRadius: 6, minWidth: 150 }}
- buttonStyle={lightButtonStyle}
- labelColor={colors.white}
- backgroundColor={colors.heroGrey}
- labelStyle={buttonLabelStyle}
- label={this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
- onClick={_.noop}
- />
+ <CallToAction fontSize="15px">
+ {this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
+ </CallToAction>
</Link>
</div>
</div>
diff --git a/packages/website/ts/utils/colors.ts b/packages/website/ts/style/colors.ts
index 5ffdd6ba7..5ffdd6ba7 100644
--- a/packages/website/ts/utils/colors.ts
+++ b/packages/website/ts/style/colors.ts
diff --git a/packages/website/ts/style/theme.ts b/packages/website/ts/style/theme.ts
new file mode 100644
index 000000000..9e447e7ee
--- /dev/null
+++ b/packages/website/ts/style/theme.ts
@@ -0,0 +1,15 @@
+import * as styledComponents from 'styled-components';
+
+const {
+ default: styled,
+ css,
+ injectGlobal,
+ keyframes,
+ ThemeProvider,
+} = styledComponents as styledComponents.ThemedStyledComponentsModule<IThemeInterface>;
+
+export interface IThemeInterface {}
+
+export const theme = {};
+
+export { styled, css, injectGlobal, keyframes, ThemeProvider };
diff --git a/packages/website/ts/utils/style.ts b/packages/website/ts/style/z_index.ts
index 0411cdd91..0411cdd91 100644
--- a/packages/website/ts/utils/style.ts
+++ b/packages/website/ts/style/z_index.ts
diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts
index c440b1604..6b16aea6b 100644
--- a/packages/website/ts/utils/backend_client.ts
+++ b/packages/website/ts/utils/backend_client.ts
@@ -8,6 +8,7 @@ const ETH_GAS_STATION_ENDPOINT = '/eth_gas_station';
const PRICES_ENDPOINT = '/prices';
const RELAYERS_ENDPOINT = '/relayers';
const WIKI_ENDPOINT = '/wiki';
+const SUBSCRIBE_SUBSTACK_NEWSLETTER_ENDPOINT = '/newsletter_subscriber/substack';
export const backendClient = {
async getGasInfoAsync(): Promise<WebsiteBackendGasInfo> {
@@ -33,4 +34,11 @@ export const backendClient = {
const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), WIKI_ENDPOINT);
return result;
},
+ async subscribeToNewsletterAsync(email: string): Promise<Response> {
+ const result = await fetchUtils.postAsync(utils.getBackendBaseUrl(), SUBSCRIBE_SUBSTACK_NEWSLETTER_ENDPOINT, {
+ email,
+ referrer: window.location.href,
+ });
+ return result;
+ },
};
diff --git a/packages/website/ts/utils/fetch_utils.ts b/packages/website/ts/utils/fetch_utils.ts
index d2e902db5..513f7e479 100644
--- a/packages/website/ts/utils/fetch_utils.ts
+++ b/packages/website/ts/utils/fetch_utils.ts
@@ -4,22 +4,38 @@ import * as queryString from 'query-string';
import { errorReporter } from 'ts/utils/error_reporter';
+const logErrorIfPresent = (response: Response, requestedURL: string) => {
+ if (response.status !== 200) {
+ const errorText = `Error requesting url: ${requestedURL}, ${response.status}: ${response.statusText}`;
+ logUtils.log(errorText);
+ const error = Error(errorText);
+ // tslint:disable-next-line:no-floating-promises
+ errorReporter.reportAsync(error);
+ throw error;
+ }
+};
+
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;
- }
+ logErrorIfPresent(response, url);
const result = await response.json();
return result;
},
+ async postAsync(baseUrl: string, path: string, body: object): Promise<Response> {
+ const url = `${baseUrl}${path}`;
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(body),
+ });
+ logErrorIfPresent(response, url);
+ return response;
+ },
};
function queryStringFromQueryParams(queryParams?: object): string {
diff --git a/packages/website/ts/utils/wallet_item_styles.ts b/packages/website/ts/utils/wallet_item_styles.ts
index 6b038efd2..9d6033d74 100644
--- a/packages/website/ts/utils/wallet_item_styles.ts
+++ b/packages/website/ts/utils/wallet_item_styles.ts
@@ -1,6 +1,6 @@
import { Styles } from '@0xproject/react-shared';
-import { colors } from 'ts/utils/colors';
+import { colors } from 'ts/style/colors';
export const styles: Styles = {
focusedItem: {