import { colors } from '@0xproject/react-shared'; import * as _ from 'lodash'; 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'; import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; interface BoxContent { title: string; description: string; imageUrl: string; classNames: string; } interface AssetType { title: string; imageUrl: string; style?: React.CSSProperties; } interface UseCase { imageUrl: string; type: string; description: string; classNames: string; style?: React.CSSProperties; projectIconUrls: string[]; } interface Project { logoFileName: string; projectUrl: string; } 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[] = [ { logoFileName: 'ercdex.png', projectUrl: constants.PROJECT_URL_ERC_DEX, }, { logoFileName: 'radar_relay.png', projectUrl: constants.PROJECT_URL_RADAR_RELAY, }, { logoFileName: 'paradex.png', projectUrl: constants.PROJECT_URL_PARADEX, }, { logoFileName: 'the_ocean.png', projectUrl: constants.PROJECT_URL_0CEAN, }, { logoFileName: 'dydx.png', projectUrl: constants.PROJECT_URL_DYDX, }, { logoFileName: 'ethfinex.png', projectUrl: constants.PROJECT_URL_ETHFINEX, }, { logoFileName: 'melonport.png', projectUrl: constants.PROJECT_URL_MELONPORT, }, { logoFileName: 'maker.png', projectUrl: constants.PROJECT_URL_MAKER, }, { logoFileName: 'dharma.png', projectUrl: constants.PROJECT_URL_DHARMA, }, { logoFileName: 'lendroid.png', projectUrl: constants.PROJECT_URL_LENDROID, }, { logoFileName: 'district0x.png', projectUrl: constants.PROJECT_URL_DISTRICT_0X, }, { logoFileName: 'aragon.png', projectUrl: constants.PROJECT_URL_ARAGON, }, { logoFileName: 'blocknet.png', projectUrl: constants.PROJECT_URL_BLOCKNET, }, { logoFileName: 'imtoken.png', projectUrl: constants.PROJECT_URL_IMTOKEN, }, { logoFileName: 'augur.png', projectUrl: constants.PROJECT_URL_AUGUR, }, { logoFileName: 'anx.png', projectUrl: constants.PROJECT_URL_OPEN_ANX, }, ]; const relayerProjects: Project[] = [ { logoFileName: 'ethfinex.png', projectUrl: constants.PROJECT_URL_ETHFINEX, }, { logoFileName: 'radar_relay.png', projectUrl: constants.PROJECT_URL_RADAR_RELAY, }, { logoFileName: 'paradex.png', projectUrl: constants.PROJECT_URL_PARADEX, }, { logoFileName: 'the_ocean.png', projectUrl: constants.PROJECT_URL_0CEAN, }, { logoFileName: 'amadeus.png', projectUrl: constants.PROJECT_URL_AMADEUS, }, { logoFileName: 'ddex.png', projectUrl: constants.PROJECT_URL_DDEX, }, { logoFileName: 'decent_ex.png', projectUrl: constants.PROJECT_URL_DECENT_EX, }, { logoFileName: 'dextroid.png', projectUrl: constants.PROJECT_URL_DEXTROID, }, { logoFileName: 'ercdex.png', projectUrl: constants.PROJECT_URL_ERC_DEX, }, { logoFileName: 'open_relay.png', projectUrl: constants.PROJECT_URL_OPEN_RELAY, }, { logoFileName: 'idt.png', projectUrl: constants.PROJECT_URL_IDT, }, { logoFileName: 'imtoken.png', projectUrl: constants.PROJECT_URL_IMTOKEN, }, ]; export interface LandingProps { location: Location; translate: Translate; dispatcher: Dispatcher; } interface LandingState { screenWidth: ScreenWidths; } export class Landing extends React.Component { private _throttledScreenWidthUpdate: () => void; constructor(props: LandingProps) { super(props); this.state = { screenWidth: utils.getScreenWidth(), }; this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); } public componentDidMount(): void { window.addEventListener('resize', this._throttledScreenWidthUpdate); window.scrollTo(0, 0); } public componentWillUnmount(): void { window.removeEventListener('resize', this._throttledScreenWidthUpdate); } public render(): React.ReactNode { return (
{this._renderHero()} {this._renderProjects( relayersAndDappProjects, this.props.translate.get(Key.ProjectsHeader, Deco.Upper), colors.projectsGrey, false, )} {this._renderTokenizationSection()} {this._renderProtocolSection()} {this._renderProjects( relayerProjects, this.props.translate.get(Key.RelayersHeader, Deco.Upper), colors.heroGrey, true, )} {this._renderInfoBoxes()} {this._renderBuildingBlocksSection()} {this._renderUseCases()} {this._renderCallToAction()}
); } private _renderHero(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const left = 'col lg-col-7 md-col-7 col-12 lg-pl4 md-pl4 sm-pl0 sm-px3 sm-center'; return (
{this._renderWhatsNew()}
{this.props.translate.get(Key.TopHeader, Deco.Cap)}
{this.props.translate.get(Key.TopTagline)}
{this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
{this.props.translate.getLanguage() === Language.English && }
); } private _renderWhatsNew(): React.ReactNode { return (
New
{WHATS_NEW_TITLE}
); } 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; let colWidth: number; switch (this.state.screenWidth) { case ScreenWidths.Sm: colWidth = 4; break; case ScreenWidths.Md: colWidth = 3; break; case ScreenWidths.Lg: colWidth = isRelayersOnly ? 2 : 2 - i % 2; break; default: throw new Error(`Encountered unknown ScreenWidths value: ${this.state.screenWidth}`); } return (
); }); const titleStyle: React.CSSProperties = { fontFamily: 'Roboto Mono', color: colors.grey, fontWeight: 300, letterSpacing: 3, }; return (
{title}
{projectList}
{this.props.translate.get(Key.FullListPrompt)}{' '} {this.props.translate.get(Key.FullListLink)}
); } private _renderTokenizationSection(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; return (
{isSmallScreen && this._renderTokenCloud()}
{this.props.translate.get(Key.TokenizedSectionHeader, Deco.Cap)}
{this.props.translate.get(Key.TokenizedSectionDescription, Deco.Cap)}
{this._renderAssetTypes()}
{!isSmallScreen && this._renderTokenCloud()}
); } private _renderProtocolSection(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; return (
{this.props.translate.get(Key.OffChainOrderRelay, Deco.Cap)}
{this.props.translate.get(Key.OonChainSettlement, Deco.Cap)}
{this.props.translate.get(Key.OffChainOnChainDescription, Deco.Cap)}
); } private _renderBuildingBlocksSection(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const descriptionStyle: React.CSSProperties = { fontFamily: 'Roboto Mono', lineHeight: isSmallScreen ? 1.5 : 2, fontWeight: 300, fontSize: 15, maxWidth: isSmallScreen ? 375 : 'none', }; const callToActionStyle: React.CSSProperties = { fontFamily: 'Roboto Mono', fontSize: 15, fontWeight: 300, maxWidth: isSmallScreen ? 375 : 441, }; return (
{isSmallScreen && this._renderBlockChipImage()}
{this.props.translate.get(Key.BuildingBlockSectionHeader, Deco.Cap)}
{this.props.translate.get(Key.BuildingBlockSectionDescription, Deco.Cap)}
{this.props.translate.get(Key.DevToolsPrompt, Deco.Cap)}{' '} 0x.js {' '} {this.props.translate.get(Key.And)}{' '} {this.props.translate.get(Key.SmartContract)} {' '} {this.props.translate.get(Key.Docs)}
{!isSmallScreen && this._renderBlockChipImage()}
); } private _renderBlockChipImage(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; return (
); } private _renderTokenCloud(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; return (
); } private _renderAssetTypes(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const assetTypes: AssetType[] = [ { title: this.props.translate.get(Key.Currency, Deco.Cap), imageUrl: '/images/landing/currency.png', }, { title: this.props.translate.get(Key.TraditionalAssets, Deco.Cap), imageUrl: '/images/landing/stocks.png', style: { paddingLeft: isSmallScreen ? 41 : 56, paddingRight: isSmallScreen ? 41 : 56, }, }, { title: this.props.translate.get(Key.DigitalGoods, Deco.Cap), imageUrl: '/images/landing/digital_goods.png', }, ]; const assets = _.map(assetTypes, (assetType: AssetType) => { const style = _.isUndefined(assetType.style) ? {} : assetType.style; return (
{assetType.title}
); }); return assets; } private _renderInfoBoxes(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const boxStyle: React.CSSProperties = { maxWidth: 253, height: 402, backgroundColor: colors.grey50, borderRadius: 5, padding: '10px 24px 24px', }; const boxContents: BoxContent[] = [ { title: this.props.translate.get(Key.BenefitOneTitle, Deco.Cap), description: this.props.translate.get(Key.BenefitOneDescription, Deco.Cap), imageUrl: '/images/landing/distributed_network.png', classNames: '', }, { title: this.props.translate.get(Key.BenefitTwoTitle, Deco.Cap), description: this.props.translate.get(Key.BenefitTwoDescription, Deco.Cap), imageUrl: '/images/landing/liquidity.png', classNames: 'mx-auto', }, { title: this.props.translate.get(Key.BenefitThreeTitle, Deco.Cap), description: this.props.translate.get(Key.BenefitThreeDescription, Deco.Cap), imageUrl: '/images/landing/open_source.png', classNames: 'right', }, ]; const boxes = _.map(boxContents, (boxContent: BoxContent) => { return (
{boxContent.title}
{boxContent.description}
); }); const titleStyle: React.CSSProperties = { fontFamily: 'Roboto Mono', color: colors.grey, fontWeight: 300, letterSpacing: 3, }; return (
{this.props.translate.get(Key.BenefitsHeader, Deco.Upper)}
{boxes}
); } private _renderUseCases(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const useCases: UseCase[] = [ { imageUrl: '/images/landing/governance_icon.png', type: this.props.translate.get(Key.DecentralizedGovernance, Deco.Upper), description: this.props.translate.get(Key.DecentralizedGovernanceDescription, Deco.Cap), projectIconUrls: ['/images/landing/aragon.png'], classNames: 'lg-px2 md-px2', }, { imageUrl: '/images/landing/prediction_market_icon.png', type: this.props.translate.get(Key.PredictionMarkets, Deco.Upper), description: this.props.translate.get(Key.PredictionMarketsDescription, Deco.Cap), projectIconUrls: ['/images/landing/augur.png'], classNames: 'lg-px2 md-px2', }, { imageUrl: '/images/landing/stable_tokens_icon.png', type: this.props.translate.get(Key.StableTokens, Deco.Upper), description: this.props.translate.get(Key.StableTokensDescription, Deco.Cap), projectIconUrls: ['/images/landing/maker.png'], classNames: 'lg-px2 md-px2', }, { imageUrl: '/images/landing/loans_icon.png', type: this.props.translate.get(Key.DecentralizedLoans, Deco.Upper), description: this.props.translate.get(Key.DecentralizedLoansDescription, Deco.Cap), projectIconUrls: ['/images/landing/dharma.png', '/images/landing/lendroid.png'], classNames: 'lg-pr2 md-pr2 lg-col-6 md-col-6', style: { width: 291, float: 'right', marginTop: !isSmallScreen ? 38 : 0, }, }, { imageUrl: '/images/landing/fund_management_icon.png', type: this.props.translate.get(Key.FundManagement, Deco.Upper), description: this.props.translate.get(Key.FundManagementDescription, Deco.Cap), projectIconUrls: ['/images/landing/melonport.png'], classNames: 'lg-pl2 md-pl2 lg-col-6 md-col-6', style: { width: 291, marginTop: !isSmallScreen ? 38 : 0 }, }, ]; const cases = _.map(useCases, (useCase: UseCase) => { const style = _.isUndefined(useCase.style) || isSmallScreen ? {} : useCase.style; const useCaseBoxStyle = { color: colors.grey, border: `1px solid ${colors.grey750}`, borderRadius: 4, maxWidth: isSmallScreen ? 375 : 'none', ...style, }; const typeStyle: React.CSSProperties = { color: colors.lightGrey, fontSize: 13, textTransform: 'uppercase', fontFamily: 'Roboto Mono', fontWeight: 300, }; return (
{useCase.type}
{useCase.description}
); }); return (
{cases}
); } private _renderCallToAction(): React.ReactNode { const isSmallScreen = this.state.screenWidth === ScreenWidths.Sm; const callToActionClassNames = 'lg-pr3 md-pr3 lg-right-align md-right-align sm-center sm-px3 h4 lg-table-cell md-table-cell'; return (
{this.props.translate.get(Key.FinalCallToAction, Deco.Cap)}
{this.props.translate.get(Key.BuildCallToAction, Deco.Cap)}
); } private _updateScreenWidth(): void { const newScreenWidth = utils.getScreenWidth(); if (newScreenWidth !== this.state.screenWidth) { this.setState({ screenWidth: newScreenWidth, }); } } } // tslint:disable:max-file-line-count