Merge pull request #652 from 0xProject/feature/website/landing-subscribe-button-2
Implement subscribe form on landing page
This commit is contained in:
124
packages/website/ts/components/forms/subscribe_form.tsx
Normal file
124
packages/website/ts/components/forms/subscribe_form.tsx
Normal file
@@ -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 });
|
||||
}
|
||||
}
|
@@ -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';
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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> {
|
||||
|
@@ -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;
|
||||
|
@@ -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';
|
||||
|
||||
|
79
packages/website/ts/components/ui/button.tsx
Normal file
79
packages/website/ts/components/ui/button.tsx
Normal file
@@ -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',
|
||||
};
|
@@ -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';
|
||||
|
43
packages/website/ts/components/ui/input.tsx
Normal file
43
packages/website/ts/components/ui/input.tsx
Normal file
@@ -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';
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
56
packages/website/ts/components/ui/text.tsx
Normal file
56
packages/website/ts/components/ui/text.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
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';
|
||||
|
||||
interface TranslatedProps {
|
||||
children: Key;
|
||||
translate: Translate;
|
||||
deco?: Deco;
|
||||
}
|
||||
|
||||
export type TranslatedTextProps = TextProps & TranslatedProps;
|
||||
|
||||
export const TranslatedText: React.StatelessComponent<TranslatedTextProps> = ({
|
||||
translate,
|
||||
children,
|
||||
deco,
|
||||
...textProps,
|
||||
}) => <Text {...textProps}>{translate.get(children, deco)}</Text>;
|
@@ -32,6 +32,8 @@ 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,
|
||||
@@ -46,9 +48,7 @@ import {
|
||||
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';
|
||||
|
||||
|
@@ -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';
|
||||
|
||||
|
@@ -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';
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -782,15 +775,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>
|
||||
|
15
packages/website/ts/style/theme.ts
Normal file
15
packages/website/ts/style/theme.ts
Normal file
@@ -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 };
|
@@ -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;
|
||||
},
|
||||
};
|
||||
|
@@ -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 {
|
||||
|
@@ -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: {
|
||||
|
Reference in New Issue
Block a user